ntdll: Don't bother to check the file name against the mask if stat() succeeded.
[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, NULL );
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             else io->u.Status = STATUS_NO_MORE_FILES;
1521         }
1522     }
1523     else ret = -1;
1524
1525 done:
1526     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1527
1528     TRACE("returning %d\n", ret);
1529
1530     return ret;
1531 }
1532
1533
1534 static inline WCHAR *mempbrkW( const WCHAR *ptr, const WCHAR *accept, size_t n )
1535 {
1536     const WCHAR *end;
1537     for (end = ptr + n; ptr < end; ptr++) if (strchrW( accept, *ptr )) return (WCHAR *)ptr;
1538     return NULL;
1539 }
1540
1541 /******************************************************************************
1542  *  NtQueryDirectoryFile        [NTDLL.@]
1543  *  ZwQueryDirectoryFile        [NTDLL.@]
1544  */
1545 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
1546                                       PIO_APC_ROUTINE apc_routine, PVOID apc_context,
1547                                       PIO_STATUS_BLOCK io,
1548                                       PVOID buffer, ULONG length,
1549                                       FILE_INFORMATION_CLASS info_class,
1550                                       BOOLEAN single_entry,
1551                                       PUNICODE_STRING mask,
1552                                       BOOLEAN restart_scan )
1553 {
1554     int cwd, fd, needs_close;
1555     static const WCHAR wszWildcards[] = { '*','?',0 };
1556
1557     TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
1558           handle, event, apc_routine, apc_context, io, buffer,
1559           length, info_class, single_entry, debugstr_us(mask),
1560           restart_scan);
1561
1562     if (length < sizeof(FILE_BOTH_DIR_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
1563
1564     if (event || apc_routine)
1565     {
1566         FIXME( "Unsupported yet option\n" );
1567         return io->u.Status = STATUS_NOT_IMPLEMENTED;
1568     }
1569     if (info_class != FileBothDirectoryInformation)
1570     {
1571         FIXME( "Unsupported file info class %d\n", info_class );
1572         return io->u.Status = STATUS_NOT_IMPLEMENTED;
1573     }
1574
1575     if ((io->u.Status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
1576         return io->u.Status;
1577
1578     io->Information = 0;
1579
1580     RtlEnterCriticalSection( &dir_section );
1581
1582     if (show_dot_files == -1) init_options();
1583
1584     cwd = open( ".", O_RDONLY );
1585     if (fchdir( fd ) != -1)
1586     {
1587 #ifdef VFAT_IOCTL_READDIR_BOTH
1588         if ((read_directory_vfat( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1589             goto done;
1590 #endif
1591         if (mask && !mempbrkW( mask->Buffer, wszWildcards, mask->Length / sizeof(WCHAR) ) &&
1592             read_directory_stat( fd, io, buffer, length, single_entry, mask, restart_scan ) != -1)
1593             goto done;
1594 #ifdef USE_GETDENTS
1595         if ((read_directory_getdents( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1596             goto done;
1597 #elif defined HAVE_GETDIRENTRIES
1598         if ((read_directory_getdirentries( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1599             goto done;
1600 #endif
1601         read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan );
1602
1603     done:
1604         if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
1605     }
1606     else io->u.Status = FILE_GetNtStatus();
1607
1608     RtlLeaveCriticalSection( &dir_section );
1609
1610     if (needs_close) close( fd );
1611     if (cwd != -1) close( cwd );
1612     TRACE( "=> %x (%ld)\n", io->u.Status, io->Information );
1613     return io->u.Status;
1614 }
1615
1616
1617 /***********************************************************************
1618  *           find_file_in_dir
1619  *
1620  * Find a file in a directory the hard way, by doing a case-insensitive search.
1621  * The file found is appended to unix_name at pos.
1622  * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
1623  */
1624 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
1625                                   int check_case )
1626 {
1627     WCHAR buffer[MAX_DIR_ENTRY_LEN];
1628     UNICODE_STRING str;
1629     BOOLEAN spaces;
1630     DIR *dir;
1631     struct dirent *de;
1632     struct stat st;
1633     int ret, used_default, is_name_8_dot_3;
1634
1635     /* try a shortcut for this directory */
1636
1637     unix_name[pos++] = '/';
1638     ret = ntdll_wcstoumbs( 0, name, length, unix_name + pos, MAX_DIR_ENTRY_LEN,
1639                            NULL, &used_default );
1640     /* if we used the default char, the Unix name won't round trip properly back to Unicode */
1641     /* so it cannot match the file we are looking for */
1642     if (ret >= 0 && !used_default)
1643     {
1644         unix_name[pos + ret] = 0;
1645         if (!stat( unix_name, &st )) return STATUS_SUCCESS;
1646     }
1647     if (check_case) goto not_found;  /* we want an exact match */
1648
1649     if (pos > 1) unix_name[pos - 1] = 0;
1650     else unix_name[1] = 0;  /* keep the initial slash */
1651
1652     /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
1653
1654     str.Buffer = (WCHAR *)name;
1655     str.Length = length * sizeof(WCHAR);
1656     str.MaximumLength = str.Length;
1657     is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
1658
1659     /* now look for it through the directory */
1660
1661 #ifdef VFAT_IOCTL_READDIR_BOTH
1662     if (is_name_8_dot_3)
1663     {
1664         int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
1665         if (fd != -1)
1666         {
1667             KERNEL_DIRENT *de;
1668
1669             RtlEnterCriticalSection( &dir_section );
1670             if ((de = start_vfat_ioctl( fd )))
1671             {
1672                 unix_name[pos - 1] = '/';
1673                 while (de[0].d_reclen)
1674                 {
1675                     /* make sure names are null-terminated to work around an x86-64 kernel bug */
1676                     size_t len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1677                     de[0].d_name[len] = 0;
1678                     len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1679                     de[1].d_name[len] = 0;
1680
1681                     if (de[1].d_name[0])
1682                     {
1683                         ret = ntdll_umbstowcs( 0, de[1].d_name, strlen(de[1].d_name),
1684                                                buffer, MAX_DIR_ENTRY_LEN );
1685                         if (ret == length && !memicmpW( buffer, name, length))
1686                         {
1687                             strcpy( unix_name + pos, de[1].d_name );
1688                             RtlLeaveCriticalSection( &dir_section );
1689                             close( fd );
1690                             return STATUS_SUCCESS;
1691                         }
1692                     }
1693                     ret = ntdll_umbstowcs( 0, de[0].d_name, strlen(de[0].d_name),
1694                                            buffer, MAX_DIR_ENTRY_LEN );
1695                     if (ret == length && !memicmpW( buffer, name, length))
1696                     {
1697                         strcpy( unix_name + pos,
1698                                 de[1].d_name[0] ? de[1].d_name : de[0].d_name );
1699                         RtlLeaveCriticalSection( &dir_section );
1700                         close( fd );
1701                         return STATUS_SUCCESS;
1702                     }
1703                     if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
1704                     {
1705                         RtlLeaveCriticalSection( &dir_section );
1706                         close( fd );
1707                         goto not_found;
1708                     }
1709                 }
1710             }
1711             RtlLeaveCriticalSection( &dir_section );
1712             close( fd );
1713         }
1714         /* fall through to normal handling */
1715     }
1716 #endif /* VFAT_IOCTL_READDIR_BOTH */
1717
1718     if (!(dir = opendir( unix_name )))
1719     {
1720         if (errno == ENOENT) return STATUS_OBJECT_PATH_NOT_FOUND;
1721         else return FILE_GetNtStatus();
1722     }
1723     unix_name[pos - 1] = '/';
1724     str.Buffer = buffer;
1725     str.MaximumLength = sizeof(buffer);
1726     while ((de = readdir( dir )))
1727     {
1728         ret = ntdll_umbstowcs( 0, de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
1729         if (ret == length && !memicmpW( buffer, name, length ))
1730         {
1731             strcpy( unix_name + pos, de->d_name );
1732             closedir( dir );
1733             return STATUS_SUCCESS;
1734         }
1735
1736         if (!is_name_8_dot_3) continue;
1737
1738         str.Length = ret * sizeof(WCHAR);
1739         if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
1740         {
1741             WCHAR short_nameW[12];
1742             ret = hash_short_file_name( &str, short_nameW );
1743             if (ret == length && !memicmpW( short_nameW, name, length ))
1744             {
1745                 strcpy( unix_name + pos, de->d_name );
1746                 closedir( dir );
1747                 return STATUS_SUCCESS;
1748             }
1749         }
1750     }
1751     closedir( dir );
1752     goto not_found;  /* avoid warning */
1753
1754 not_found:
1755     unix_name[pos - 1] = 0;
1756     return STATUS_OBJECT_PATH_NOT_FOUND;
1757 }
1758
1759
1760 /******************************************************************************
1761  *           get_dos_device
1762  *
1763  * Get the Unix path of a DOS device.
1764  */
1765 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *unix_name_ret )
1766 {
1767     const char *config_dir = wine_get_config_dir();
1768     struct stat st;
1769     char *unix_name, *new_name, *dev;
1770     unsigned int i;
1771     int unix_len;
1772
1773     /* make sure the device name is ASCII */
1774     for (i = 0; i < name_len; i++)
1775         if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
1776
1777     unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
1778
1779     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
1780         return STATUS_NO_MEMORY;
1781
1782     strcpy( unix_name, config_dir );
1783     strcat( unix_name, "/dosdevices/" );
1784     dev = unix_name + strlen(unix_name);
1785
1786     for (i = 0; i < name_len; i++) dev[i] = (char)tolowerW(name[i]);
1787     dev[i] = 0;
1788
1789     /* special case for drive devices */
1790     if (name_len == 2 && dev[1] == ':')
1791     {
1792         dev[i++] = ':';
1793         dev[i] = 0;
1794     }
1795
1796     for (;;)
1797     {
1798         if (!stat( unix_name, &st ))
1799         {
1800             TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
1801             unix_name_ret->Buffer = unix_name;
1802             unix_name_ret->Length = strlen(unix_name);
1803             unix_name_ret->MaximumLength = unix_len;
1804             return STATUS_SUCCESS;
1805         }
1806         if (!dev) break;
1807
1808         /* now try some defaults for it */
1809         if (!strcmp( dev, "aux" ))
1810         {
1811             strcpy( dev, "com1" );
1812             continue;
1813         }
1814         if (!strcmp( dev, "prn" ))
1815         {
1816             strcpy( dev, "lpt1" );
1817             continue;
1818         }
1819         if (!strcmp( dev, "nul" ))
1820         {
1821             strcpy( unix_name, "/dev/null" );
1822             dev = NULL; /* last try */
1823             continue;
1824         }
1825
1826         new_name = NULL;
1827         if (dev[1] == ':' && dev[2] == ':')  /* drive device */
1828         {
1829             dev[2] = 0;  /* remove last ':' to get the drive mount point symlink */
1830             new_name = get_default_drive_device( unix_name );
1831         }
1832         else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( dev[3] - '0' );
1833         else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( dev[3] - '0' );
1834
1835         if (!new_name) break;
1836
1837         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1838         unix_name = new_name;
1839         unix_len = strlen(unix_name) + 1;
1840         dev = NULL; /* last try */
1841     }
1842     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1843     return STATUS_BAD_DEVICE_TYPE;
1844 }
1845
1846
1847 /* return the length of the DOS namespace prefix if any */
1848 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
1849 {
1850     static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
1851     static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
1852
1853     if (name->Length > sizeof(nt_prefixW) &&
1854         !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
1855         return sizeof(nt_prefixW) / sizeof(WCHAR);
1856
1857     if (name->Length > sizeof(dosdev_prefixW) &&
1858         !memicmpW( name->Buffer, dosdev_prefixW, sizeof(dosdev_prefixW)/sizeof(WCHAR) ))
1859         return sizeof(dosdev_prefixW) / sizeof(WCHAR);
1860
1861     return 0;
1862 }
1863
1864
1865 /******************************************************************************
1866  *           wine_nt_to_unix_file_name  (NTDLL.@) Not a Windows API
1867  *
1868  * Convert a file name from NT namespace to Unix namespace.
1869  *
1870  * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
1871  * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
1872  * returned, but the unix name is still filled in properly.
1873  */
1874 NTSTATUS wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
1875                                     UINT disposition, BOOLEAN check_case )
1876 {
1877     static const WCHAR unixW[] = {'u','n','i','x'};
1878     static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
1879
1880     NTSTATUS status = STATUS_SUCCESS;
1881     const char *config_dir = wine_get_config_dir();
1882     const WCHAR *name, *p;
1883     struct stat st;
1884     char *unix_name;
1885     int pos, ret, name_len, unix_len, prefix_len, used_default;
1886     WCHAR prefix[MAX_DIR_ENTRY_LEN];
1887     BOOLEAN is_unix = FALSE;
1888
1889     name     = nameW->Buffer;
1890     name_len = nameW->Length / sizeof(WCHAR);
1891
1892     if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
1893
1894     if (!(pos = get_dos_prefix_len( nameW )))
1895         return STATUS_BAD_DEVICE_TYPE;  /* no DOS prefix, assume NT native name */
1896
1897     name += pos;
1898     name_len -= pos;
1899
1900     /* check for sub-directory */
1901     for (pos = 0; pos < name_len; pos++)
1902     {
1903         if (IS_SEPARATOR(name[pos])) break;
1904         if (name[pos] < 32 || strchrW( invalid_charsW, name[pos] ))
1905             return STATUS_OBJECT_NAME_INVALID;
1906     }
1907     if (pos > MAX_DIR_ENTRY_LEN)
1908         return STATUS_OBJECT_NAME_INVALID;
1909
1910     if (pos == name_len)  /* no subdir, plain DOS device */
1911         return get_dos_device( name, name_len, unix_name_ret );
1912
1913     for (prefix_len = 0; prefix_len < pos; prefix_len++)
1914         prefix[prefix_len] = tolowerW(name[prefix_len]);
1915
1916     name += prefix_len;
1917     name_len -= prefix_len;
1918
1919     /* check for invalid characters (all chars except 0 are valid for unix) */
1920     is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
1921     if (is_unix)
1922     {
1923         for (p = name; p < name + name_len; p++)
1924             if (!*p) return STATUS_OBJECT_NAME_INVALID;
1925         check_case = TRUE;
1926     }
1927     else
1928     {
1929         for (p = name; p < name + name_len; p++)
1930             if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
1931     }
1932
1933     unix_len = ntdll_wcstoumbs( 0, prefix, prefix_len, NULL, 0, NULL, NULL );
1934     unix_len += ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
1935     unix_len += MAX_DIR_ENTRY_LEN + 3;
1936     unix_len += strlen(config_dir) + sizeof("/dosdevices/");
1937     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
1938         return STATUS_NO_MEMORY;
1939     strcpy( unix_name, config_dir );
1940     strcat( unix_name, "/dosdevices/" );
1941     pos = strlen(unix_name);
1942
1943     ret = ntdll_wcstoumbs( 0, prefix, prefix_len, unix_name + pos, unix_len - pos - 1,
1944                            NULL, &used_default );
1945     if (!ret || used_default)
1946     {
1947         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1948         return STATUS_OBJECT_NAME_INVALID;
1949     }
1950     pos += ret;
1951
1952     /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
1953
1954     if (prefix_len != 2 || prefix[1] != ':')
1955     {
1956         unix_name[pos] = 0;
1957         if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
1958         {
1959             if (!is_unix)
1960             {
1961                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1962                 return STATUS_BAD_DEVICE_TYPE;
1963             }
1964             pos = 0;  /* fall back to unix root */
1965         }
1966     }
1967
1968     /* try a shortcut first */
1969
1970     ret = ntdll_wcstoumbs( 0, name, name_len, unix_name + pos, unix_len - pos - 1,
1971                            NULL, &used_default );
1972
1973     while (name_len && IS_SEPARATOR(*name))
1974     {
1975         name++;
1976         name_len--;
1977     }
1978
1979     if (ret > 0 && !used_default)  /* if we used the default char the name didn't convert properly */
1980     {
1981         char *p;
1982         unix_name[pos + ret] = 0;
1983         for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
1984         if (!stat( unix_name, &st ))
1985         {
1986             /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */
1987             if (disposition == FILE_CREATE)
1988             {
1989                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1990                 return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED;
1991             }
1992             goto done;
1993         }
1994     }
1995
1996     if (!name_len)  /* empty name -> drive root doesn't exist */
1997     {
1998         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1999         return STATUS_OBJECT_PATH_NOT_FOUND;
2000     }
2001     if (check_case && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
2002     {
2003         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2004         return STATUS_OBJECT_NAME_NOT_FOUND;
2005     }
2006
2007     /* now do it component by component */
2008
2009     while (name_len)
2010     {
2011         const WCHAR *end, *next;
2012
2013         end = name;
2014         while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
2015         next = end;
2016         while (next < name + name_len && IS_SEPARATOR(*next)) next++;
2017         name_len -= next - name;
2018
2019         /* grow the buffer if needed */
2020
2021         if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
2022         {
2023             char *new_name;
2024             unix_len += 2 * MAX_DIR_ENTRY_LEN;
2025             if (!(new_name = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name, unix_len )))
2026             {
2027                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2028                 return STATUS_NO_MEMORY;
2029             }
2030             unix_name = new_name;
2031         }
2032
2033         status = find_file_in_dir( unix_name, pos, name, end - name, check_case );
2034
2035         /* if this is the last element, not finding it is not necessarily fatal */
2036         if (!name_len)
2037         {
2038             if (status == STATUS_OBJECT_PATH_NOT_FOUND)
2039             {
2040                 status = STATUS_OBJECT_NAME_NOT_FOUND;
2041                 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
2042                 {
2043                     ret = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2044                                            MAX_DIR_ENTRY_LEN, NULL, &used_default );
2045                     if (ret > 0 && !used_default)
2046                     {
2047                         unix_name[pos] = '/';
2048                         unix_name[pos + 1 + ret] = 0;
2049                         status = STATUS_NO_SUCH_FILE;
2050                         break;
2051                     }
2052                 }
2053             }
2054             else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
2055             {
2056                 status = STATUS_OBJECT_NAME_COLLISION;
2057             }
2058         }
2059
2060         if (status != STATUS_SUCCESS)
2061         {
2062             /* couldn't find it at all, fail */
2063             WARN( "%s not found in %s\n", debugstr_w(name), unix_name );
2064             RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2065             return status;
2066         }
2067
2068         pos += strlen( unix_name + pos );
2069         name = next;
2070     }
2071
2072     WARN( "%s -> %s required a case-insensitive search\n",
2073           debugstr_us(nameW), debugstr_a(unix_name) );
2074
2075 done:
2076     TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
2077     unix_name_ret->Buffer = unix_name;
2078     unix_name_ret->Length = strlen(unix_name);
2079     unix_name_ret->MaximumLength = unix_len;
2080     return status;
2081 }
2082
2083
2084 /******************************************************************
2085  *              RtlDoesFileExists_U   (NTDLL.@)
2086  */
2087 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
2088 {
2089     UNICODE_STRING nt_name;
2090     FILE_BASIC_INFORMATION basic_info;
2091     OBJECT_ATTRIBUTES attr;
2092     BOOLEAN ret;
2093
2094     if (!RtlDosPathNameToNtPathName_U( file_name, &nt_name, NULL, NULL )) return FALSE;
2095
2096     attr.Length = sizeof(attr);
2097     attr.RootDirectory = 0;
2098     attr.ObjectName = &nt_name;
2099     attr.Attributes = OBJ_CASE_INSENSITIVE;
2100     attr.SecurityDescriptor = NULL;
2101     attr.SecurityQualityOfService = NULL;
2102
2103     ret = NtQueryAttributesFile(&attr, &basic_info) == STATUS_SUCCESS;
2104
2105     RtlFreeUnicodeString( &nt_name );
2106     return ret;
2107 }
2108
2109
2110 /***********************************************************************
2111  *           DIR_unmount_device
2112  *
2113  * Unmount the specified device.
2114  */
2115 NTSTATUS DIR_unmount_device( HANDLE handle )
2116 {
2117     NTSTATUS status;
2118     int unix_fd, needs_close;
2119
2120     if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
2121     {
2122         struct stat st;
2123         char *mount_point = NULL;
2124
2125         if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
2126             status = STATUS_INVALID_PARAMETER;
2127         else
2128         {
2129             if ((mount_point = get_device_mount_point( st.st_rdev )))
2130             {
2131 #ifdef __APPLE__
2132                 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
2133 #else
2134                 static const char umount[] = "umount >/dev/null 2>&1 ";
2135 #endif
2136                 char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount));
2137                 if (cmd)
2138                 {
2139                     strcpy( cmd, umount );
2140                     strcat( cmd, mount_point );
2141                     system( cmd );
2142                     RtlFreeHeap( GetProcessHeap(), 0, cmd );
2143 #ifdef linux
2144                     /* umount will fail to release the loop device since we still have
2145                        a handle to it, so we release it here */
2146                     if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
2147 #endif
2148                 }
2149                 RtlFreeHeap( GetProcessHeap(), 0, mount_point );
2150             }
2151         }
2152         if (needs_close) close( unix_fd );
2153     }
2154     return status;
2155 }
2156
2157
2158 /******************************************************************************
2159  *           DIR_get_unix_cwd
2160  *
2161  * Retrieve the Unix name of the current directory; helper for wine_unix_to_nt_file_name.
2162  * Returned value must be freed by caller.
2163  */
2164 NTSTATUS DIR_get_unix_cwd( char **cwd )
2165 {
2166     int old_cwd, unix_fd, needs_close;
2167     CURDIR *curdir;
2168     HANDLE handle;
2169     NTSTATUS status;
2170
2171     RtlAcquirePebLock();
2172
2173     if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
2174         curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
2175     else
2176         curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
2177
2178     if (!(handle = curdir->Handle))
2179     {
2180         UNICODE_STRING dirW;
2181         OBJECT_ATTRIBUTES attr;
2182         IO_STATUS_BLOCK io;
2183
2184         if (!RtlDosPathNameToNtPathName_U( curdir->DosPath.Buffer, &dirW, NULL, NULL ))
2185         {
2186             status = STATUS_OBJECT_NAME_INVALID;
2187             goto done;
2188         }
2189         attr.Length = sizeof(attr);
2190         attr.RootDirectory = 0;
2191         attr.Attributes = OBJ_CASE_INSENSITIVE;
2192         attr.ObjectName = &dirW;
2193         attr.SecurityDescriptor = NULL;
2194         attr.SecurityQualityOfService = NULL;
2195
2196         status = NtOpenFile( &handle, 0, &attr, &io, 0,
2197                              FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
2198         RtlFreeUnicodeString( &dirW );
2199         if (status != STATUS_SUCCESS) goto done;
2200     }
2201
2202     if ((status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )) == STATUS_SUCCESS)
2203     {
2204         RtlEnterCriticalSection( &dir_section );
2205
2206         if ((old_cwd = open(".", O_RDONLY)) != -1 && fchdir( unix_fd ) != -1)
2207         {
2208             unsigned int size = 512;
2209
2210             for (;;)
2211             {
2212                 if (!(*cwd = RtlAllocateHeap( GetProcessHeap(), 0, size )))
2213                 {
2214                     status = STATUS_NO_MEMORY;
2215                     break;
2216                 }
2217                 if (getcwd( *cwd, size )) break;
2218                 RtlFreeHeap( GetProcessHeap(), 0, *cwd );
2219                 if (errno != ERANGE)
2220                 {
2221                     status = STATUS_OBJECT_PATH_INVALID;
2222                     break;
2223                 }
2224                 size *= 2;
2225             }
2226             if (fchdir( old_cwd ) == -1) chdir( "/" );
2227         }
2228         else status = FILE_GetNtStatus();
2229
2230         RtlLeaveCriticalSection( &dir_section );
2231         if (needs_close) close( unix_fd );
2232     }
2233     if (!curdir->Handle) NtClose( handle );
2234
2235 done:
2236     RtlReleasePebLock();
2237     return status;
2238 }
2239
2240 struct read_changes_info
2241 {
2242     HANDLE FileHandle;
2243     PVOID Buffer;
2244     ULONG BufferSize;
2245     PIO_APC_ROUTINE apc;
2246     void           *apc_arg;
2247 };
2248
2249 /* callback for ioctl user APC */
2250 static void WINAPI read_changes_user_apc( void *arg, IO_STATUS_BLOCK *io, ULONG reserved )
2251 {
2252     struct read_changes_info *info = arg;
2253     if (info->apc) info->apc( info->apc_arg, io, reserved );
2254     RtlFreeHeap( GetProcessHeap(), 0, info );
2255 }
2256
2257 static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, ULONG_PTR *total )
2258 {
2259     struct read_changes_info *info = user;
2260     char path[PATH_MAX];
2261     NTSTATUS ret = STATUS_SUCCESS;
2262     int len, action, i;
2263
2264     SERVER_START_REQ( read_change )
2265     {
2266         req->handle = info->FileHandle;
2267         wine_server_set_reply( req, path, PATH_MAX );
2268         ret = wine_server_call( req );
2269         action = reply->action;
2270         len = wine_server_reply_size( reply );
2271     }
2272     SERVER_END_REQ;
2273
2274     if (ret == STATUS_SUCCESS && info->Buffer && 
2275         (info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
2276     {
2277         PFILE_NOTIFY_INFORMATION pfni;
2278
2279         pfni = (PFILE_NOTIFY_INFORMATION) info->Buffer;
2280
2281         /* convert to an NT style path */
2282         for (i=0; i<len; i++)
2283             if (path[i] == '/')
2284                 path[i] = '\\';
2285
2286         len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
2287                                info->BufferSize - sizeof (*pfni) );
2288
2289         pfni->NextEntryOffset = 0;
2290         pfni->Action = action;
2291         pfni->FileNameLength = len * sizeof (WCHAR);
2292         pfni->FileName[len] = 0;
2293         len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
2294     }
2295     else
2296     {
2297         ret = STATUS_NOTIFY_ENUM_DIR;
2298         len = 0;
2299     }
2300
2301     iosb->u.Status = ret;
2302     iosb->Information = *total = len;
2303     return ret;
2304 }
2305
2306 #define FILE_NOTIFY_ALL        (  \
2307  FILE_NOTIFY_CHANGE_FILE_NAME   | \
2308  FILE_NOTIFY_CHANGE_DIR_NAME    | \
2309  FILE_NOTIFY_CHANGE_ATTRIBUTES  | \
2310  FILE_NOTIFY_CHANGE_SIZE        | \
2311  FILE_NOTIFY_CHANGE_LAST_WRITE  | \
2312  FILE_NOTIFY_CHANGE_LAST_ACCESS | \
2313  FILE_NOTIFY_CHANGE_CREATION    | \
2314  FILE_NOTIFY_CHANGE_SECURITY   )
2315
2316 /******************************************************************************
2317  *  NtNotifyChangeDirectoryFile [NTDLL.@]
2318  */
2319 NTSTATUS WINAPI
2320 NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
2321         PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
2322         PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
2323         ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
2324 {
2325     struct read_changes_info *info;
2326     NTSTATUS status;
2327     ULONG_PTR cvalue = ApcRoutine ? 0 : (ULONG_PTR)ApcContext;
2328
2329     TRACE("%p %p %p %p %p %p %u %u %d\n",
2330           FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
2331           Buffer, BufferSize, CompletionFilter, WatchTree );
2332
2333     if (!IoStatusBlock)
2334         return STATUS_ACCESS_VIOLATION;
2335
2336     if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
2337         return STATUS_INVALID_PARAMETER;
2338
2339     info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof *info );
2340     if (!info)
2341         return STATUS_NO_MEMORY;
2342
2343     info->FileHandle = FileHandle;
2344     info->Buffer     = Buffer;
2345     info->BufferSize = BufferSize;
2346     info->apc        = ApcRoutine;
2347     info->apc_arg    = ApcContext;
2348
2349     SERVER_START_REQ( read_directory_changes )
2350     {
2351         req->handle     = FileHandle;
2352         req->filter     = CompletionFilter;
2353         req->want_data  = (Buffer != NULL);
2354         req->subtree    = WatchTree;
2355         req->async.callback = read_changes_apc;
2356         req->async.iosb     = IoStatusBlock;
2357         req->async.arg      = info;
2358         req->async.apc      = read_changes_user_apc;
2359         req->async.event    = Event;
2360         req->async.cvalue   = cvalue;
2361         status = wine_server_call( req );
2362     }
2363     SERVER_END_REQ;
2364
2365     if (status != STATUS_PENDING)
2366         RtlFreeHeap( GetProcessHeap(), 0, info );
2367
2368     return status;
2369 }