winex11: Abstract window map/unmap to separate functions.
[wine] / dlls / ntdll / directory.c
1 /*
2  * NTDLL directory functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 2003 Eric Pouech
6  * Copyright 1996, 2004 Alexandre Julliard
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <limits.h>
35 #ifdef HAVE_MNTENT_H
36 #include <mntent.h>
37 #endif
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
40 #endif
41 #ifdef HAVE_SYS_IOCTL_H
42 #include <sys/ioctl.h>
43 #endif
44 #ifdef HAVE_LINUX_IOCTL_H
45 #include <linux/ioctl.h>
46 #endif
47 #ifdef HAVE_LINUX_MAJOR_H
48 # include <linux/major.h>
49 #endif
50 #ifdef HAVE_SYS_PARAM_H
51 #include <sys/param.h>
52 #endif
53 #ifdef HAVE_SYS_MOUNT_H
54 #include <sys/mount.h>
55 #endif
56 #include <time.h>
57 #ifdef HAVE_UNISTD_H
58 # include <unistd.h>
59 #endif
60
61 #define NONAMELESSUNION
62 #define NONAMELESSSTRUCT
63 #include "ntstatus.h"
64 #define WIN32_NO_STATUS
65 #include "windef.h"
66 #include "winnt.h"
67 #include "winternl.h"
68 #include "ntdll_misc.h"
69 #include "wine/unicode.h"
70 #include "wine/server.h"
71 #include "wine/library.h"
72 #include "wine/debug.h"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(file);
75
76 /* just in case... */
77 #undef VFAT_IOCTL_READDIR_BOTH
78 #undef USE_GETDENTS
79
80 #ifdef linux
81
82 /* We want the real kernel dirent structure, not the libc one */
83 typedef struct
84 {
85     long d_ino;
86     long d_off;
87     unsigned short d_reclen;
88     char d_name[256];
89 } KERNEL_DIRENT;
90
91 /* Define the VFAT ioctl to get both short and long file names */
92 #define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, KERNEL_DIRENT [2] )
93
94 #ifndef O_DIRECTORY
95 # define O_DIRECTORY 0200000 /* must be directory */
96 #endif
97
98 #ifdef __i386__
99
100 typedef struct
101 {
102     ULONG64        d_ino;
103     LONG64         d_off;
104     unsigned short d_reclen;
105     unsigned char  d_type;
106     char           d_name[256];
107 } KERNEL_DIRENT64;
108
109 static inline int getdents64( int fd, char *de, unsigned int size )
110 {
111     int ret;
112     __asm__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
113              : "=a" (ret)
114              : "0" (220 /*NR_getdents64*/), "r" (fd), "c" (de), "d" (size)
115              : "memory" );
116     if (ret < 0)
117     {
118         errno = -ret;
119         ret = -1;
120     }
121     return ret;
122 }
123 #define USE_GETDENTS
124
125 #endif  /* i386 */
126
127 #endif  /* linux */
128
129 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
130 #define IS_SEPARATOR(ch)   ((ch) == '\\' || (ch) == '/')
131
132 #define INVALID_NT_CHARS   '*','?','<','>','|','"'
133 #define INVALID_DOS_CHARS  INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
134
135 #define MAX_DIR_ENTRY_LEN 255  /* max length of a directory entry in chars */
136
137 static const unsigned int max_dir_info_size = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[MAX_DIR_ENTRY_LEN] );
138
139 static int show_dot_files = -1;
140
141 /* at some point we may want to allow Winelib apps to set this */
142 static const int is_case_sensitive = FALSE;
143
144 static RTL_CRITICAL_SECTION dir_section;
145 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
146 {
147     0, 0, &dir_section,
148     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
149       0, 0, { (DWORD_PTR)(__FILE__ ": dir_section") }
150 };
151 static RTL_CRITICAL_SECTION dir_section = { &critsect_debug, -1, 0, 0, 0, 0 };
152
153
154 /* check if a given Unicode char is OK in a DOS short name */
155 static inline BOOL is_invalid_dos_char( WCHAR ch )
156 {
157     static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
158     if (ch > 0x7f) return TRUE;
159     return strchrW( invalid_chars, ch ) != NULL;
160 }
161
162 /* check if the device can be a mounted volume */
163 static inline int is_valid_mounted_device( const struct stat *st )
164 {
165 #if defined(linux) || defined(__sun__)
166     return S_ISBLK( st->st_mode );
167 #else
168     /* disks are char devices on *BSD */
169     return S_ISCHR( st->st_mode );
170 #endif
171 }
172
173 /***********************************************************************
174  *           get_default_com_device
175  *
176  * Return the default device to use for serial ports.
177  */
178 static char *get_default_com_device( int num )
179 {
180     char *ret = NULL;
181
182     if (!num || num > 9) return ret;
183 #ifdef linux
184     ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/ttyS0") );
185     if (ret)
186     {
187         strcpy( ret, "/dev/ttyS0" );
188         ret[strlen(ret) - 1] = '0' + num - 1;
189     }
190 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
191     ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/cuad0") );
192     if (ret)
193     {
194         strcpy( ret, "/dev/cuad0" );
195         ret[strlen(ret) - 1] = '0' + num - 1;
196     }
197 #else
198     FIXME( "no known default for device com%d\n", num );
199 #endif
200     return ret;
201 }
202
203
204 /***********************************************************************
205  *           get_default_lpt_device
206  *
207  * Return the default device to use for parallel ports.
208  */
209 static char *get_default_lpt_device( int num )
210 {
211     char *ret = NULL;
212
213     if (!num || num > 9) return ret;
214 #ifdef linux
215     ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/lp0") );
216     if (ret)
217     {
218         strcpy( ret, "/dev/lp0" );
219         ret[strlen(ret) - 1] = '0' + num - 1;
220     }
221 #else
222     FIXME( "no known default for device lpt%d\n", num );
223 #endif
224     return ret;
225 }
226
227
228 /***********************************************************************
229  *           DIR_get_drives_info
230  *
231  * Retrieve device/inode number for all the drives. Helper for find_drive_root.
232  */
233 unsigned int DIR_get_drives_info( struct drive_info info[MAX_DOS_DRIVES] )
234 {
235     static struct drive_info cache[MAX_DOS_DRIVES];
236     static time_t last_update;
237     static unsigned int nb_drives;
238     unsigned int ret;
239     time_t now = time(NULL);
240
241     RtlEnterCriticalSection( &dir_section );
242     if (now != last_update)
243     {
244         const char *config_dir = wine_get_config_dir();
245         char *buffer, *p;
246         struct stat st;
247         unsigned int i;
248
249         if ((buffer = RtlAllocateHeap( GetProcessHeap(), 0,
250                                        strlen(config_dir) + sizeof("/dosdevices/a:") )))
251         {
252             strcpy( buffer, config_dir );
253             strcat( buffer, "/dosdevices/a:" );
254             p = buffer + strlen(buffer) - 2;
255
256             for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
257             {
258                 *p = 'a' + i;
259                 if (!stat( buffer, &st ))
260                 {
261                     cache[i].dev = st.st_dev;
262                     cache[i].ino = st.st_ino;
263                     nb_drives++;
264                 }
265                 else
266                 {
267                     cache[i].dev = 0;
268                     cache[i].ino = 0;
269                 }
270             }
271             RtlFreeHeap( GetProcessHeap(), 0, buffer );
272         }
273         last_update = now;
274     }
275     memcpy( info, cache, sizeof(cache) );
276     ret = nb_drives;
277     RtlLeaveCriticalSection( &dir_section );
278     return ret;
279 }
280
281
282 /***********************************************************************
283  *           parse_mount_entries
284  *
285  * Parse mount entries looking for a given device. Helper for get_default_drive_device.
286  */
287
288 #ifdef sun
289 #include <sys/vfstab.h>
290 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
291 {
292     struct vfstab entry;
293     struct stat st;
294     char *device;
295
296     while (! getvfsent( f, &entry ))
297     {
298         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
299         if (!strcmp( entry.vfs_fstype, "nfs" ) ||
300             !strcmp( entry.vfs_fstype, "smbfs" ) ||
301             !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
302
303         if (stat( entry.vfs_mountp, &st ) == -1) continue;
304         if (st.st_dev != dev || st.st_ino != ino) continue;
305         if (!strcmp( entry.vfs_fstype, "fd" ))
306         {
307             if ((device = strstr( entry.vfs_mntopts, "dev=" )))
308             {
309                 char *p = strchr( device + 4, ',' );
310                 if (p) *p = 0;
311                 return device + 4;
312             }
313         }
314         else
315             return entry.vfs_special;
316     }
317     return NULL;
318 }
319 #endif
320
321 #ifdef linux
322 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
323 {
324     struct mntent *entry;
325     struct stat st;
326     char *device;
327
328     while ((entry = getmntent( f )))
329     {
330         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
331         if (!strcmp( entry->mnt_type, "nfs" ) ||
332             !strcmp( entry->mnt_type, "smbfs" ) ||
333             !strcmp( entry->mnt_type, "ncpfs" )) continue;
334
335         if (stat( entry->mnt_dir, &st ) == -1) continue;
336         if (st.st_dev != dev || st.st_ino != ino) continue;
337         if (!strcmp( entry->mnt_type, "supermount" ))
338         {
339             if ((device = strstr( entry->mnt_opts, "dev=" )))
340             {
341                 char *p = strchr( device + 4, ',' );
342                 if (p) *p = 0;
343                 return device + 4;
344             }
345         }
346         else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
347         {
348             /* if device is a regular file check for a loop mount */
349             if ((device = strstr( entry->mnt_opts, "loop=" )))
350             {
351                 char *p = strchr( device + 5, ',' );
352                 if (p) *p = 0;
353                 return device + 5;
354             }
355         }
356         else
357             return entry->mnt_fsname;
358     }
359     return NULL;
360 }
361 #endif
362
363 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
364 #include <fstab.h>
365 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
366 {
367     struct fstab *entry;
368     struct stat st;
369
370     while ((entry = getfsent()))
371     {
372         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
373         if (!strcmp( entry->fs_vfstype, "nfs" ) ||
374             !strcmp( entry->fs_vfstype, "smbfs" ) ||
375             !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
376
377         if (stat( entry->fs_file, &st ) == -1) continue;
378         if (st.st_dev != dev || st.st_ino != ino) continue;
379         return entry->fs_spec;
380     }
381     return NULL;
382 }
383 #endif
384
385 #ifdef sun
386 #include <sys/mnttab.h>
387 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
388 {
389     struct mnttab entry;
390     struct stat st;
391     char *device;
392
393
394     while (( ! getmntent( f, &entry) ))
395     {
396         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
397         if (!strcmp( entry.mnt_fstype, "nfs" ) ||
398             !strcmp( entry.mnt_fstype, "smbfs" ) ||
399             !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
400
401         if (stat( entry.mnt_mountp, &st ) == -1) continue;
402         if (st.st_dev != dev || st.st_ino != ino) continue;
403         if (!strcmp( entry.mnt_fstype, "fd" ))
404         {
405             if ((device = strstr( entry.mnt_mntopts, "dev=" )))
406             {
407                 char *p = strchr( device + 4, ',' );
408                 if (p) *p = 0;
409                 return device + 4;
410             }
411         }
412         else
413             return entry.mnt_special;
414     }
415     return NULL;
416 }
417 #endif
418
419 /***********************************************************************
420  *           get_default_drive_device
421  *
422  * Return the default device to use for a given drive mount point.
423  */
424 static char *get_default_drive_device( const char *root )
425 {
426     char *ret = NULL;
427
428 #ifdef linux
429     FILE *f;
430     char *device = NULL;
431     int fd, res = -1;
432     struct stat st;
433
434     /* try to open it first to force it to get mounted */
435     if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
436     {
437         res = fstat( fd, &st );
438         close( fd );
439     }
440     /* now try normal stat just in case */
441     if (res == -1) res = stat( root, &st );
442     if (res == -1) return NULL;
443
444     RtlEnterCriticalSection( &dir_section );
445
446     if ((f = fopen( "/etc/mtab", "r" )))
447     {
448         device = parse_mount_entries( f, st.st_dev, st.st_ino );
449         endmntent( f );
450     }
451     /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
452     if (!device && (f = fopen( "/etc/fstab", "r" )))
453     {
454         device = parse_mount_entries( f, st.st_dev, st.st_ino );
455         endmntent( f );
456     }
457     if (device)
458     {
459         ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
460         if (ret) strcpy( ret, device );
461     }
462     RtlLeaveCriticalSection( &dir_section );
463
464 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ )
465     char *device = NULL;
466     int fd, res = -1;
467     struct stat st;
468
469     /* try to open it first to force it to get mounted */
470     if ((fd = open( root, O_RDONLY )) != -1)
471     {
472         res = fstat( fd, &st );
473         close( fd );
474     }
475     /* now try normal stat just in case */
476     if (res == -1) res = stat( root, &st );
477     if (res == -1) return NULL;
478
479     RtlEnterCriticalSection( &dir_section );
480
481     /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
482      * pass NULL.  Leave the argument in for symmetry.
483      */
484     device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
485     if (device)
486     {
487         ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
488         if (ret) strcpy( ret, device );
489     }
490     RtlLeaveCriticalSection( &dir_section );
491
492 #elif defined( sun )
493     FILE *f;
494     char *device = NULL;
495     int fd, res = -1;
496     struct stat st;
497
498     /* try to open it first to force it to get mounted */
499     if ((fd = open( root, O_RDONLY )) != -1)
500     {
501         res = fstat( fd, &st );
502         close( fd );
503     }
504     /* now try normal stat just in case */
505     if (res == -1) res = stat( root, &st );
506     if (res == -1) return NULL;
507
508     RtlEnterCriticalSection( &dir_section );
509
510     if ((f = fopen( "/etc/mnttab", "r" )))
511     {
512         device = parse_mount_entries( f, st.st_dev, st.st_ino);
513         fclose( f );
514     }
515     /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
516     if (!device && (f = fopen( "/etc/vfstab", "r" )))
517     {
518         device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
519         fclose( f );
520     }
521     if (device)
522     {
523         ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
524         if (ret) strcpy( ret, device );
525     }
526     RtlLeaveCriticalSection( &dir_section );
527
528 #elif defined(__APPLE__)
529     struct statfs *mntStat;
530     struct stat st;
531     int i;
532     int mntSize;
533     dev_t dev;
534     ino_t ino;
535     static const char path_bsd_device[] = "/dev/disk";
536     int res;
537
538     res = stat( root, &st );
539     if (res == -1) return NULL;
540
541     dev = st.st_dev;
542     ino = st.st_ino;
543
544     RtlEnterCriticalSection( &dir_section );
545
546     mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
547
548     for (i = 0; i < mntSize && !ret; i++)
549     {
550         if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
551         if (st.st_dev != dev || st.st_ino != ino) continue;
552
553         /* FIXME add support for mounted network drive */
554         if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
555         {
556             /* set return value to the corresponding raw BSD node */
557             ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
558             if (ret)
559             {
560                 strcpy(ret, "/dev/r");
561                 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
562             }
563         }
564     }
565     RtlLeaveCriticalSection( &dir_section );
566 #else
567     static int warned;
568     if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
569 #endif
570     return ret;
571 }
572
573
574 /***********************************************************************
575  *           get_device_mount_point
576  *
577  * Return the current mount point for a device.
578  */
579 static char *get_device_mount_point( dev_t dev )
580 {
581     char *ret = NULL;
582
583 #ifdef linux
584     FILE *f;
585
586     RtlEnterCriticalSection( &dir_section );
587
588     if ((f = fopen( "/etc/mtab", "r" )))
589     {
590         struct mntent *entry;
591         struct stat st;
592         char *p, *device;
593
594         while ((entry = getmntent( f )))
595         {
596             /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
597             if (!strcmp( entry->mnt_type, "nfs" ) ||
598                 !strcmp( entry->mnt_type, "smbfs" ) ||
599                 !strcmp( entry->mnt_type, "ncpfs" )) continue;
600
601             if (!strcmp( entry->mnt_type, "supermount" ))
602             {
603                 if ((device = strstr( entry->mnt_opts, "dev=" )))
604                 {
605                     device += 4;
606                     if ((p = strchr( device, ',' ))) *p = 0;
607                 }
608             }
609             else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
610             {
611                 /* if device is a regular file check for a loop mount */
612                 if ((device = strstr( entry->mnt_opts, "loop=" )))
613                 {
614                     device += 5;
615                     if ((p = strchr( device, ',' ))) *p = 0;
616                 }
617             }
618             else device = entry->mnt_fsname;
619
620             if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
621             {
622                 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry->mnt_dir) + 1 );
623                 if (ret) strcpy( ret, entry->mnt_dir );
624                 break;
625             }
626         }
627         endmntent( f );
628     }
629     RtlLeaveCriticalSection( &dir_section );
630 #elif defined(__APPLE__)
631     struct statfs *entry;
632     struct stat st;
633     int i, size;
634
635     RtlEnterCriticalSection( &dir_section );
636
637     size = getmntinfo( &entry, MNT_NOWAIT );
638     for (i = 0; i < size; i++)
639     {
640         if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
641         if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
642         {
643             ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry[i].f_mntfromname) + 1 );
644             if (ret) strcpy( ret, entry[i].f_mntfromname );
645             break;
646         }
647     }
648     RtlLeaveCriticalSection( &dir_section );
649 #else
650     static int warned;
651     if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
652 #endif
653     return ret;
654 }
655
656
657 /***********************************************************************
658  *           init_options
659  *
660  * Initialize the show_dot_files options.
661  */
662 static void init_options(void)
663 {
664     static const WCHAR WineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e',0};
665     static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
666     char tmp[80];
667     HANDLE root, hkey;
668     DWORD dummy;
669     OBJECT_ATTRIBUTES attr;
670     UNICODE_STRING nameW;
671
672     show_dot_files = 0;
673
674     RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
675     attr.Length = sizeof(attr);
676     attr.RootDirectory = root;
677     attr.ObjectName = &nameW;
678     attr.Attributes = 0;
679     attr.SecurityDescriptor = NULL;
680     attr.SecurityQualityOfService = NULL;
681     RtlInitUnicodeString( &nameW, WineW );
682
683     /* @@ Wine registry key: HKCU\Software\Wine */
684     if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
685     {
686         RtlInitUnicodeString( &nameW, ShowDotFilesW );
687         if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
688         {
689             WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
690             show_dot_files = IS_OPTION_TRUE( str[0] );
691         }
692         NtClose( hkey );
693     }
694     NtClose( root );
695 }
696
697
698 /***********************************************************************
699  *           DIR_is_hidden_file
700  *
701  * Check if the specified file should be hidden based on its name and the show dot files option.
702  */
703 BOOL DIR_is_hidden_file( const UNICODE_STRING *name )
704 {
705     WCHAR *p, *end;
706
707     if (show_dot_files == -1) init_options();
708     if (show_dot_files) return FALSE;
709
710     end = p = name->Buffer + name->Length/sizeof(WCHAR);
711     while (p > name->Buffer && IS_SEPARATOR(p[-1])) p--;
712     while (p > name->Buffer && !IS_SEPARATOR(p[-1])) p--;
713     if (p == end || *p != '.') return FALSE;
714     /* make sure it isn't '.' or '..' */
715     if (p + 1 == end) return FALSE;
716     if (p[1] == '.' && p + 2 == end) return FALSE;
717     return TRUE;
718 }
719
720
721 /***********************************************************************
722  *           hash_short_file_name
723  *
724  * Transform a Unix file name into a hashed DOS name. If the name is a valid
725  * DOS name, it is converted to upper-case; otherwise it is replaced by a
726  * hashed version that fits in 8.3 format.
727  * 'buffer' must be at least 12 characters long.
728  * Returns length of short name in bytes; short name is NOT null-terminated.
729  */
730 static ULONG hash_short_file_name( const UNICODE_STRING *name, LPWSTR buffer )
731 {
732     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
733
734     LPCWSTR p, ext, end = name->Buffer + name->Length / sizeof(WCHAR);
735     LPWSTR dst;
736     unsigned short hash;
737     int i;
738
739     /* Compute the hash code of the file name */
740     /* If you know something about hash functions, feel free to */
741     /* insert a better algorithm here... */
742     if (!is_case_sensitive)
743     {
744         for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
745             hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
746         hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
747     }
748     else
749     {
750         for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
751             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
752         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
753     }
754
755     /* Find last dot for start of the extension */
756     for (p = name->Buffer + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
757
758     /* Copy first 4 chars, replacing invalid chars with '_' */
759     for (i = 4, p = name->Buffer, dst = buffer; i > 0; i--, p++)
760     {
761         if (p == end || p == ext) break;
762         *dst++ = is_invalid_dos_char(*p) ? '_' : toupperW(*p);
763     }
764     /* Pad to 5 chars with '~' */
765     while (i-- >= 0) *dst++ = '~';
766
767     /* Insert hash code converted to 3 ASCII chars */
768     *dst++ = hash_chars[(hash >> 10) & 0x1f];
769     *dst++ = hash_chars[(hash >> 5) & 0x1f];
770     *dst++ = hash_chars[hash & 0x1f];
771
772     /* Copy the first 3 chars of the extension (if any) */
773     if (ext)
774     {
775         *dst++ = '.';
776         for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
777             *dst++ = is_invalid_dos_char(*ext) ? '_' : toupperW(*ext);
778     }
779     return dst - buffer;
780 }
781
782
783 /***********************************************************************
784  *           match_filename
785  *
786  * Check a long file name against a mask.
787  *
788  * Tests (done in W95 DOS shell - case insensitive):
789  * *.txt                        test1.test.txt                          *
790  * *st1*                        test1.txt                               *
791  * *.t??????.t*                 test1.ta.tornado.txt                    *
792  * *tornado*                    test1.ta.tornado.txt                    *
793  * t*t                          test1.ta.tornado.txt                    *
794  * ?est*                        test1.txt                               *
795  * ?est???                      test1.txt                               -
796  * *test1.txt*                  test1.txt                               *
797  * h?l?o*t.dat                  hellothisisatest.dat                    *
798  */
799 static BOOLEAN match_filename( const UNICODE_STRING *name_str, const UNICODE_STRING *mask_str )
800 {
801     int mismatch;
802     const WCHAR *name = name_str->Buffer;
803     const WCHAR *mask = mask_str->Buffer;
804     const WCHAR *name_end = name + name_str->Length / sizeof(WCHAR);
805     const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
806     const WCHAR *lastjoker = NULL;
807     const WCHAR *next_to_retry = NULL;
808
809     TRACE("(%s, %s)\n", debugstr_us(name_str), debugstr_us(mask_str));
810
811     while (name < name_end && mask < mask_end)
812     {
813         switch(*mask)
814         {
815         case '*':
816             mask++;
817             while (mask < mask_end && *mask == '*') mask++;  /* Skip consecutive '*' */
818             if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
819             lastjoker = mask;
820
821             /* skip to the next match after the joker(s) */
822             if (is_case_sensitive)
823                 while (name < name_end && (*name != *mask)) name++;
824             else
825                 while (name < name_end && (toupperW(*name) != toupperW(*mask))) name++;
826             next_to_retry = name;
827             break;
828         case '?':
829             mask++;
830             name++;
831             break;
832         default:
833             if (is_case_sensitive) mismatch = (*mask != *name);
834             else mismatch = (toupperW(*mask) != toupperW(*name));
835
836             if (!mismatch)
837             {
838                 mask++;
839                 name++;
840                 if (mask == mask_end)
841                 {
842                     if (name == name_end) return TRUE;
843                     if (lastjoker) mask = lastjoker;
844                 }
845             }
846             else /* mismatch ! */
847             {
848                 if (lastjoker) /* we had an '*', so we can try unlimitedly */
849                 {
850                     mask = lastjoker;
851
852                     /* this scan sequence was a mismatch, so restart
853                      * 1 char after the first char we checked last time */
854                     next_to_retry++;
855                     name = next_to_retry;
856                 }
857                 else return FALSE; /* bad luck */
858             }
859             break;
860         }
861     }
862     while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
863         mask++;  /* Ignore trailing '.' or '*' in mask */
864     return (name == name_end && mask == mask_end);
865 }
866
867
868 /***********************************************************************
869  *           append_entry
870  *
871  * helper for NtQueryDirectoryFile
872  */
873 static FILE_BOTH_DIR_INFORMATION *append_entry( void *info_ptr, ULONG_PTR *pos, ULONG max_length,
874                                                 const char *long_name, const char *short_name,
875                                                 const UNICODE_STRING *mask )
876 {
877     FILE_BOTH_DIR_INFORMATION *info;
878     int i, long_len, short_len, total_len;
879     struct stat st;
880     WCHAR long_nameW[MAX_DIR_ENTRY_LEN];
881     WCHAR short_nameW[12];
882     UNICODE_STRING str;
883
884     long_len = ntdll_umbstowcs( 0, long_name, strlen(long_name), long_nameW, MAX_DIR_ENTRY_LEN );
885     if (long_len == -1) return NULL;
886
887     str.Buffer = long_nameW;
888     str.Length = long_len * sizeof(WCHAR);
889     str.MaximumLength = sizeof(long_nameW);
890
891     if (short_name)
892     {
893         short_len = ntdll_umbstowcs( 0, short_name, strlen(short_name),
894                                      short_nameW, sizeof(short_nameW) / sizeof(WCHAR) );
895         if (short_len == -1) short_len = sizeof(short_nameW) / sizeof(WCHAR);
896     }
897     else  /* generate a short name if necessary */
898     {
899         BOOLEAN spaces;
900
901         short_len = 0;
902         if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
903             short_len = hash_short_file_name( &str, short_nameW );
904     }
905
906     TRACE( "long %s short %s mask %s\n",
907            debugstr_us(&str), debugstr_wn(short_nameW, short_len), debugstr_us(mask) );
908
909     if (mask && !match_filename( &str, mask ))
910     {
911         if (!short_len) return NULL;  /* no short name to match */
912         str.Buffer = short_nameW;
913         str.Length = short_len * sizeof(WCHAR);
914         str.MaximumLength = sizeof(short_nameW);
915         if (!match_filename( &str, mask )) return NULL;
916     }
917
918     total_len = (sizeof(*info) - sizeof(info->FileName) + long_len*sizeof(WCHAR) + 3) & ~3;
919     info = (FILE_BOTH_DIR_INFORMATION *)((char *)info_ptr + *pos);
920
921     if (*pos + total_len > max_length) total_len = max_length - *pos;
922
923     info->FileAttributes = 0;
924     if (lstat( long_name, &st ) == -1) return NULL;
925     if (S_ISLNK( st.st_mode ))
926     {
927         if (stat( long_name, &st ) == -1) return NULL;
928         if (S_ISDIR( st.st_mode )) info->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
929     }
930
931     info->NextEntryOffset = total_len;
932     info->FileIndex = 0;  /* NTFS always has 0 here, so let's not bother with it */
933
934     RtlSecondsSince1970ToTime( st.st_mtime, &info->CreationTime );
935     RtlSecondsSince1970ToTime( st.st_mtime, &info->LastWriteTime );
936     RtlSecondsSince1970ToTime( st.st_atime, &info->LastAccessTime );
937     RtlSecondsSince1970ToTime( st.st_ctime, &info->ChangeTime );
938
939     if (S_ISDIR(st.st_mode))
940     {
941         info->EndOfFile.QuadPart = info->AllocationSize.QuadPart = 0;
942         info->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
943     }
944     else
945     {
946         info->EndOfFile.QuadPart = st.st_size;
947         info->AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
948         info->FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
949     }
950
951     if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
952         info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
953
954     if (!show_dot_files && long_name[0] == '.' && long_name[1] && (long_name[1] != '.' || long_name[2]))
955         info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
956
957     info->EaSize = 0; /* FIXME */
958     info->ShortNameLength = short_len * sizeof(WCHAR);
959     for (i = 0; i < short_len; i++) info->ShortName[i] = toupperW(short_nameW[i]);
960     info->FileNameLength = long_len * sizeof(WCHAR);
961     memcpy( info->FileName, long_nameW,
962             min( info->FileNameLength, total_len-sizeof(*info)+sizeof(info->FileName) ));
963
964     *pos += total_len;
965     return info;
966 }
967
968
969 #ifdef VFAT_IOCTL_READDIR_BOTH
970
971 /***********************************************************************
972  *           start_vfat_ioctl
973  *
974  * Wrapper for the VFAT ioctl to work around various kernel bugs.
975  * dir_section must be held by caller.
976  */
977 static KERNEL_DIRENT *start_vfat_ioctl( int fd )
978 {
979     static KERNEL_DIRENT *de;
980     int res;
981
982     if (!de)
983     {
984         const size_t page_size = getpagesize();
985         SIZE_T size = 2 * sizeof(*de) + page_size;
986         void *addr = NULL;
987
988         if (NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_RESERVE, PAGE_READWRITE ))
989             return NULL;
990         /* commit only the size needed for the dir entries */
991         /* this leaves an extra unaccessible page, which should make the kernel */
992         /* fail with -EFAULT before it stomps all over our memory */
993         de = addr;
994         size = 2 * sizeof(*de);
995         NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_COMMIT, PAGE_READWRITE );
996     }
997
998     /* set d_reclen to 65535 to work around an AFS kernel bug */
999     de[0].d_reclen = 65535;
1000     res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
1001     if (res == -1)
1002     {
1003         if (errno != ENOENT) return NULL;  /* VFAT ioctl probably not supported */
1004         de[0].d_reclen = 0;  /* eof */
1005     }
1006     else if (!res && de[0].d_reclen == 65535) return NULL;  /* AFS bug */
1007
1008     return de;
1009 }
1010
1011
1012 /***********************************************************************
1013  *           read_directory_vfat
1014  *
1015  * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
1016  */
1017 static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1018                                 BOOLEAN single_entry, const UNICODE_STRING *mask,
1019                                 BOOLEAN restart_scan )
1020
1021 {
1022     size_t len;
1023     KERNEL_DIRENT *de;
1024     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
1025
1026     io->u.Status = STATUS_SUCCESS;
1027
1028     if (restart_scan) lseek( fd, 0, SEEK_SET );
1029
1030     if (length < max_dir_info_size)  /* we may have to return a partial entry here */
1031     {
1032         off_t old_pos = lseek( fd, 0, SEEK_CUR );
1033
1034         if (!(de = start_vfat_ioctl( fd ))) return -1;  /* not supported */
1035
1036         while (de[0].d_reclen)
1037         {
1038             /* make sure names are null-terminated to work around an x86-64 kernel bug */
1039             len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1040             de[0].d_name[len] = 0;
1041             len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1042             de[1].d_name[len] = 0;
1043
1044             if (de[1].d_name[0])
1045                 info = append_entry( buffer, &io->Information, length,
1046                                      de[1].d_name, de[0].d_name, mask );
1047             else
1048                 info = append_entry( buffer, &io->Information, length,
1049                                      de[0].d_name, NULL, mask );
1050             if (info)
1051             {
1052                 last_info = info;
1053                 if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
1054                 {
1055                     io->u.Status = STATUS_BUFFER_OVERFLOW;
1056                     lseek( fd, old_pos, SEEK_SET );  /* restore pos to previous entry */
1057                 }
1058                 break;
1059             }
1060             old_pos = lseek( fd, 0, SEEK_CUR );
1061             if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
1062         }
1063     }
1064     else  /* we'll only return full entries, no need to worry about overflow */
1065     {
1066         if (!(de = start_vfat_ioctl( fd ))) return -1;  /* not supported */
1067
1068         while (de[0].d_reclen)
1069         {
1070             /* make sure names are null-terminated to work around an x86-64 kernel bug */
1071             len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1072             de[0].d_name[len] = 0;
1073             len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1074             de[1].d_name[len] = 0;
1075
1076             if (de[1].d_name[0])
1077                 info = append_entry( buffer, &io->Information, length,
1078                                      de[1].d_name, de[0].d_name, mask );
1079             else
1080                 info = append_entry( buffer, &io->Information, length,
1081                                      de[0].d_name, NULL, mask );
1082             if (info)
1083             {
1084                 last_info = info;
1085                 if (single_entry) break;
1086                 /* check if we still have enough space for the largest possible entry */
1087                 if (io->Information + max_dir_info_size > length) break;
1088             }
1089             if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
1090         }
1091     }
1092
1093     if (last_info) last_info->NextEntryOffset = 0;
1094     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1095     return 0;
1096 }
1097 #endif /* VFAT_IOCTL_READDIR_BOTH */
1098
1099
1100 /***********************************************************************
1101  *           read_directory_getdents
1102  *
1103  * Read a directory using the Linux getdents64 system call; helper for NtQueryDirectoryFile.
1104  */
1105 #ifdef USE_GETDENTS
1106 static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1107                                     BOOLEAN single_entry, const UNICODE_STRING *mask,
1108                                     BOOLEAN restart_scan )
1109 {
1110     off_t old_pos = 0;
1111     size_t size = length;
1112     int res, fake_dot_dot = 1;
1113     char *data, local_buffer[8192];
1114     KERNEL_DIRENT64 *de;
1115     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
1116
1117     if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1118     {
1119         size = sizeof(local_buffer);
1120         data = local_buffer;
1121     }
1122
1123     if (restart_scan) lseek( fd, 0, SEEK_SET );
1124     else if (length < max_dir_info_size)  /* we may have to return a partial entry here */
1125     {
1126         old_pos = lseek( fd, 0, SEEK_CUR );
1127         if (old_pos == -1 && errno == ENOENT)
1128         {
1129             io->u.Status = STATUS_NO_MORE_FILES;
1130             res = 0;
1131             goto done;
1132         }
1133     }
1134
1135     io->u.Status = STATUS_SUCCESS;
1136
1137     res = getdents64( fd, data, size );
1138     if (res == -1)
1139     {
1140         if (errno != ENOSYS)
1141         {
1142             io->u.Status = FILE_GetNtStatus();
1143             res = 0;
1144         }
1145         goto done;
1146     }
1147
1148     de = (KERNEL_DIRENT64 *)data;
1149
1150     if (restart_scan)
1151     {
1152         /* check if we got . and .. from getdents */
1153         if (res > 0)
1154         {
1155             if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
1156             {
1157                 KERNEL_DIRENT64 *next_de = (KERNEL_DIRENT64 *)(data + de->d_reclen);
1158                 if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
1159             }
1160         }
1161         /* make sure we have enough room for both entries */
1162         if (fake_dot_dot)
1163         {
1164             static const ULONG min_info_size = (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[1]) +
1165                                                 FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[2]) + 3) & ~3;
1166             if (length < min_info_size || single_entry)
1167             {
1168                 FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
1169                 fake_dot_dot = 0;
1170             }
1171         }
1172
1173         if (fake_dot_dot)
1174         {
1175             if ((info = append_entry( buffer, &io->Information, length, ".", NULL, mask )))
1176                 last_info = info;
1177             if ((info = append_entry( buffer, &io->Information, length, "..", NULL, mask )))
1178                 last_info = info;
1179
1180             /* check if we still have enough space for the largest possible entry */
1181             if (last_info && io->Information + max_dir_info_size > length)
1182             {
1183                 lseek( fd, 0, SEEK_SET );  /* reset pos to first entry */
1184                 res = 0;
1185             }
1186         }
1187     }
1188
1189     while (res > 0)
1190     {
1191         res -= de->d_reclen;
1192         if (!(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
1193             (info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask )))
1194         {
1195             last_info = info;
1196             if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
1197             {
1198                 io->u.Status = STATUS_BUFFER_OVERFLOW;
1199                 lseek( fd, old_pos, SEEK_SET );  /* restore pos to previous entry */
1200                 break;
1201             }
1202             /* check if we still have enough space for the largest possible entry */
1203             if (single_entry || io->Information + max_dir_info_size > length)
1204             {
1205                 if (res > 0) lseek( fd, de->d_off, SEEK_SET );  /* set pos to next entry */
1206                 break;
1207             }
1208         }
1209         old_pos = de->d_off;
1210         /* move on to the next entry */
1211         if (res > 0) de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen);
1212         else
1213         {
1214             res = getdents64( fd, data, size );
1215             de = (KERNEL_DIRENT64 *)data;
1216         }
1217     }
1218
1219     if (last_info) last_info->NextEntryOffset = 0;
1220     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1221     res = 0;
1222 done:
1223     if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1224     return res;
1225 }
1226
1227 #elif defined HAVE_GETDIRENTRIES
1228
1229 /***********************************************************************
1230  *           wine_getdirentries
1231  *
1232  * Wrapper for the BSD getdirentries system call to fix a bug in the
1233  * Mac OS X version.  For some file systems (at least Apple Filing
1234  * Protocol a.k.a. AFP), getdirentries resets the file position to 0
1235  * when it's about to return 0 (no more entries).  So, a subsequent
1236  * getdirentries call starts over at the beginning again, causing an
1237  * infinite loop.
1238  */
1239 static inline int wine_getdirentries(int fd, char *buf, int nbytes, long *basep)
1240 {
1241     int res = getdirentries(fd, buf, nbytes, basep);
1242 #ifdef __APPLE__
1243     if (res == 0)
1244         lseek(fd, *basep, SEEK_SET);
1245 #endif
1246     return res;
1247 }
1248
1249 /***********************************************************************
1250  *           read_directory_getdirentries
1251  *
1252  * Read a directory using the BSD getdirentries system call; helper for NtQueryDirectoryFile.
1253  */
1254 static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1255                                          BOOLEAN single_entry, const UNICODE_STRING *mask,
1256                                          BOOLEAN restart_scan )
1257 {
1258     long restart_pos;
1259     ULONG_PTR restart_info_pos = 0;
1260     size_t size, initial_size = length;
1261     int res, fake_dot_dot = 1;
1262     char *data, local_buffer[8192];
1263     struct dirent *de;
1264     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL, *restart_last_info = NULL;
1265
1266     size = initial_size;
1267     data = local_buffer;
1268     if (size > sizeof(local_buffer) && !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1269     {
1270         io->u.Status = STATUS_NO_MEMORY;
1271         return io->u.Status;
1272     }
1273
1274     if (restart_scan) lseek( fd, 0, SEEK_SET );
1275
1276     io->u.Status = STATUS_SUCCESS;
1277
1278     /* FIXME: should make sure size is larger than filesystem block size */
1279     res = wine_getdirentries( fd, data, size, &restart_pos );
1280     if (res == -1)
1281     {
1282         io->u.Status = FILE_GetNtStatus();
1283         res = 0;
1284         goto done;
1285     }
1286
1287     de = (struct dirent *)data;
1288
1289     if (restart_scan)
1290     {
1291         /* check if we got . and .. from getdirentries */
1292         if (res > 0)
1293         {
1294             if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
1295             {
1296                 struct dirent *next_de = (struct dirent *)(data + de->d_reclen);
1297                 if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
1298             }
1299         }
1300         /* make sure we have enough room for both entries */
1301         if (fake_dot_dot)
1302         {
1303             static const ULONG min_info_size = (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[1]) +
1304                                                 FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[2]) + 3) & ~3;
1305             if (length < min_info_size || single_entry)
1306             {
1307                 FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
1308                 fake_dot_dot = 0;
1309             }
1310         }
1311
1312         if (fake_dot_dot)
1313         {
1314             if ((info = append_entry( buffer, &io->Information, length, ".", NULL, mask )))
1315                 last_info = info;
1316             if ((info = append_entry( buffer, &io->Information, length, "..", NULL, mask )))
1317                 last_info = info;
1318
1319             restart_last_info = last_info;
1320             restart_info_pos = io->Information;
1321
1322             /* check if we still have enough space for the largest possible entry */
1323             if (last_info && io->Information + max_dir_info_size > length)
1324             {
1325                 lseek( fd, 0, SEEK_SET );  /* reset pos to first entry */
1326                 res = 0;
1327             }
1328         }
1329     }
1330
1331     while (res > 0)
1332     {
1333         res -= de->d_reclen;
1334         if (de->d_fileno &&
1335             !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
1336             ((info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask ))))
1337         {
1338             last_info = info;
1339             if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
1340             {
1341                 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1342                 if (restart_info_pos)  /* if we have a complete read already, return it */
1343                 {
1344                     io->Information = restart_info_pos;
1345                     last_info = restart_last_info;
1346                     break;
1347                 }
1348                 /* otherwise restart from the start with a smaller size */
1349                 size = (char *)de - data;
1350                 if (!size)
1351                 {
1352                     io->u.Status = STATUS_BUFFER_OVERFLOW;
1353                     break;
1354                 }
1355                 io->Information = 0;
1356                 last_info = NULL;
1357                 goto restart;
1358             }
1359             /* if we have to return but the buffer contains more data, restart with a smaller size */
1360             if (res > 0 && (single_entry || io->Information + max_dir_info_size > length))
1361             {
1362                 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1363                 size = (char *)de - data;
1364                 io->Information = restart_info_pos;
1365                 last_info = restart_last_info;
1366                 goto restart;
1367             }
1368         }
1369         /* move on to the next entry */
1370         if (res > 0)
1371         {
1372             de = (struct dirent *)((char *)de + de->d_reclen);
1373             continue;
1374         }
1375         if (size < initial_size) break;  /* already restarted once, give up now */
1376         size = min( size, length - io->Information );
1377         /* if size is too small don't bother to continue */
1378         if (size < max_dir_info_size && last_info) break;
1379         restart_last_info = last_info;
1380         restart_info_pos = io->Information;
1381     restart:
1382         res = wine_getdirentries( fd, data, size, &restart_pos );
1383         de = (struct dirent *)data;
1384     }
1385
1386     if (last_info) last_info->NextEntryOffset = 0;
1387     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1388     res = 0;
1389 done:
1390     if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1391     return res;
1392 }
1393 #endif  /* HAVE_GETDIRENTRIES */
1394
1395
1396 /***********************************************************************
1397  *           read_directory_readdir
1398  *
1399  * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
1400  */
1401 static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1402                                     BOOLEAN single_entry, const UNICODE_STRING *mask,
1403                                     BOOLEAN restart_scan )
1404 {
1405     DIR *dir;
1406     off_t i, old_pos = 0;
1407     struct dirent *de;
1408     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
1409
1410     if (!(dir = opendir( "." )))
1411     {
1412         io->u.Status = FILE_GetNtStatus();
1413         return;
1414     }
1415
1416     if (!restart_scan)
1417     {
1418         old_pos = lseek( fd, 0, SEEK_CUR );
1419         /* skip the right number of entries */
1420         for (i = 0; i < old_pos - 2; i++)
1421         {
1422             if (!readdir( dir ))
1423             {
1424                 closedir( dir );
1425                 io->u.Status = STATUS_NO_MORE_FILES;
1426                 return;
1427             }
1428         }
1429     }
1430     io->u.Status = STATUS_SUCCESS;
1431
1432     for (;;)
1433     {
1434         if (old_pos == 0)
1435             info = append_entry( buffer, &io->Information, length, ".", NULL, mask );
1436         else if (old_pos == 1)
1437             info = append_entry( buffer, &io->Information, length, "..", NULL, mask );
1438         else if ((de = readdir( dir )))
1439         {
1440             if (strcmp( de->d_name, "." ) && strcmp( de->d_name, ".." ))
1441                 info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask );
1442             else
1443                 info = NULL;
1444         }
1445         else
1446             break;
1447         old_pos++;
1448         if (info)
1449         {
1450             last_info = info;
1451             if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
1452             {
1453                 io->u.Status = STATUS_BUFFER_OVERFLOW;
1454                 old_pos--;  /* restore pos to previous entry */
1455                 break;
1456             }
1457             if (single_entry) break;
1458             /* check if we still have enough space for the largest possible entry */
1459             if (io->Information + max_dir_info_size > length) break;
1460         }
1461     }
1462
1463     lseek( fd, old_pos, SEEK_SET );  /* store dir offset as filepos for fd */
1464     closedir( dir );
1465
1466     if (last_info) last_info->NextEntryOffset = 0;
1467     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1468 }
1469
1470 /***********************************************************************
1471  *           read_directory_stat
1472  *
1473  * Read a single file from a directory by determining whether the file
1474  * identified by mask exists using stat.
1475  */
1476 static int read_directory_stat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1477                                 BOOLEAN single_entry, const UNICODE_STRING *mask,
1478                                 BOOLEAN restart_scan )
1479 {
1480     int unix_len, ret, used_default;
1481     char *unix_name;
1482     struct stat st;
1483
1484     TRACE("trying optimisation for file %s\n", debugstr_us( mask ));
1485
1486     unix_len = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), NULL, 0, NULL, NULL );
1487     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len + 1)))
1488     {
1489         io->u.Status = STATUS_NO_MEMORY;
1490         return 0;
1491     }
1492     ret = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), unix_name, unix_len,
1493                            NULL, &used_default );
1494     if (ret > 0 && !used_default)
1495     {
1496         unix_name[ret] = 0;
1497         if (restart_scan)
1498         {
1499             lseek( fd, 0, SEEK_SET );
1500         }
1501         else if (lseek( fd, 0, SEEK_CUR ) != 0)
1502         {
1503             io->u.Status = STATUS_NO_MORE_FILES;
1504             ret = 0;
1505             goto done;
1506         }
1507
1508         ret = stat( unix_name, &st );
1509         if (!ret)
1510         {
1511             FILE_BOTH_DIR_INFORMATION *info = append_entry( buffer, &io->Information, length, unix_name, NULL, mask );
1512             if (info)
1513             {
1514                 info->NextEntryOffset = 0;
1515                 if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
1516                     io->u.Status = STATUS_BUFFER_OVERFLOW;
1517                 else
1518                     lseek( fd, 1, SEEK_CUR );
1519             }
1520         }
1521     }
1522     else ret = -1;
1523
1524 done:
1525     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1526
1527     TRACE("returning %d\n", ret);
1528
1529     return ret;
1530 }
1531
1532
1533 static inline WCHAR *mempbrkW( const WCHAR *ptr, const WCHAR *accept, size_t n )
1534 {
1535     const WCHAR *end;
1536     for (end = ptr + n; ptr < end; ptr++) if (strchrW( accept, *ptr )) return (WCHAR *)ptr;
1537     return NULL;
1538 }
1539
1540 /******************************************************************************
1541  *  NtQueryDirectoryFile        [NTDLL.@]
1542  *  ZwQueryDirectoryFile        [NTDLL.@]
1543  */
1544 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
1545                                       PIO_APC_ROUTINE apc_routine, PVOID apc_context,
1546                                       PIO_STATUS_BLOCK io,
1547                                       PVOID buffer, ULONG length,
1548                                       FILE_INFORMATION_CLASS info_class,
1549                                       BOOLEAN single_entry,
1550                                       PUNICODE_STRING mask,
1551                                       BOOLEAN restart_scan )
1552 {
1553     int cwd, fd, needs_close;
1554     static const WCHAR wszWildcards[] = { '*','?',0 };
1555
1556     TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
1557           handle, event, apc_routine, apc_context, io, buffer,
1558           length, info_class, single_entry, debugstr_us(mask),
1559           restart_scan);
1560
1561     if (length < sizeof(FILE_BOTH_DIR_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
1562
1563     if (event || apc_routine)
1564     {
1565         FIXME( "Unsupported yet option\n" );
1566         return io->u.Status = STATUS_NOT_IMPLEMENTED;
1567     }
1568     if (info_class != FileBothDirectoryInformation)
1569     {
1570         FIXME( "Unsupported file info class %d\n", info_class );
1571         return io->u.Status = STATUS_NOT_IMPLEMENTED;
1572     }
1573
1574     if ((io->u.Status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
1575         return io->u.Status;
1576
1577     io->Information = 0;
1578
1579     RtlEnterCriticalSection( &dir_section );
1580
1581     if (show_dot_files == -1) init_options();
1582
1583     cwd = open( ".", O_RDONLY );
1584     if (fchdir( fd ) != -1)
1585     {
1586 #ifdef VFAT_IOCTL_READDIR_BOTH
1587         if ((read_directory_vfat( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1588             goto done;
1589 #endif
1590         if (mask && !mempbrkW( mask->Buffer, wszWildcards, mask->Length / sizeof(WCHAR) ) &&
1591             read_directory_stat( fd, io, buffer, length, single_entry, mask, restart_scan ) != -1)
1592             goto done;
1593 #ifdef USE_GETDENTS
1594         if ((read_directory_getdents( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1595             goto done;
1596 #elif defined HAVE_GETDIRENTRIES
1597         if ((read_directory_getdirentries( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1598             goto done;
1599 #endif
1600         read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan );
1601
1602     done:
1603         if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
1604     }
1605     else io->u.Status = FILE_GetNtStatus();
1606
1607     RtlLeaveCriticalSection( &dir_section );
1608
1609     if (needs_close) close( fd );
1610     if (cwd != -1) close( cwd );
1611     TRACE( "=> %x (%ld)\n", io->u.Status, io->Information );
1612     return io->u.Status;
1613 }
1614
1615
1616 /***********************************************************************
1617  *           find_file_in_dir
1618  *
1619  * Find a file in a directory the hard way, by doing a case-insensitive search.
1620  * The file found is appended to unix_name at pos.
1621  * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
1622  */
1623 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
1624                                   int check_case )
1625 {
1626     WCHAR buffer[MAX_DIR_ENTRY_LEN];
1627     UNICODE_STRING str;
1628     BOOLEAN spaces;
1629     DIR *dir;
1630     struct dirent *de;
1631     struct stat st;
1632     int ret, used_default, is_name_8_dot_3;
1633
1634     /* try a shortcut for this directory */
1635
1636     unix_name[pos++] = '/';
1637     ret = ntdll_wcstoumbs( 0, name, length, unix_name + pos, MAX_DIR_ENTRY_LEN,
1638                            NULL, &used_default );
1639     /* if we used the default char, the Unix name won't round trip properly back to Unicode */
1640     /* so it cannot match the file we are looking for */
1641     if (ret >= 0 && !used_default)
1642     {
1643         unix_name[pos + ret] = 0;
1644         if (!stat( unix_name, &st )) return STATUS_SUCCESS;
1645     }
1646     if (check_case) goto not_found;  /* we want an exact match */
1647
1648     if (pos > 1) unix_name[pos - 1] = 0;
1649     else unix_name[1] = 0;  /* keep the initial slash */
1650
1651     /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
1652
1653     str.Buffer = (WCHAR *)name;
1654     str.Length = length * sizeof(WCHAR);
1655     str.MaximumLength = str.Length;
1656     is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
1657
1658     /* now look for it through the directory */
1659
1660 #ifdef VFAT_IOCTL_READDIR_BOTH
1661     if (is_name_8_dot_3)
1662     {
1663         int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
1664         if (fd != -1)
1665         {
1666             KERNEL_DIRENT *de;
1667
1668             RtlEnterCriticalSection( &dir_section );
1669             if ((de = start_vfat_ioctl( fd )))
1670             {
1671                 unix_name[pos - 1] = '/';
1672                 while (de[0].d_reclen)
1673                 {
1674                     /* make sure names are null-terminated to work around an x86-64 kernel bug */
1675                     size_t len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1676                     de[0].d_name[len] = 0;
1677                     len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1678                     de[1].d_name[len] = 0;
1679
1680                     if (de[1].d_name[0])
1681                     {
1682                         ret = ntdll_umbstowcs( 0, de[1].d_name, strlen(de[1].d_name),
1683                                                buffer, MAX_DIR_ENTRY_LEN );
1684                         if (ret == length && !memicmpW( buffer, name, length))
1685                         {
1686                             strcpy( unix_name + pos, de[1].d_name );
1687                             RtlLeaveCriticalSection( &dir_section );
1688                             close( fd );
1689                             return STATUS_SUCCESS;
1690                         }
1691                     }
1692                     ret = ntdll_umbstowcs( 0, de[0].d_name, strlen(de[0].d_name),
1693                                            buffer, MAX_DIR_ENTRY_LEN );
1694                     if (ret == length && !memicmpW( buffer, name, length))
1695                     {
1696                         strcpy( unix_name + pos,
1697                                 de[1].d_name[0] ? de[1].d_name : de[0].d_name );
1698                         RtlLeaveCriticalSection( &dir_section );
1699                         close( fd );
1700                         return STATUS_SUCCESS;
1701                     }
1702                     if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
1703                     {
1704                         RtlLeaveCriticalSection( &dir_section );
1705                         close( fd );
1706                         goto not_found;
1707                     }
1708                 }
1709             }
1710             RtlLeaveCriticalSection( &dir_section );
1711             close( fd );
1712         }
1713         /* fall through to normal handling */
1714     }
1715 #endif /* VFAT_IOCTL_READDIR_BOTH */
1716
1717     if (!(dir = opendir( unix_name )))
1718     {
1719         if (errno == ENOENT) return STATUS_OBJECT_PATH_NOT_FOUND;
1720         else return FILE_GetNtStatus();
1721     }
1722     unix_name[pos - 1] = '/';
1723     str.Buffer = buffer;
1724     str.MaximumLength = sizeof(buffer);
1725     while ((de = readdir( dir )))
1726     {
1727         ret = ntdll_umbstowcs( 0, de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
1728         if (ret == length && !memicmpW( buffer, name, length ))
1729         {
1730             strcpy( unix_name + pos, de->d_name );
1731             closedir( dir );
1732             return STATUS_SUCCESS;
1733         }
1734
1735         if (!is_name_8_dot_3) continue;
1736
1737         str.Length = ret * sizeof(WCHAR);
1738         if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
1739         {
1740             WCHAR short_nameW[12];
1741             ret = hash_short_file_name( &str, short_nameW );
1742             if (ret == length && !memicmpW( short_nameW, name, length ))
1743             {
1744                 strcpy( unix_name + pos, de->d_name );
1745                 closedir( dir );
1746                 return STATUS_SUCCESS;
1747             }
1748         }
1749     }
1750     closedir( dir );
1751     goto not_found;  /* avoid warning */
1752
1753 not_found:
1754     unix_name[pos - 1] = 0;
1755     return STATUS_OBJECT_PATH_NOT_FOUND;
1756 }
1757
1758
1759 /******************************************************************************
1760  *           get_dos_device
1761  *
1762  * Get the Unix path of a DOS device.
1763  */
1764 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *unix_name_ret )
1765 {
1766     const char *config_dir = wine_get_config_dir();
1767     struct stat st;
1768     char *unix_name, *new_name, *dev;
1769     unsigned int i;
1770     int unix_len;
1771
1772     /* make sure the device name is ASCII */
1773     for (i = 0; i < name_len; i++)
1774         if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
1775
1776     unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
1777
1778     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
1779         return STATUS_NO_MEMORY;
1780
1781     strcpy( unix_name, config_dir );
1782     strcat( unix_name, "/dosdevices/" );
1783     dev = unix_name + strlen(unix_name);
1784
1785     for (i = 0; i < name_len; i++) dev[i] = (char)tolowerW(name[i]);
1786     dev[i] = 0;
1787
1788     /* special case for drive devices */
1789     if (name_len == 2 && dev[1] == ':')
1790     {
1791         dev[i++] = ':';
1792         dev[i] = 0;
1793     }
1794
1795     for (;;)
1796     {
1797         if (!stat( unix_name, &st ))
1798         {
1799             TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
1800             unix_name_ret->Buffer = unix_name;
1801             unix_name_ret->Length = strlen(unix_name);
1802             unix_name_ret->MaximumLength = unix_len;
1803             return STATUS_SUCCESS;
1804         }
1805         if (!dev) break;
1806
1807         /* now try some defaults for it */
1808         if (!strcmp( dev, "aux" ))
1809         {
1810             strcpy( dev, "com1" );
1811             continue;
1812         }
1813         if (!strcmp( dev, "prn" ))
1814         {
1815             strcpy( dev, "lpt1" );
1816             continue;
1817         }
1818         if (!strcmp( dev, "nul" ))
1819         {
1820             strcpy( unix_name, "/dev/null" );
1821             dev = NULL; /* last try */
1822             continue;
1823         }
1824
1825         new_name = NULL;
1826         if (dev[1] == ':' && dev[2] == ':')  /* drive device */
1827         {
1828             dev[2] = 0;  /* remove last ':' to get the drive mount point symlink */
1829             new_name = get_default_drive_device( unix_name );
1830         }
1831         else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( dev[3] - '0' );
1832         else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( dev[3] - '0' );
1833
1834         if (!new_name) break;
1835
1836         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1837         unix_name = new_name;
1838         unix_len = strlen(unix_name) + 1;
1839         dev = NULL; /* last try */
1840     }
1841     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1842     return STATUS_BAD_DEVICE_TYPE;
1843 }
1844
1845
1846 /* return the length of the DOS namespace prefix if any */
1847 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
1848 {
1849     static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
1850     static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
1851
1852     if (name->Length > sizeof(nt_prefixW) &&
1853         !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
1854         return sizeof(nt_prefixW) / sizeof(WCHAR);
1855
1856     if (name->Length > sizeof(dosdev_prefixW) &&
1857         !memicmpW( name->Buffer, dosdev_prefixW, sizeof(dosdev_prefixW)/sizeof(WCHAR) ))
1858         return sizeof(dosdev_prefixW) / sizeof(WCHAR);
1859
1860     return 0;
1861 }
1862
1863
1864 /******************************************************************************
1865  *           wine_nt_to_unix_file_name  (NTDLL.@) Not a Windows API
1866  *
1867  * Convert a file name from NT namespace to Unix namespace.
1868  *
1869  * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
1870  * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
1871  * returned, but the unix name is still filled in properly.
1872  */
1873 NTSTATUS wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
1874                                     UINT disposition, BOOLEAN check_case )
1875 {
1876     static const WCHAR unixW[] = {'u','n','i','x'};
1877     static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
1878
1879     NTSTATUS status = STATUS_SUCCESS;
1880     const char *config_dir = wine_get_config_dir();
1881     const WCHAR *name, *p;
1882     struct stat st;
1883     char *unix_name;
1884     int pos, ret, name_len, unix_len, prefix_len, used_default;
1885     WCHAR prefix[MAX_DIR_ENTRY_LEN];
1886     BOOLEAN is_unix = FALSE;
1887
1888     name     = nameW->Buffer;
1889     name_len = nameW->Length / sizeof(WCHAR);
1890
1891     if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
1892
1893     if (!(pos = get_dos_prefix_len( nameW )))
1894         return STATUS_BAD_DEVICE_TYPE;  /* no DOS prefix, assume NT native name */
1895
1896     name += pos;
1897     name_len -= pos;
1898
1899     /* check for sub-directory */
1900     for (pos = 0; pos < name_len; pos++)
1901     {
1902         if (IS_SEPARATOR(name[pos])) break;
1903         if (name[pos] < 32 || strchrW( invalid_charsW, name[pos] ))
1904             return STATUS_OBJECT_NAME_INVALID;
1905     }
1906     if (pos > MAX_DIR_ENTRY_LEN)
1907         return STATUS_OBJECT_NAME_INVALID;
1908
1909     if (pos == name_len)  /* no subdir, plain DOS device */
1910         return get_dos_device( name, name_len, unix_name_ret );
1911
1912     for (prefix_len = 0; prefix_len < pos; prefix_len++)
1913         prefix[prefix_len] = tolowerW(name[prefix_len]);
1914
1915     name += prefix_len;
1916     name_len -= prefix_len;
1917
1918     /* check for invalid characters (all chars except 0 are valid for unix) */
1919     is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
1920     if (is_unix)
1921     {
1922         for (p = name; p < name + name_len; p++)
1923             if (!*p) return STATUS_OBJECT_NAME_INVALID;
1924         check_case = TRUE;
1925     }
1926     else
1927     {
1928         for (p = name; p < name + name_len; p++)
1929             if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
1930     }
1931
1932     unix_len = ntdll_wcstoumbs( 0, prefix, prefix_len, NULL, 0, NULL, NULL );
1933     unix_len += ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
1934     unix_len += MAX_DIR_ENTRY_LEN + 3;
1935     unix_len += strlen(config_dir) + sizeof("/dosdevices/");
1936     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
1937         return STATUS_NO_MEMORY;
1938     strcpy( unix_name, config_dir );
1939     strcat( unix_name, "/dosdevices/" );
1940     pos = strlen(unix_name);
1941
1942     ret = ntdll_wcstoumbs( 0, prefix, prefix_len, unix_name + pos, unix_len - pos - 1,
1943                            NULL, &used_default );
1944     if (!ret || used_default)
1945     {
1946         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1947         return STATUS_OBJECT_NAME_INVALID;
1948     }
1949     pos += ret;
1950
1951     /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
1952
1953     if (prefix_len != 2 || prefix[1] != ':')
1954     {
1955         unix_name[pos] = 0;
1956         if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
1957         {
1958             if (!is_unix)
1959             {
1960                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1961                 return STATUS_BAD_DEVICE_TYPE;
1962             }
1963             pos = 0;  /* fall back to unix root */
1964         }
1965     }
1966
1967     /* try a shortcut first */
1968
1969     ret = ntdll_wcstoumbs( 0, name, name_len, unix_name + pos, unix_len - pos - 1,
1970                            NULL, &used_default );
1971
1972     while (name_len && IS_SEPARATOR(*name))
1973     {
1974         name++;
1975         name_len--;
1976     }
1977
1978     if (ret > 0 && !used_default)  /* if we used the default char the name didn't convert properly */
1979     {
1980         char *p;
1981         unix_name[pos + ret] = 0;
1982         for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
1983         if (!stat( unix_name, &st ))
1984         {
1985             /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */
1986             if (disposition == FILE_CREATE)
1987             {
1988                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1989                 return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED;
1990             }
1991             goto done;
1992         }
1993     }
1994
1995     if (!name_len)  /* empty name -> drive root doesn't exist */
1996     {
1997         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1998         return STATUS_OBJECT_PATH_NOT_FOUND;
1999     }
2000     if (check_case && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
2001     {
2002         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2003         return STATUS_OBJECT_NAME_NOT_FOUND;
2004     }
2005
2006     /* now do it component by component */
2007
2008     while (name_len)
2009     {
2010         const WCHAR *end, *next;
2011
2012         end = name;
2013         while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
2014         next = end;
2015         while (next < name + name_len && IS_SEPARATOR(*next)) next++;
2016         name_len -= next - name;
2017
2018         /* grow the buffer if needed */
2019
2020         if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
2021         {
2022             char *new_name;
2023             unix_len += 2 * MAX_DIR_ENTRY_LEN;
2024             if (!(new_name = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name, unix_len )))
2025             {
2026                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2027                 return STATUS_NO_MEMORY;
2028             }
2029             unix_name = new_name;
2030         }
2031
2032         status = find_file_in_dir( unix_name, pos, name, end - name, check_case );
2033
2034         /* if this is the last element, not finding it is not necessarily fatal */
2035         if (!name_len)
2036         {
2037             if (status == STATUS_OBJECT_PATH_NOT_FOUND)
2038             {
2039                 status = STATUS_OBJECT_NAME_NOT_FOUND;
2040                 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
2041                 {
2042                     ret = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2043                                            MAX_DIR_ENTRY_LEN, NULL, &used_default );
2044                     if (ret > 0 && !used_default)
2045                     {
2046                         unix_name[pos] = '/';
2047                         unix_name[pos + 1 + ret] = 0;
2048                         status = STATUS_NO_SUCH_FILE;
2049                         break;
2050                     }
2051                 }
2052             }
2053             else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
2054             {
2055                 status = STATUS_OBJECT_NAME_COLLISION;
2056             }
2057         }
2058
2059         if (status != STATUS_SUCCESS)
2060         {
2061             /* couldn't find it at all, fail */
2062             WARN( "%s not found in %s\n", debugstr_w(name), unix_name );
2063             RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2064             return status;
2065         }
2066
2067         pos += strlen( unix_name + pos );
2068         name = next;
2069     }
2070
2071     WARN( "%s -> %s required a case-insensitive search\n",
2072           debugstr_us(nameW), debugstr_a(unix_name) );
2073
2074 done:
2075     TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
2076     unix_name_ret->Buffer = unix_name;
2077     unix_name_ret->Length = strlen(unix_name);
2078     unix_name_ret->MaximumLength = unix_len;
2079     return status;
2080 }
2081
2082
2083 /******************************************************************
2084  *              RtlDoesFileExists_U   (NTDLL.@)
2085  */
2086 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
2087 {
2088     UNICODE_STRING nt_name;
2089     FILE_BASIC_INFORMATION basic_info;
2090     OBJECT_ATTRIBUTES attr;
2091     BOOLEAN ret;
2092
2093     if (!RtlDosPathNameToNtPathName_U( file_name, &nt_name, NULL, NULL )) return FALSE;
2094
2095     attr.Length = sizeof(attr);
2096     attr.RootDirectory = 0;
2097     attr.ObjectName = &nt_name;
2098     attr.Attributes = OBJ_CASE_INSENSITIVE;
2099     attr.SecurityDescriptor = NULL;
2100     attr.SecurityQualityOfService = NULL;
2101
2102     ret = NtQueryAttributesFile(&attr, &basic_info) == STATUS_SUCCESS;
2103
2104     RtlFreeUnicodeString( &nt_name );
2105     return ret;
2106 }
2107
2108
2109 /***********************************************************************
2110  *           DIR_unmount_device
2111  *
2112  * Unmount the specified device.
2113  */
2114 NTSTATUS DIR_unmount_device( HANDLE handle )
2115 {
2116     NTSTATUS status;
2117     int unix_fd, needs_close;
2118
2119     if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
2120     {
2121         struct stat st;
2122         char *mount_point = NULL;
2123
2124         if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
2125             status = STATUS_INVALID_PARAMETER;
2126         else
2127         {
2128             if ((mount_point = get_device_mount_point( st.st_rdev )))
2129             {
2130 #ifdef __APPLE__
2131                 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
2132 #else
2133                 static const char umount[] = "umount >/dev/null 2>&1 ";
2134 #endif
2135                 char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount));
2136                 if (cmd)
2137                 {
2138                     strcpy( cmd, umount );
2139                     strcat( cmd, mount_point );
2140                     system( cmd );
2141                     RtlFreeHeap( GetProcessHeap(), 0, cmd );
2142 #ifdef linux
2143                     /* umount will fail to release the loop device since we still have
2144                        a handle to it, so we release it here */
2145                     if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
2146 #endif
2147                 }
2148                 RtlFreeHeap( GetProcessHeap(), 0, mount_point );
2149             }
2150         }
2151         if (needs_close) close( unix_fd );
2152     }
2153     return status;
2154 }
2155
2156
2157 /******************************************************************************
2158  *           DIR_get_unix_cwd
2159  *
2160  * Retrieve the Unix name of the current directory; helper for wine_unix_to_nt_file_name.
2161  * Returned value must be freed by caller.
2162  */
2163 NTSTATUS DIR_get_unix_cwd( char **cwd )
2164 {
2165     int old_cwd, unix_fd, needs_close;
2166     CURDIR *curdir;
2167     HANDLE handle;
2168     NTSTATUS status;
2169
2170     RtlAcquirePebLock();
2171
2172     if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
2173         curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
2174     else
2175         curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
2176
2177     if (!(handle = curdir->Handle))
2178     {
2179         UNICODE_STRING dirW;
2180         OBJECT_ATTRIBUTES attr;
2181         IO_STATUS_BLOCK io;
2182
2183         if (!RtlDosPathNameToNtPathName_U( curdir->DosPath.Buffer, &dirW, NULL, NULL ))
2184         {
2185             status = STATUS_OBJECT_NAME_INVALID;
2186             goto done;
2187         }
2188         attr.Length = sizeof(attr);
2189         attr.RootDirectory = 0;
2190         attr.Attributes = OBJ_CASE_INSENSITIVE;
2191         attr.ObjectName = &dirW;
2192         attr.SecurityDescriptor = NULL;
2193         attr.SecurityQualityOfService = NULL;
2194
2195         status = NtOpenFile( &handle, 0, &attr, &io, 0,
2196                              FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
2197         RtlFreeUnicodeString( &dirW );
2198         if (status != STATUS_SUCCESS) goto done;
2199     }
2200
2201     if ((status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )) == STATUS_SUCCESS)
2202     {
2203         RtlEnterCriticalSection( &dir_section );
2204
2205         if ((old_cwd = open(".", O_RDONLY)) != -1 && fchdir( unix_fd ) != -1)
2206         {
2207             unsigned int size = 512;
2208
2209             for (;;)
2210             {
2211                 if (!(*cwd = RtlAllocateHeap( GetProcessHeap(), 0, size )))
2212                 {
2213                     status = STATUS_NO_MEMORY;
2214                     break;
2215                 }
2216                 if (getcwd( *cwd, size )) break;
2217                 RtlFreeHeap( GetProcessHeap(), 0, *cwd );
2218                 if (errno != ERANGE)
2219                 {
2220                     status = STATUS_OBJECT_PATH_INVALID;
2221                     break;
2222                 }
2223                 size *= 2;
2224             }
2225             if (fchdir( old_cwd ) == -1) chdir( "/" );
2226         }
2227         else status = FILE_GetNtStatus();
2228
2229         RtlLeaveCriticalSection( &dir_section );
2230         if (needs_close) close( unix_fd );
2231     }
2232     if (!curdir->Handle) NtClose( handle );
2233
2234 done:
2235     RtlReleasePebLock();
2236     return status;
2237 }
2238
2239 struct read_changes_info
2240 {
2241     HANDLE FileHandle;
2242     PVOID Buffer;
2243     ULONG BufferSize;
2244     PIO_APC_ROUTINE apc;
2245     void           *apc_arg;
2246 };
2247
2248 /* callback for ioctl user APC */
2249 static void WINAPI read_changes_user_apc( void *arg, IO_STATUS_BLOCK *io, ULONG reserved )
2250 {
2251     struct read_changes_info *info = arg;
2252     if (info->apc) info->apc( info->apc_arg, io, reserved );
2253     RtlFreeHeap( GetProcessHeap(), 0, info );
2254 }
2255
2256 static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, ULONG_PTR *total )
2257 {
2258     struct read_changes_info *info = user;
2259     char path[PATH_MAX];
2260     NTSTATUS ret = STATUS_SUCCESS;
2261     int len, action, i;
2262
2263     SERVER_START_REQ( read_change )
2264     {
2265         req->handle = info->FileHandle;
2266         wine_server_set_reply( req, path, PATH_MAX );
2267         ret = wine_server_call( req );
2268         action = reply->action;
2269         len = wine_server_reply_size( reply );
2270     }
2271     SERVER_END_REQ;
2272
2273     if (ret == STATUS_SUCCESS && info->Buffer && 
2274         (info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
2275     {
2276         PFILE_NOTIFY_INFORMATION pfni;
2277
2278         pfni = (PFILE_NOTIFY_INFORMATION) info->Buffer;
2279
2280         /* convert to an NT style path */
2281         for (i=0; i<len; i++)
2282             if (path[i] == '/')
2283                 path[i] = '\\';
2284
2285         len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
2286                                info->BufferSize - sizeof (*pfni) );
2287
2288         pfni->NextEntryOffset = 0;
2289         pfni->Action = action;
2290         pfni->FileNameLength = len * sizeof (WCHAR);
2291         pfni->FileName[len] = 0;
2292         len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
2293     }
2294     else
2295     {
2296         ret = STATUS_NOTIFY_ENUM_DIR;
2297         len = 0;
2298     }
2299
2300     iosb->u.Status = ret;
2301     iosb->Information = *total = len;
2302     return ret;
2303 }
2304
2305 #define FILE_NOTIFY_ALL        (  \
2306  FILE_NOTIFY_CHANGE_FILE_NAME   | \
2307  FILE_NOTIFY_CHANGE_DIR_NAME    | \
2308  FILE_NOTIFY_CHANGE_ATTRIBUTES  | \
2309  FILE_NOTIFY_CHANGE_SIZE        | \
2310  FILE_NOTIFY_CHANGE_LAST_WRITE  | \
2311  FILE_NOTIFY_CHANGE_LAST_ACCESS | \
2312  FILE_NOTIFY_CHANGE_CREATION    | \
2313  FILE_NOTIFY_CHANGE_SECURITY   )
2314
2315 /******************************************************************************
2316  *  NtNotifyChangeDirectoryFile [NTDLL.@]
2317  */
2318 NTSTATUS WINAPI
2319 NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
2320         PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
2321         PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
2322         ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
2323 {
2324     struct read_changes_info *info;
2325     NTSTATUS status;
2326     ULONG_PTR cvalue = ApcRoutine ? 0 : (ULONG_PTR)ApcContext;
2327
2328     TRACE("%p %p %p %p %p %p %u %u %d\n",
2329           FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
2330           Buffer, BufferSize, CompletionFilter, WatchTree );
2331
2332     if (!IoStatusBlock)
2333         return STATUS_ACCESS_VIOLATION;
2334
2335     if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
2336         return STATUS_INVALID_PARAMETER;
2337
2338     info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof *info );
2339     if (!info)
2340         return STATUS_NO_MEMORY;
2341
2342     info->FileHandle = FileHandle;
2343     info->Buffer     = Buffer;
2344     info->BufferSize = BufferSize;
2345     info->apc        = ApcRoutine;
2346     info->apc_arg    = ApcContext;
2347
2348     SERVER_START_REQ( read_directory_changes )
2349     {
2350         req->handle     = FileHandle;
2351         req->filter     = CompletionFilter;
2352         req->want_data  = (Buffer != NULL);
2353         req->subtree    = WatchTree;
2354         req->async.callback = read_changes_apc;
2355         req->async.iosb     = IoStatusBlock;
2356         req->async.arg      = info;
2357         req->async.apc      = read_changes_user_apc;
2358         req->async.event    = Event;
2359         req->async.cvalue   = cvalue;
2360         status = wine_server_call( req );
2361     }
2362     SERVER_END_REQ;
2363
2364     if (status != STATUS_PENDING)
2365         RtlFreeHeap( GetProcessHeap(), 0, info );
2366
2367     return status;
2368 }