ntdll: Compile VIRTUAL_Dump only when somebody really needs it.
[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 "thread.h"
68 #include "winternl.h"
69 #include "ntdll_misc.h"
70 #include "wine/unicode.h"
71 #include "wine/server.h"
72 #include "wine/library.h"
73 #include "wine/debug.h"
74
75 WINE_DEFAULT_DEBUG_CHANNEL(file);
76
77 /* just in case... */
78 #undef VFAT_IOCTL_READDIR_BOTH
79 #undef USE_GETDENTS
80
81 #ifdef linux
82
83 /* We want the real kernel dirent structure, not the libc one */
84 typedef struct
85 {
86     long d_ino;
87     long d_off;
88     unsigned short d_reclen;
89     char d_name[256];
90 } KERNEL_DIRENT;
91
92 /* Define the VFAT ioctl to get both short and long file names */
93 #define VFAT_IOCTL_READDIR_BOTH  _IOR('r', 1, KERNEL_DIRENT [2] )
94
95 #ifndef O_DIRECTORY
96 # define O_DIRECTORY 0200000 /* must be directory */
97 #endif
98
99 #ifdef __i386__
100
101 typedef struct
102 {
103     ULONG64        d_ino;
104     LONG64         d_off;
105     unsigned short d_reclen;
106     unsigned char  d_type;
107     char           d_name[256];
108 } KERNEL_DIRENT64;
109
110 static inline int getdents64( int fd, KERNEL_DIRENT64 *de, unsigned int size )
111 {
112     int ret;
113     __asm__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
114              : "=a" (ret)
115              : "0" (220 /*NR_getdents64*/), "r" (fd), "c" (de), "d" (size)
116              : "memory" );
117     if (ret < 0)
118     {
119         errno = -ret;
120         ret = -1;
121     }
122     return ret;
123 }
124 #define USE_GETDENTS
125
126 #endif  /* i386 */
127
128 #endif  /* linux */
129
130 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
131 #define IS_SEPARATOR(ch)   ((ch) == '\\' || (ch) == '/')
132
133 #define INVALID_NT_CHARS   '*','?','<','>','|','"'
134 #define INVALID_DOS_CHARS  INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
135
136 #define MAX_DIR_ENTRY_LEN 255  /* max length of a directory entry in chars */
137
138 static int show_dot_files = -1;
139
140 /* at some point we may want to allow Winelib apps to set this */
141 static const int is_case_sensitive = FALSE;
142
143 static RTL_CRITICAL_SECTION dir_section;
144 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
145 {
146     0, 0, &dir_section,
147     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
148       0, 0, { (DWORD_PTR)(__FILE__ ": dir_section") }
149 };
150 static RTL_CRITICAL_SECTION dir_section = { &critsect_debug, -1, 0, 0, 0, 0 };
151
152
153 /* check if a given Unicode char is OK in a DOS short name */
154 static inline BOOL is_invalid_dos_char( WCHAR ch )
155 {
156     static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
157     if (ch > 0x7f) return TRUE;
158     return strchrW( invalid_chars, ch ) != NULL;
159 }
160
161 /***********************************************************************
162  *           get_default_com_device
163  *
164  * Return the default device to use for serial ports.
165  */
166 static char *get_default_com_device( int num )
167 {
168     char *ret = NULL;
169
170     if (!num || num > 9) return ret;
171 #ifdef linux
172     ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/ttyS0") );
173     if (ret)
174     {
175         strcpy( ret, "/dev/ttyS0" );
176         ret[strlen(ret) - 1] = '0' + num - 1;
177     }
178 #else
179     FIXME( "no known default for device com%d\n", num );
180 #endif
181     return ret;
182 }
183
184
185 /***********************************************************************
186  *           get_default_lpt_device
187  *
188  * Return the default device to use for parallel ports.
189  */
190 static char *get_default_lpt_device( int num )
191 {
192     char *ret = NULL;
193
194     if (!num || num > 9) return ret;
195 #ifdef linux
196     ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/lp0") );
197     if (ret)
198     {
199         strcpy( ret, "/dev/lp0" );
200         ret[strlen(ret) - 1] = '0' + num - 1;
201     }
202 #else
203     FIXME( "no known default for device lpt%d\n", num );
204 #endif
205     return ret;
206 }
207
208
209 /***********************************************************************
210  *           parse_mount_entries
211  *
212  * Parse mount entries looking for a given device. Helper for get_default_drive_device.
213  */
214
215 #ifdef sun
216 #include <sys/vfstab.h>
217 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
218 {
219
220     struct vfstab vfs_entry;
221     struct vfstab *entry=&vfs_entry;
222     struct stat st;
223     char *device;
224
225     while (! getvfsent( f, entry ))
226     {
227         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
228         if (!strcmp( entry->vfs_fstype, "nfs" ) ||
229             !strcmp( entry->vfs_fstype, "smbfs" ) ||
230             !strcmp( entry->vfs_fstype, "ncpfs" )) continue;
231
232         if (stat( entry->vfs_mountp, &st ) == -1) continue;
233         if (st.st_dev != dev || st.st_ino != ino) continue;
234         if (!strcmp( entry->vfs_fstype, "fd" ))
235         {
236             if ((device = strstr( entry->vfs_mntopts, "dev=" )))
237             {
238                 char *p = strchr( device + 4, ',' );
239                 if (p) *p = 0;
240                 return device + 4;
241             }
242         }
243         else
244             return entry->vfs_special;
245     }
246     return NULL;
247 }
248 #endif
249
250 #ifdef linux
251 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
252 {
253     struct mntent *entry;
254     struct stat st;
255     char *device;
256
257     while ((entry = getmntent( f )))
258     {
259         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
260         if (!strcmp( entry->mnt_type, "nfs" ) ||
261             !strcmp( entry->mnt_type, "smbfs" ) ||
262             !strcmp( entry->mnt_type, "ncpfs" )) continue;
263
264         if (stat( entry->mnt_dir, &st ) == -1) continue;
265         if (st.st_dev != dev || st.st_ino != ino) continue;
266         if (!strcmp( entry->mnt_type, "supermount" ))
267         {
268             if ((device = strstr( entry->mnt_opts, "dev=" )))
269             {
270                 char *p = strchr( device + 4, ',' );
271                 if (p) *p = 0;
272                 return device + 4;
273             }
274         }
275         else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
276         {
277             /* if device is a regular file check for a loop mount */
278             if ((device = strstr( entry->mnt_opts, "loop=" )))
279             {
280                 char *p = strchr( device + 5, ',' );
281                 if (p) *p = 0;
282                 return device + 5;
283             }
284         }
285         else
286             return entry->mnt_fsname;
287     }
288     return NULL;
289 }
290 #endif
291
292 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
293 #include <fstab.h>
294 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
295 {
296     struct fstab *entry;
297     struct stat st;
298
299     while ((entry = getfsent()))
300     {
301         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
302         if (!strcmp( entry->fs_vfstype, "nfs" ) ||
303             !strcmp( entry->fs_vfstype, "smbfs" ) ||
304             !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
305
306         if (stat( entry->fs_file, &st ) == -1) continue;
307         if (st.st_dev != dev || st.st_ino != ino) continue;
308         return entry->fs_spec;
309     }
310     return NULL;
311 }
312 #endif
313
314 #ifdef sun
315 #include <sys/mnttab.h>
316 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
317 {
318
319     volatile struct mnttab mntentry;
320     struct mnttab *entry=&mntentry;
321     struct stat st;
322     char *device;
323
324
325     while (( ! getmntent( f , entry) ))
326     {
327         /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
328         if (!strcmp( entry->mnt_fstype, "nfs" ) ||
329             !strcmp( entry->mnt_fstype, "smbfs" ) ||
330             !strcmp( entry->mnt_fstype, "ncpfs" )) continue;
331
332         if (stat( entry->mnt_mountp, &st ) == -1) continue;
333         if (st.st_dev != dev || st.st_ino != ino) continue;
334         if (!strcmp( entry->mnt_fstype, "fd" ))
335         {
336             if ((device = strstr( entry->mnt_mntopts, "dev=" )))
337             {
338                 char *p = strchr( device + 4, ',' );
339                 if (p) *p = 0;
340                 return device + 4;
341             }
342         }
343         else
344             return entry->mnt_special;
345     }
346     return NULL;
347 }
348 #endif
349
350 /***********************************************************************
351  *           get_default_drive_device
352  *
353  * Return the default device to use for a given drive mount point.
354  */
355 static char *get_default_drive_device( const char *root )
356 {
357     char *ret = NULL;
358
359 #ifdef linux
360     FILE *f;
361     char *device = NULL;
362     int fd, res = -1;
363     struct stat st;
364
365     /* try to open it first to force it to get mounted */
366     if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
367     {
368         res = fstat( fd, &st );
369         close( fd );
370     }
371     /* now try normal stat just in case */
372     if (res == -1) res = stat( root, &st );
373     if (res == -1) return NULL;
374
375     RtlEnterCriticalSection( &dir_section );
376
377     if ((f = fopen( "/etc/mtab", "r" )))
378     {
379         device = parse_mount_entries( f, st.st_dev, st.st_ino );
380         endmntent( f );
381     }
382     /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
383     if (!device && (f = fopen( "/etc/fstab", "r" )))
384     {
385         device = parse_mount_entries( f, st.st_dev, st.st_ino );
386         endmntent( f );
387     }
388     if (device)
389     {
390         ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
391         if (ret) strcpy( ret, device );
392     }
393     RtlLeaveCriticalSection( &dir_section );
394
395 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ )
396     char *device = NULL;
397     int fd, res = -1;
398     struct stat st;
399
400     /* try to open it first to force it to get mounted */
401     if ((fd = open( root, O_RDONLY )) != -1)
402     {
403         res = fstat( fd, &st );
404         close( fd );
405     }
406     /* now try normal stat just in case */
407     if (res == -1) res = stat( root, &st );
408     if (res == -1) return NULL;
409
410     RtlEnterCriticalSection( &dir_section );
411
412     /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
413      * pass NULL.  Leave the argument in for symmetry.
414      */
415     device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
416     if (device)
417     {
418         ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
419         if (ret) strcpy( ret, device );
420     }
421     RtlLeaveCriticalSection( &dir_section );
422
423 #elif defined( sun )
424     FILE *f;
425     char *device = NULL;
426     int fd, res = -1;
427     struct stat st;
428
429     /* try to open it first to force it to get mounted */
430     if ((fd = open( root, O_RDONLY )) != -1)
431     {
432         res = fstat( fd, &st );
433         close( fd );
434     }
435     /* now try normal stat just in case */
436     if (res == -1) res = stat( root, &st );
437     if (res == -1) return NULL;
438
439     RtlEnterCriticalSection( &dir_section );
440
441     if ((f = fopen( "/etc/mnttab", "r" )))
442     {
443         device = parse_mount_entries( f, st.st_dev, st.st_ino);
444         fclose( f );
445     }
446     /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
447     if (!device && (f = fopen( "/etc/vfstab", "r" )))
448     {
449         device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
450         fclose( f );
451     }
452     if (device)
453     {
454         ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
455         if (ret) strcpy( ret, device );
456     }
457     RtlLeaveCriticalSection( &dir_section );
458
459 #elif defined(__APPLE__)
460     struct statfs *mntStat;
461     struct stat st;
462     int i;
463     int mntSize;
464     dev_t dev;
465     ino_t ino;
466     static const char path_bsd_device[] = "/dev/disk";
467     int res;
468
469     res = stat( root, &st );
470     if (res == -1) return NULL;
471
472     dev = st.st_dev;
473     ino = st.st_ino;
474
475     RtlEnterCriticalSection( &dir_section );
476
477     mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
478
479     for (i = 0; i < mntSize && !ret; i++)
480     {
481         if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
482         if (st.st_dev != dev || st.st_ino != ino) continue;
483
484         /* FIXME add support for mounted network drive */
485         if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
486         {
487             /* set return value to the corresponding raw BSD node */
488             ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
489             if (ret)
490             {
491                 strcpy(ret, "/dev/r");
492                 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
493             }
494         }
495     }
496     RtlLeaveCriticalSection( &dir_section );
497 #else
498     static int warned;
499     if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
500 #endif
501     return ret;
502 }
503
504
505 /***********************************************************************
506  *           get_device_mount_point
507  *
508  * Return the current mount point for a device.
509  */
510 static char *get_device_mount_point( dev_t dev )
511 {
512     char *ret = NULL;
513
514 #ifdef linux
515     FILE *f;
516
517     RtlEnterCriticalSection( &dir_section );
518
519     if ((f = fopen( "/etc/mtab", "r" )))
520     {
521         struct mntent *entry;
522         struct stat st;
523         char *p, *device;
524
525         while ((entry = getmntent( f )))
526         {
527             /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
528             if (!strcmp( entry->mnt_type, "nfs" ) ||
529                 !strcmp( entry->mnt_type, "smbfs" ) ||
530                 !strcmp( entry->mnt_type, "ncpfs" )) continue;
531
532             if (!strcmp( entry->mnt_type, "supermount" ))
533             {
534                 if ((device = strstr( entry->mnt_opts, "dev=" )))
535                 {
536                     device += 4;
537                     if ((p = strchr( device, ',' ))) *p = 0;
538                 }
539             }
540             else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
541             {
542                 /* if device is a regular file check for a loop mount */
543                 if ((device = strstr( entry->mnt_opts, "loop=" )))
544                 {
545                     device += 5;
546                     if ((p = strchr( device, ',' ))) *p = 0;
547                 }
548             }
549             else device = entry->mnt_fsname;
550
551             if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
552             {
553                 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry->mnt_dir) + 1 );
554                 if (ret) strcpy( ret, entry->mnt_dir );
555                 break;
556             }
557         }
558         endmntent( f );
559     }
560     RtlLeaveCriticalSection( &dir_section );
561 #else
562     static int warned;
563     if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
564 #endif
565     return ret;
566 }
567
568
569 /***********************************************************************
570  *           init_options
571  *
572  * Initialize the show_dot_files options.
573  */
574 static void init_options(void)
575 {
576     static const WCHAR WineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e',0};
577     static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
578     char tmp[80];
579     HANDLE root, hkey;
580     DWORD dummy;
581     OBJECT_ATTRIBUTES attr;
582     UNICODE_STRING nameW;
583
584     show_dot_files = 0;
585
586     RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
587     attr.Length = sizeof(attr);
588     attr.RootDirectory = root;
589     attr.ObjectName = &nameW;
590     attr.Attributes = 0;
591     attr.SecurityDescriptor = NULL;
592     attr.SecurityQualityOfService = NULL;
593     RtlInitUnicodeString( &nameW, WineW );
594
595     /* @@ Wine registry key: HKCU\Software\Wine */
596     if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
597     {
598         RtlInitUnicodeString( &nameW, ShowDotFilesW );
599         if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
600         {
601             WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
602             show_dot_files = IS_OPTION_TRUE( str[0] );
603         }
604         NtClose( hkey );
605     }
606     NtClose( root );
607 }
608
609
610 /***********************************************************************
611  *           DIR_is_hidden_file
612  *
613  * Check if the specified file should be hidden based on its name and the show dot files option.
614  */
615 BOOL DIR_is_hidden_file( const UNICODE_STRING *name )
616 {
617     WCHAR *p, *end;
618
619     if (show_dot_files == -1) init_options();
620     if (show_dot_files) return FALSE;
621
622     end = p = name->Buffer + name->Length/sizeof(WCHAR);
623     while (p > name->Buffer && IS_SEPARATOR(p[-1])) p--;
624     while (p > name->Buffer && !IS_SEPARATOR(p[-1])) p--;
625     if (p == end || *p != '.') return FALSE;
626     /* make sure it isn't '.' or '..' */
627     if (p + 1 == end) return FALSE;
628     if (p[1] == '.' && p + 2 == end) return FALSE;
629     return TRUE;
630 }
631
632
633 /***********************************************************************
634  *           hash_short_file_name
635  *
636  * Transform a Unix file name into a hashed DOS name. If the name is a valid
637  * DOS name, it is converted to upper-case; otherwise it is replaced by a
638  * hashed version that fits in 8.3 format.
639  * 'buffer' must be at least 12 characters long.
640  * Returns length of short name in bytes; short name is NOT null-terminated.
641  */
642 static ULONG hash_short_file_name( const UNICODE_STRING *name, LPWSTR buffer )
643 {
644     static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
645
646     LPCWSTR p, ext, end = name->Buffer + name->Length / sizeof(WCHAR);
647     LPWSTR dst;
648     unsigned short hash;
649     int i;
650
651     /* Compute the hash code of the file name */
652     /* If you know something about hash functions, feel free to */
653     /* insert a better algorithm here... */
654     if (!is_case_sensitive)
655     {
656         for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
657             hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
658         hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
659     }
660     else
661     {
662         for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
663             hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
664         hash = (hash << 3) ^ (hash >> 5) ^ *p;  /* Last character */
665     }
666
667     /* Find last dot for start of the extension */
668     for (p = name->Buffer + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
669
670     /* Copy first 4 chars, replacing invalid chars with '_' */
671     for (i = 4, p = name->Buffer, dst = buffer; i > 0; i--, p++)
672     {
673         if (p == end || p == ext) break;
674         *dst++ = is_invalid_dos_char(*p) ? '_' : toupperW(*p);
675     }
676     /* Pad to 5 chars with '~' */
677     while (i-- >= 0) *dst++ = '~';
678
679     /* Insert hash code converted to 3 ASCII chars */
680     *dst++ = hash_chars[(hash >> 10) & 0x1f];
681     *dst++ = hash_chars[(hash >> 5) & 0x1f];
682     *dst++ = hash_chars[hash & 0x1f];
683
684     /* Copy the first 3 chars of the extension (if any) */
685     if (ext)
686     {
687         *dst++ = '.';
688         for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
689             *dst++ = is_invalid_dos_char(*ext) ? '_' : toupperW(*ext);
690     }
691     return dst - buffer;
692 }
693
694
695 /***********************************************************************
696  *           match_filename
697  *
698  * Check a long file name against a mask.
699  *
700  * Tests (done in W95 DOS shell - case insensitive):
701  * *.txt                        test1.test.txt                          *
702  * *st1*                        test1.txt                               *
703  * *.t??????.t*                 test1.ta.tornado.txt                    *
704  * *tornado*                    test1.ta.tornado.txt                    *
705  * t*t                          test1.ta.tornado.txt                    *
706  * ?est*                        test1.txt                               *
707  * ?est???                      test1.txt                               -
708  * *test1.txt*                  test1.txt                               *
709  * h?l?o*t.dat                  hellothisisatest.dat                    *
710  */
711 static BOOLEAN match_filename( const UNICODE_STRING *name_str, const UNICODE_STRING *mask_str )
712 {
713     int mismatch;
714     const WCHAR *name = name_str->Buffer;
715     const WCHAR *mask = mask_str->Buffer;
716     const WCHAR *name_end = name + name_str->Length / sizeof(WCHAR);
717     const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
718     const WCHAR *lastjoker = NULL;
719     const WCHAR *next_to_retry = NULL;
720
721     TRACE("(%s, %s)\n", debugstr_us(name_str), debugstr_us(mask_str));
722
723     while (name < name_end && mask < mask_end)
724     {
725         switch(*mask)
726         {
727         case '*':
728             mask++;
729             while (mask < mask_end && *mask == '*') mask++;  /* Skip consecutive '*' */
730             if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
731             lastjoker = mask;
732
733             /* skip to the next match after the joker(s) */
734             if (is_case_sensitive)
735                 while (name < name_end && (*name != *mask)) name++;
736             else
737                 while (name < name_end && (toupperW(*name) != toupperW(*mask))) name++;
738             next_to_retry = name;
739             break;
740         case '?':
741             mask++;
742             name++;
743             break;
744         default:
745             if (is_case_sensitive) mismatch = (*mask != *name);
746             else mismatch = (toupperW(*mask) != toupperW(*name));
747
748             if (!mismatch)
749             {
750                 mask++;
751                 name++;
752                 if (mask == mask_end)
753                 {
754                     if (name == name_end) return TRUE;
755                     if (lastjoker) mask = lastjoker;
756                 }
757             }
758             else /* mismatch ! */
759             {
760                 if (lastjoker) /* we had an '*', so we can try unlimitedly */
761                 {
762                     mask = lastjoker;
763
764                     /* this scan sequence was a mismatch, so restart
765                      * 1 char after the first char we checked last time */
766                     next_to_retry++;
767                     name = next_to_retry;
768                 }
769                 else return FALSE; /* bad luck */
770             }
771             break;
772         }
773     }
774     while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
775         mask++;  /* Ignore trailing '.' or '*' in mask */
776     return (name == name_end && mask == mask_end);
777 }
778
779
780 /***********************************************************************
781  *           append_entry
782  *
783  * helper for NtQueryDirectoryFile
784  */
785 static FILE_BOTH_DIR_INFORMATION *append_entry( void *info_ptr, ULONG_PTR *pos, ULONG max_length,
786                                                 const char *long_name, const char *short_name,
787                                                 const UNICODE_STRING *mask )
788 {
789     FILE_BOTH_DIR_INFORMATION *info;
790     int i, long_len, short_len, total_len;
791     struct stat st;
792     WCHAR long_nameW[MAX_DIR_ENTRY_LEN];
793     WCHAR short_nameW[12];
794     UNICODE_STRING str;
795
796     long_len = ntdll_umbstowcs( 0, long_name, strlen(long_name), long_nameW, MAX_DIR_ENTRY_LEN );
797     if (long_len == -1) return NULL;
798
799     str.Buffer = long_nameW;
800     str.Length = long_len * sizeof(WCHAR);
801     str.MaximumLength = sizeof(long_nameW);
802
803     if (short_name)
804     {
805         short_len = ntdll_umbstowcs( 0, short_name, strlen(short_name),
806                                      short_nameW, sizeof(short_nameW) / sizeof(WCHAR) );
807         if (short_len == -1) short_len = sizeof(short_nameW) / sizeof(WCHAR);
808     }
809     else  /* generate a short name if necessary */
810     {
811         BOOLEAN spaces;
812
813         short_len = 0;
814         if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
815             short_len = hash_short_file_name( &str, short_nameW );
816     }
817
818     TRACE( "long %s short %s mask %s\n",
819            debugstr_us(&str), debugstr_wn(short_nameW, short_len), debugstr_us(mask) );
820
821     if (mask && !match_filename( &str, mask ))
822     {
823         if (!short_len) return NULL;  /* no short name to match */
824         str.Buffer = short_nameW;
825         str.Length = short_len * sizeof(WCHAR);
826         str.MaximumLength = sizeof(short_nameW);
827         if (!match_filename( &str, mask )) return NULL;
828     }
829
830     total_len = (sizeof(*info) - sizeof(info->FileName) + long_len*sizeof(WCHAR) + 3) & ~3;
831     info = (FILE_BOTH_DIR_INFORMATION *)((char *)info_ptr + *pos);
832
833     if (*pos + total_len > max_length) total_len = max_length - *pos;
834
835     info->FileAttributes = 0;
836     if (lstat( long_name, &st ) == -1) return NULL;
837     if (S_ISLNK( st.st_mode ))
838     {
839         if (stat( long_name, &st ) == -1) return NULL;
840         if (S_ISDIR( st.st_mode )) info->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
841     }
842
843     info->NextEntryOffset = total_len;
844     info->FileIndex = 0;  /* NTFS always has 0 here, so let's not bother with it */
845
846     RtlSecondsSince1970ToTime( st.st_mtime, &info->CreationTime );
847     RtlSecondsSince1970ToTime( st.st_mtime, &info->LastWriteTime );
848     RtlSecondsSince1970ToTime( st.st_atime, &info->LastAccessTime );
849     RtlSecondsSince1970ToTime( st.st_ctime, &info->ChangeTime );
850
851     if (S_ISDIR(st.st_mode))
852     {
853         info->EndOfFile.QuadPart = info->AllocationSize.QuadPart = 0;
854         info->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
855     }
856     else
857     {
858         info->EndOfFile.QuadPart = st.st_size;
859         info->AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
860         info->FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
861     }
862
863     if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
864         info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
865
866     if (!show_dot_files && long_name[0] == '.' && long_name[1] && (long_name[1] != '.' || long_name[2]))
867         info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
868
869     info->EaSize = 0; /* FIXME */
870     info->ShortNameLength = short_len * sizeof(WCHAR);
871     for (i = 0; i < short_len; i++) info->ShortName[i] = toupperW(short_nameW[i]);
872     info->FileNameLength = long_len * sizeof(WCHAR);
873     memcpy( info->FileName, long_nameW,
874             min( info->FileNameLength, total_len-sizeof(*info)+sizeof(info->FileName) ));
875
876     *pos += total_len;
877     return info;
878 }
879
880
881 /***********************************************************************
882  *           read_directory_vfat
883  *
884  * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
885  */
886 #ifdef VFAT_IOCTL_READDIR_BOTH
887 static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
888                                 BOOLEAN single_entry, const UNICODE_STRING *mask,
889                                 BOOLEAN restart_scan )
890
891 {
892     int res;
893     KERNEL_DIRENT de[2];
894     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
895     static const unsigned int max_dir_info_size = sizeof(*info) + (MAX_DIR_ENTRY_LEN-1) * sizeof(WCHAR);
896
897     io->u.Status = STATUS_SUCCESS;
898
899     if (restart_scan) lseek( fd, 0, SEEK_SET );
900
901     if (length < max_dir_info_size)  /* we may have to return a partial entry here */
902     {
903         off_t old_pos = lseek( fd, 0, SEEK_CUR );
904
905         /* Set d_reclen to 65535 to work around an AFS kernel bug */
906         de[0].d_reclen = 65535;
907         res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
908         if (res == -1 && errno != ENOENT) return -1;  /* VFAT ioctl probably not supported */
909         if (!res && de[0].d_reclen == 65535) return -1;  /* AFS bug */
910
911         while (res != -1)
912         {
913             if (!de[0].d_reclen) break;
914             if (de[1].d_name[0])
915                 info = append_entry( buffer, &io->Information, length,
916                                      de[1].d_name, de[0].d_name, mask );
917             else
918                 info = append_entry( buffer, &io->Information, length,
919                                      de[0].d_name, NULL, mask );
920             if (info)
921             {
922                 last_info = info;
923                 if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
924                 {
925                     io->u.Status = STATUS_BUFFER_OVERFLOW;
926                     lseek( fd, old_pos, SEEK_SET );  /* restore pos to previous entry */
927                 }
928                 break;
929             }
930             old_pos = lseek( fd, 0, SEEK_CUR );
931             res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
932         }
933     }
934     else  /* we'll only return full entries, no need to worry about overflow */
935     {
936         /* Set d_reclen to 65535 to work around an AFS kernel bug */
937         de[0].d_reclen = 65535;
938         res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
939         if (res == -1 && errno != ENOENT) return -1;  /* VFAT ioctl probably not supported */
940         if (!res && de[0].d_reclen == 65535) return -1;  /* AFS bug */
941
942         while (res != -1)
943         {
944             if (!de[0].d_reclen) break;
945             if (de[1].d_name[0])
946                 info = append_entry( buffer, &io->Information, length,
947                                      de[1].d_name, de[0].d_name, mask );
948             else
949                 info = append_entry( buffer, &io->Information, length,
950                                      de[0].d_name, NULL, mask );
951             if (info)
952             {
953                 last_info = info;
954                 if (single_entry) break;
955                 /* check if we still have enough space for the largest possible entry */
956                 if (io->Information + max_dir_info_size > length) break;
957             }
958             res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
959         }
960     }
961
962     if (last_info) last_info->NextEntryOffset = 0;
963     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
964     return 0;
965 }
966 #endif /* VFAT_IOCTL_READDIR_BOTH */
967
968
969 /***********************************************************************
970  *           read_directory_getdents
971  *
972  * Read a directory using the Linux getdents64 system call; helper for NtQueryDirectoryFile.
973  */
974 #ifdef USE_GETDENTS
975 static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
976                                     BOOLEAN single_entry, const UNICODE_STRING *mask,
977                                     BOOLEAN restart_scan )
978 {
979     off_t old_pos = 0;
980     size_t size = length;
981     int res;
982     char local_buffer[8192];
983     KERNEL_DIRENT64 *data, *de;
984     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
985     static const unsigned int max_dir_info_size = sizeof(*info) + (MAX_DIR_ENTRY_LEN-1) * sizeof(WCHAR);
986
987     if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
988     {
989         size = sizeof(local_buffer);
990         data = (KERNEL_DIRENT64 *)local_buffer;
991     }
992
993     if (restart_scan) lseek( fd, 0, SEEK_SET );
994     else if (length < max_dir_info_size)  /* we may have to return a partial entry here */
995     {
996         old_pos = lseek( fd, 0, SEEK_CUR );
997         if (old_pos == -1 && errno == ENOENT)
998         {
999             io->u.Status = STATUS_NO_MORE_FILES;
1000             res = 0;
1001             goto done;
1002         }
1003     }
1004
1005     io->u.Status = STATUS_SUCCESS;
1006
1007     res = getdents64( fd, data, size );
1008     if (res == -1)
1009     {
1010         if (errno != ENOSYS)
1011         {
1012             io->u.Status = FILE_GetNtStatus();
1013             res = 0;
1014         }
1015         goto done;
1016     }
1017
1018     de = data;
1019
1020     while (res > 0)
1021     {
1022         res -= de->d_reclen;
1023         info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask );
1024         if (info)
1025         {
1026             last_info = info;
1027             if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
1028             {
1029                 io->u.Status = STATUS_BUFFER_OVERFLOW;
1030                 lseek( fd, old_pos, SEEK_SET );  /* restore pos to previous entry */
1031                 break;
1032             }
1033             /* check if we still have enough space for the largest possible entry */
1034             if (single_entry || io->Information + max_dir_info_size > length)
1035             {
1036                 if (res > 0) lseek( fd, de->d_off, SEEK_SET );  /* set pos to next entry */
1037                 break;
1038             }
1039         }
1040         old_pos = de->d_off;
1041         /* move on to the next entry */
1042         if (res > 0) de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen);
1043         else
1044         {
1045             res = getdents64( fd, data, size );
1046             de = data;
1047         }
1048     }
1049
1050     if (last_info) last_info->NextEntryOffset = 0;
1051     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1052     res = 0;
1053 done:
1054     if ((char *)data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1055     return res;
1056 }
1057 #endif  /* USE_GETDENTS */
1058
1059
1060 /***********************************************************************
1061  *           read_directory_readdir
1062  *
1063  * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
1064  */
1065 static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1066                                     BOOLEAN single_entry, const UNICODE_STRING *mask,
1067                                     BOOLEAN restart_scan )
1068 {
1069     DIR *dir;
1070     off_t i, old_pos = 0;
1071     struct dirent *de;
1072     FILE_BOTH_DIR_INFORMATION *info, *last_info = NULL;
1073     static const unsigned int max_dir_info_size = sizeof(*info) + (MAX_DIR_ENTRY_LEN-1) * sizeof(WCHAR);
1074
1075     if (!(dir = opendir( "." )))
1076     {
1077         io->u.Status = FILE_GetNtStatus();
1078         return;
1079     }
1080
1081     if (!restart_scan)
1082     {
1083         old_pos = lseek( fd, 0, SEEK_CUR );
1084         /* skip the right number of entries */
1085         for (i = 0; i < old_pos; i++)
1086         {
1087             if (!readdir( dir ))
1088             {
1089                 closedir( dir );
1090                 io->u.Status = STATUS_NO_MORE_FILES;
1091                 return;
1092             }
1093         }
1094     }
1095     io->u.Status = STATUS_SUCCESS;
1096
1097     while ((de = readdir( dir )))
1098     {
1099         old_pos++;
1100         info = append_entry( buffer, &io->Information, length, de->d_name, NULL, mask );
1101         if (info)
1102         {
1103             last_info = info;
1104             if ((char *)info->FileName + info->FileNameLength > (char *)buffer + length)
1105             {
1106                 io->u.Status = STATUS_BUFFER_OVERFLOW;
1107                 old_pos--;  /* restore pos to previous entry */
1108                 break;
1109             }
1110             if (single_entry) break;
1111             /* check if we still have enough space for the largest possible entry */
1112             if (io->Information + max_dir_info_size > length) break;
1113         }
1114     }
1115
1116     lseek( fd, old_pos, SEEK_SET );  /* store dir offset as filepos for fd */
1117     closedir( dir );
1118
1119     if (last_info) last_info->NextEntryOffset = 0;
1120     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1121 }
1122
1123
1124 /******************************************************************************
1125  *  NtQueryDirectoryFile        [NTDLL.@]
1126  *  ZwQueryDirectoryFile        [NTDLL.@]
1127  */
1128 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
1129                                       PIO_APC_ROUTINE apc_routine, PVOID apc_context,
1130                                       PIO_STATUS_BLOCK io,
1131                                       PVOID buffer, ULONG length,
1132                                       FILE_INFORMATION_CLASS info_class,
1133                                       BOOLEAN single_entry,
1134                                       PUNICODE_STRING mask,
1135                                       BOOLEAN restart_scan )
1136 {
1137     int cwd, fd;
1138
1139     TRACE("(%p %p %p %p %p %p 0x%08lx 0x%08x 0x%08x %s 0x%08x\n",
1140           handle, event, apc_routine, apc_context, io, buffer,
1141           length, info_class, single_entry, debugstr_us(mask),
1142           restart_scan);
1143
1144     if (length < sizeof(FILE_BOTH_DIR_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
1145
1146     if (event || apc_routine)
1147     {
1148         FIXME( "Unsupported yet option\n" );
1149         return io->u.Status = STATUS_NOT_IMPLEMENTED;
1150     }
1151     if (info_class != FileBothDirectoryInformation)
1152     {
1153         FIXME( "Unsupported file info class %d\n", info_class );
1154         return io->u.Status = STATUS_NOT_IMPLEMENTED;
1155     }
1156
1157     if ((io->u.Status = wine_server_handle_to_fd( handle, FILE_LIST_DIRECTORY, &fd, NULL )) != STATUS_SUCCESS)
1158         return io->u.Status;
1159
1160     io->Information = 0;
1161
1162     RtlEnterCriticalSection( &dir_section );
1163
1164     if (show_dot_files == -1) init_options();
1165
1166     if ((cwd = open(".", O_RDONLY)) != -1 && fchdir( fd ) != -1)
1167     {
1168 #ifdef VFAT_IOCTL_READDIR_BOTH
1169         if ((read_directory_vfat( fd, io, buffer, length, single_entry, mask, restart_scan )) == -1)
1170 #endif
1171 #ifdef USE_GETDENTS
1172             if ((read_directory_getdents( fd, io, buffer, length, single_entry, mask, restart_scan )) == -1)
1173 #endif
1174                 read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan );
1175
1176         if (fchdir( cwd ) == -1) chdir( "/" );
1177     }
1178     else io->u.Status = FILE_GetNtStatus();
1179
1180     RtlLeaveCriticalSection( &dir_section );
1181
1182     wine_server_release_fd( handle, fd );
1183     if (cwd != -1) close( cwd );
1184     TRACE( "=> %lx (%ld)\n", io->u.Status, io->Information );
1185     return io->u.Status;
1186 }
1187
1188
1189 /***********************************************************************
1190  *           find_file_in_dir
1191  *
1192  * Find a file in a directory the hard way, by doing a case-insensitive search.
1193  * The file found is appended to unix_name at pos.
1194  * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
1195  */
1196 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
1197                                   int check_case )
1198 {
1199     WCHAR buffer[MAX_DIR_ENTRY_LEN];
1200     UNICODE_STRING str;
1201     BOOLEAN spaces;
1202     DIR *dir;
1203     struct dirent *de;
1204     struct stat st;
1205     int ret, used_default, is_name_8_dot_3;
1206
1207     /* try a shortcut for this directory */
1208
1209     unix_name[pos++] = '/';
1210     ret = ntdll_wcstoumbs( 0, name, length, unix_name + pos, MAX_DIR_ENTRY_LEN,
1211                            NULL, &used_default );
1212     /* if we used the default char, the Unix name won't round trip properly back to Unicode */
1213     /* so it cannot match the file we are looking for */
1214     if (ret >= 0 && !used_default)
1215     {
1216         unix_name[pos + ret] = 0;
1217         if (!stat( unix_name, &st )) return STATUS_SUCCESS;
1218     }
1219     if (check_case) goto not_found;  /* we want an exact match */
1220
1221     if (pos > 1) unix_name[pos - 1] = 0;
1222     else unix_name[1] = 0;  /* keep the initial slash */
1223
1224     /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
1225
1226     str.Buffer = (WCHAR *)name;
1227     str.Length = length * sizeof(WCHAR);
1228     str.MaximumLength = str.Length;
1229     is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
1230
1231     /* now look for it through the directory */
1232
1233 #ifdef VFAT_IOCTL_READDIR_BOTH
1234     if (is_name_8_dot_3)
1235     {
1236         int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
1237         if (fd != -1)
1238         {
1239             KERNEL_DIRENT de[2];
1240
1241             /* Set d_reclen to 65535 to work around an AFS kernel bug */
1242             de[0].d_reclen = 65535;
1243             if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1 &&
1244                 de[0].d_reclen != 65535)
1245             {
1246                 unix_name[pos - 1] = '/';
1247                 for (;;)
1248                 {
1249                     if (!de[0].d_reclen) break;
1250
1251                     if (de[1].d_name[0])
1252                     {
1253                         ret = ntdll_umbstowcs( 0, de[1].d_name, strlen(de[1].d_name),
1254                                                buffer, MAX_DIR_ENTRY_LEN );
1255                         if (ret == length && !memicmpW( buffer, name, length))
1256                         {
1257                             strcpy( unix_name + pos, de[1].d_name );
1258                             close( fd );
1259                             return STATUS_SUCCESS;
1260                         }
1261                     }
1262                     ret = ntdll_umbstowcs( 0, de[0].d_name, strlen(de[0].d_name),
1263                                            buffer, MAX_DIR_ENTRY_LEN );
1264                     if (ret == length && !memicmpW( buffer, name, length))
1265                     {
1266                         strcpy( unix_name + pos,
1267                                 de[1].d_name[0] ? de[1].d_name : de[0].d_name );
1268                         close( fd );
1269                         return STATUS_SUCCESS;
1270                     }
1271                     if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
1272                     {
1273                         close( fd );
1274                         goto not_found;
1275                     }
1276                 }
1277             }
1278             close( fd );
1279         }
1280         /* fall through to normal handling */
1281     }
1282 #endif /* VFAT_IOCTL_READDIR_BOTH */
1283
1284     if (!(dir = opendir( unix_name )))
1285     {
1286         if (errno == ENOENT) return STATUS_OBJECT_PATH_NOT_FOUND;
1287         else return FILE_GetNtStatus();
1288     }
1289     unix_name[pos - 1] = '/';
1290     str.Buffer = buffer;
1291     str.MaximumLength = sizeof(buffer);
1292     while ((de = readdir( dir )))
1293     {
1294         ret = ntdll_umbstowcs( 0, de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
1295         if (ret == length && !memicmpW( buffer, name, length ))
1296         {
1297             strcpy( unix_name + pos, de->d_name );
1298             closedir( dir );
1299             return STATUS_SUCCESS;
1300         }
1301
1302         if (!is_name_8_dot_3) continue;
1303
1304         str.Length = ret * sizeof(WCHAR);
1305         if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
1306         {
1307             WCHAR short_nameW[12];
1308             ret = hash_short_file_name( &str, short_nameW );
1309             if (ret == length && !memicmpW( short_nameW, name, length ))
1310             {
1311                 strcpy( unix_name + pos, de->d_name );
1312                 closedir( dir );
1313                 return STATUS_SUCCESS;
1314             }
1315         }
1316     }
1317     closedir( dir );
1318     goto not_found;  /* avoid warning */
1319
1320 not_found:
1321     unix_name[pos - 1] = 0;
1322     return STATUS_OBJECT_PATH_NOT_FOUND;
1323 }
1324
1325
1326 /******************************************************************************
1327  *           get_dos_device
1328  *
1329  * Get the Unix path of a DOS device.
1330  */
1331 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *unix_name_ret )
1332 {
1333     const char *config_dir = wine_get_config_dir();
1334     struct stat st;
1335     char *unix_name, *new_name, *dev;
1336     unsigned int i;
1337     int unix_len;
1338
1339     /* make sure the device name is ASCII */
1340     for (i = 0; i < name_len; i++)
1341         if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
1342
1343     unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
1344
1345     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
1346         return STATUS_NO_MEMORY;
1347
1348     strcpy( unix_name, config_dir );
1349     strcat( unix_name, "/dosdevices/" );
1350     dev = unix_name + strlen(unix_name);
1351
1352     for (i = 0; i < name_len; i++) dev[i] = (char)tolowerW(name[i]);
1353     dev[i] = 0;
1354
1355     /* special case for drive devices */
1356     if (name_len == 2 && dev[1] == ':')
1357     {
1358         dev[i++] = ':';
1359         dev[i] = 0;
1360     }
1361
1362     for (;;)
1363     {
1364         if (!stat( unix_name, &st ))
1365         {
1366             TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
1367             unix_name_ret->Buffer = unix_name;
1368             unix_name_ret->Length = strlen(unix_name);
1369             unix_name_ret->MaximumLength = unix_len;
1370             return STATUS_SUCCESS;
1371         }
1372         if (!dev) break;
1373
1374         /* now try some defaults for it */
1375         if (!strcmp( dev, "aux" ))
1376         {
1377             strcpy( dev, "com1" );
1378             continue;
1379         }
1380         if (!strcmp( dev, "prn" ))
1381         {
1382             strcpy( dev, "lpt1" );
1383             continue;
1384         }
1385         if (!strcmp( dev, "nul" ))
1386         {
1387             strcpy( unix_name, "/dev/null" );
1388             dev = NULL; /* last try */
1389             continue;
1390         }
1391
1392         new_name = NULL;
1393         if (dev[1] == ':' && dev[2] == ':')  /* drive device */
1394         {
1395             dev[2] = 0;  /* remove last ':' to get the drive mount point symlink */
1396             new_name = get_default_drive_device( unix_name );
1397         }
1398         else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( dev[3] - '0' );
1399         else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( dev[3] - '0' );
1400
1401         if (!new_name) break;
1402
1403         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1404         unix_name = new_name;
1405         unix_len = strlen(unix_name) + 1;
1406         dev = NULL; /* last try */
1407     }
1408     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1409     return STATUS_BAD_DEVICE_TYPE;
1410 }
1411
1412
1413 /* return the length of the DOS namespace prefix if any */
1414 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
1415 {
1416     static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
1417     static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
1418
1419     if (name->Length > sizeof(nt_prefixW) &&
1420         !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
1421         return sizeof(nt_prefixW) / sizeof(WCHAR);
1422
1423     if (name->Length > sizeof(dosdev_prefixW) &&
1424         !memicmpW( name->Buffer, dosdev_prefixW, sizeof(dosdev_prefixW)/sizeof(WCHAR) ))
1425         return sizeof(dosdev_prefixW) / sizeof(WCHAR);
1426
1427     return 0;
1428 }
1429
1430
1431 /******************************************************************************
1432  *           wine_nt_to_unix_file_name  (NTDLL.@) Not a Windows API
1433  *
1434  * Convert a file name from NT namespace to Unix namespace.
1435  *
1436  * If disposition is not FILE_OPEN or FILE_OVERWRITTE, the last path
1437  * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
1438  * returned, but the unix name is still filled in properly.
1439  */
1440 NTSTATUS wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
1441                                     UINT disposition, BOOLEAN check_case )
1442 {
1443     static const WCHAR uncW[] = {'U','N','C','\\'};
1444     static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
1445
1446     NTSTATUS status = STATUS_SUCCESS;
1447     const char *config_dir = wine_get_config_dir();
1448     const WCHAR *name, *p;
1449     struct stat st;
1450     char *unix_name;
1451     int pos, ret, name_len, unix_len, used_default;
1452
1453     name     = nameW->Buffer;
1454     name_len = nameW->Length / sizeof(WCHAR);
1455
1456     if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
1457
1458     if ((pos = get_dos_prefix_len( nameW )))
1459     {
1460         BOOLEAN is_unc = FALSE;
1461
1462         name += pos;
1463         name_len -= pos;
1464
1465         /* check for UNC prefix */
1466         if (name_len > 4 && !memicmpW( name, uncW, 4 ))
1467         {
1468             name += 3;
1469             name_len -= 3;
1470             is_unc = TRUE;
1471         }
1472         else
1473         {
1474             /* check for a drive letter with path */
1475             if (name_len < 3 || !isalphaW(name[0]) || name[1] != ':' || !IS_SEPARATOR(name[2]))
1476             {
1477                 /* not a drive with path, try other DOS devices */
1478                 return get_dos_device( name, name_len, unix_name_ret );
1479             }
1480             name += 2;  /* skip drive letter */
1481             name_len -= 2;
1482         }
1483
1484         /* check for invalid characters */
1485         for (p = name; p < name + name_len; p++)
1486             if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
1487
1488         unix_len = ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
1489         unix_len += MAX_DIR_ENTRY_LEN + 3;
1490         unix_len += strlen(config_dir) + sizeof("/dosdevices/") + 3;
1491         if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
1492             return STATUS_NO_MEMORY;
1493         strcpy( unix_name, config_dir );
1494         strcat( unix_name, "/dosdevices/" );
1495         pos = strlen(unix_name);
1496         if (is_unc)
1497         {
1498             strcpy( unix_name + pos, "unc" );
1499             pos += 3;
1500         }
1501         else
1502         {
1503             unix_name[pos++] = tolowerW( name[-2] );
1504             unix_name[pos++] = ':';
1505             unix_name[pos] = 0;
1506         }
1507     }
1508     else  /* no DOS prefix, assume NT native name, map directly to Unix */
1509     {
1510         if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_NAME_INVALID;
1511         unix_len = ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
1512         unix_len += MAX_DIR_ENTRY_LEN + 3;
1513         if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
1514             return STATUS_NO_MEMORY;
1515         pos = 0;
1516     }
1517
1518     /* try a shortcut first */
1519
1520     ret = ntdll_wcstoumbs( 0, name, name_len, unix_name + pos, unix_len - pos - 1,
1521                            NULL, &used_default );
1522
1523     while (name_len && IS_SEPARATOR(*name))
1524     {
1525         name++;
1526         name_len--;
1527     }
1528
1529     if (ret > 0 && !used_default)  /* if we used the default char the name didn't convert properly */
1530     {
1531         char *p;
1532         unix_name[pos + ret] = 0;
1533         for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
1534         if (!stat( unix_name, &st ))
1535         {
1536             /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */
1537             if (disposition == FILE_CREATE)
1538                 return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED;
1539             goto done;
1540         }
1541     }
1542
1543     if (!name_len)  /* empty name -> drive root doesn't exist */
1544     {
1545         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1546         return STATUS_OBJECT_PATH_NOT_FOUND;
1547     }
1548     if (check_case && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
1549     {
1550         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1551         return STATUS_OBJECT_NAME_NOT_FOUND;
1552     }
1553
1554     /* now do it component by component */
1555
1556     while (name_len)
1557     {
1558         const WCHAR *end, *next;
1559
1560         end = name;
1561         while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
1562         next = end;
1563         while (next < name + name_len && IS_SEPARATOR(*next)) next++;
1564         name_len -= next - name;
1565
1566         /* grow the buffer if needed */
1567
1568         if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
1569         {
1570             char *new_name;
1571             unix_len += 2 * MAX_DIR_ENTRY_LEN;
1572             if (!(new_name = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name, unix_len )))
1573             {
1574                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1575                 return STATUS_NO_MEMORY;
1576             }
1577             unix_name = new_name;
1578         }
1579
1580         status = find_file_in_dir( unix_name, pos, name, end - name, check_case );
1581
1582         /* if this is the last element, not finding it is not necessarily fatal */
1583         if (!name_len)
1584         {
1585             if (status == STATUS_OBJECT_PATH_NOT_FOUND)
1586             {
1587                 status = STATUS_OBJECT_NAME_NOT_FOUND;
1588                 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
1589                 {
1590                     ret = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
1591                                            MAX_DIR_ENTRY_LEN, NULL, &used_default );
1592                     if (ret > 0 && !used_default)
1593                     {
1594                         unix_name[pos] = '/';
1595                         unix_name[pos + 1 + ret] = 0;
1596                         status = STATUS_NO_SUCH_FILE;
1597                         break;
1598                     }
1599                 }
1600             }
1601             else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
1602             {
1603                 status = STATUS_OBJECT_NAME_COLLISION;
1604             }
1605         }
1606
1607         if (status != STATUS_SUCCESS)
1608         {
1609             /* couldn't find it at all, fail */
1610             WARN( "%s not found in %s\n", debugstr_w(name), unix_name );
1611             RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1612             return status;
1613         }
1614
1615         pos += strlen( unix_name + pos );
1616         name = next;
1617     }
1618
1619     WARN( "%s -> %s required a case-insensitive search\n",
1620           debugstr_us(nameW), debugstr_a(unix_name) );
1621
1622 done:
1623     TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
1624     unix_name_ret->Buffer = unix_name;
1625     unix_name_ret->Length = strlen(unix_name);
1626     unix_name_ret->MaximumLength = unix_len;
1627     return status;
1628 }
1629
1630
1631 /******************************************************************
1632  *              RtlDoesFileExists_U   (NTDLL.@)
1633  */
1634 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
1635 {
1636     UNICODE_STRING nt_name;
1637     ANSI_STRING unix_name;
1638     BOOLEAN ret;
1639
1640     if (!RtlDosPathNameToNtPathName_U( file_name, &nt_name, NULL, NULL )) return FALSE;
1641     ret = (wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE ) == STATUS_SUCCESS);
1642     if (ret) RtlFreeAnsiString( &unix_name );
1643     RtlFreeUnicodeString( &nt_name );
1644     return ret;
1645 }
1646
1647
1648 /***********************************************************************
1649  *           DIR_unmount_device
1650  *
1651  * Unmount the specified device.
1652  */
1653 NTSTATUS DIR_unmount_device( HANDLE handle )
1654 {
1655     NTSTATUS status;
1656     int unix_fd;
1657
1658     SERVER_START_REQ( unmount_device )
1659     {
1660         req->handle = handle;
1661         status = wine_server_call( req );
1662     }
1663     SERVER_END_REQ;
1664     if (status) return status;
1665
1666     if (!(status = wine_server_handle_to_fd( handle, 0, &unix_fd, NULL )))
1667     {
1668         struct stat st;
1669         char *mount_point = NULL;
1670
1671         if (fstat( unix_fd, &st ) == -1 || !S_ISBLK(st.st_mode))
1672             status = STATUS_INVALID_PARAMETER;
1673         else
1674         {
1675             if ((mount_point = get_device_mount_point( st.st_rdev )))
1676             {
1677                 static const char umount[] = "umount >/dev/null 2>&1 ";
1678                 char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount));
1679                 if (cmd)
1680                 {
1681                     strcpy( cmd, umount );
1682                     strcat( cmd, mount_point );
1683                     system( cmd );
1684                     RtlFreeHeap( GetProcessHeap(), 0, cmd );
1685 #ifdef linux
1686                     /* umount will fail to release the loop device since we still have
1687                        a handle to it, so we release it here */
1688                     if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
1689 #endif
1690                 }
1691                 RtlFreeHeap( GetProcessHeap(), 0, mount_point );
1692             }
1693         }
1694         wine_server_release_fd( handle, unix_fd );
1695     }
1696     return status;
1697 }
1698
1699
1700 /******************************************************************************
1701  *           DIR_get_unix_cwd
1702  *
1703  * Retrieve the Unix name of the current directory; helper for wine_unix_to_nt_file_name.
1704  * Returned value must be freed by caller.
1705  */
1706 NTSTATUS DIR_get_unix_cwd( char **cwd )
1707 {
1708     int old_cwd, unix_fd;
1709     CURDIR *curdir;
1710     HANDLE handle;
1711     NTSTATUS status;
1712
1713     RtlAcquirePebLock();
1714
1715     if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
1716         curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
1717     else
1718         curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
1719
1720     if (!(handle = curdir->Handle))
1721     {
1722         UNICODE_STRING dirW;
1723         OBJECT_ATTRIBUTES attr;
1724         IO_STATUS_BLOCK io;
1725
1726         if (!RtlDosPathNameToNtPathName_U( curdir->DosPath.Buffer, &dirW, NULL, NULL ))
1727         {
1728             status = STATUS_OBJECT_NAME_INVALID;
1729             goto done;
1730         }
1731         attr.Length = sizeof(attr);
1732         attr.RootDirectory = 0;
1733         attr.Attributes = OBJ_CASE_INSENSITIVE;
1734         attr.ObjectName = &dirW;
1735         attr.SecurityDescriptor = NULL;
1736         attr.SecurityQualityOfService = NULL;
1737
1738         status = NtOpenFile( &handle, 0, &attr, &io, 0,
1739                              FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
1740         RtlFreeUnicodeString( &dirW );
1741         if (status != STATUS_SUCCESS) goto done;
1742     }
1743
1744     if ((status = wine_server_handle_to_fd( handle, 0, &unix_fd, NULL )) == STATUS_SUCCESS)
1745     {
1746         RtlEnterCriticalSection( &dir_section );
1747
1748         if ((old_cwd = open(".", O_RDONLY)) != -1 && fchdir( unix_fd ) != -1)
1749         {
1750             unsigned int size = 512;
1751
1752             for (;;)
1753             {
1754                 if (!(*cwd = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1755                 {
1756                     status = STATUS_NO_MEMORY;
1757                     break;
1758                 }
1759                 if (getcwd( *cwd, size )) break;
1760                 RtlFreeHeap( GetProcessHeap(), 0, *cwd );
1761                 if (errno != ERANGE)
1762                 {
1763                     status = STATUS_OBJECT_PATH_INVALID;
1764                     break;
1765                 }
1766                 size *= 2;
1767             }
1768             if (fchdir( old_cwd ) == -1) chdir( "/" );
1769         }
1770         else status = FILE_GetNtStatus();
1771
1772         RtlLeaveCriticalSection( &dir_section );
1773         wine_server_release_fd( handle, unix_fd );
1774     }
1775     if (!curdir->Handle) NtClose( handle );
1776
1777 done:
1778     RtlReleasePebLock();
1779     return status;
1780 }
1781
1782 struct read_changes_info
1783 {
1784     HANDLE FileHandle;
1785     HANDLE Event;
1786     PIO_APC_ROUTINE ApcRoutine;
1787     PVOID ApcContext;
1788     PVOID Buffer;
1789     ULONG BufferSize;
1790 };
1791
1792 static void WINAPI read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, ULONG status )
1793 {
1794     struct read_changes_info *info = user;
1795     char path[PATH_MAX];
1796     NTSTATUS ret = STATUS_SUCCESS;
1797     int len, action, i;
1798
1799     TRACE("%p %p %p %08lx\n", info, info->ApcContext, iosb, status);
1800
1801     /*
1802      * FIXME: race me!
1803      *
1804      * hEvent/hDir is set before the output buffer and iosb is updated.
1805      * Since the thread that called NtNotifyChangeDirectoryFile is usually
1806      * waiting, we'll be safe since we're called in that thread's context.
1807      * If a different thread is waiting on our hEvent/hDir we're going to be
1808      * in trouble...
1809      */
1810     SERVER_START_REQ( read_change )
1811     {
1812         req->handle = info->FileHandle;
1813         wine_server_set_reply( req, path, PATH_MAX );
1814         ret = wine_server_call( req );
1815         action = reply->action;
1816         len = wine_server_reply_size( reply );
1817     }
1818     SERVER_END_REQ;
1819
1820     if (ret == STATUS_SUCCESS && info->Buffer && 
1821         (info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
1822     {
1823         PFILE_NOTIFY_INFORMATION pfni;
1824
1825         pfni = (PFILE_NOTIFY_INFORMATION) info->Buffer;
1826
1827         /* convert to an NT style path */
1828         for (i=0; i<len; i++)
1829             if (path[i] == '/')
1830                 path[i] = '\\';
1831
1832         len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
1833                                info->BufferSize - sizeof (*pfni) );
1834
1835         pfni->NextEntryOffset = 0;
1836         pfni->Action = action;
1837         pfni->FileNameLength = len * sizeof (WCHAR);
1838         pfni->FileName[len] = 0;
1839
1840         TRACE("action = %ld name = %s\n", pfni->Action,
1841               debugstr_w(pfni->FileName) );
1842         len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
1843     }
1844     else
1845     {
1846         ret = STATUS_NOTIFY_ENUM_DIR;
1847         len = 0;
1848     }
1849
1850     iosb->u.Status = ret;
1851     iosb->Information = len;
1852
1853     RtlFreeHeap( GetProcessHeap(), 0, info );
1854 }
1855
1856 #define FILE_NOTIFY_ALL        (  \
1857  FILE_NOTIFY_CHANGE_FILE_NAME   | \
1858  FILE_NOTIFY_CHANGE_DIR_NAME    | \
1859  FILE_NOTIFY_CHANGE_ATTRIBUTES  | \
1860  FILE_NOTIFY_CHANGE_SIZE        | \
1861  FILE_NOTIFY_CHANGE_LAST_WRITE  | \
1862  FILE_NOTIFY_CHANGE_LAST_ACCESS | \
1863  FILE_NOTIFY_CHANGE_CREATION    | \
1864  FILE_NOTIFY_CHANGE_SECURITY   )
1865
1866 /******************************************************************************
1867  *  NtNotifyChangeDirectoryFile [NTDLL.@]
1868  */
1869 NTSTATUS WINAPI
1870 NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
1871         PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
1872         PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
1873         ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
1874 {
1875     struct read_changes_info *info;
1876     NTSTATUS status;
1877
1878     TRACE("%p %p %p %p %p %p %lu %lu %d\n",
1879           FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
1880           Buffer, BufferSize, CompletionFilter, WatchTree );
1881
1882     if (!IoStatusBlock)
1883         return STATUS_ACCESS_VIOLATION;
1884
1885     if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
1886         return STATUS_INVALID_PARAMETER;
1887
1888     if (ApcRoutine)
1889         FIXME("parameters ignored %p %p\n", ApcRoutine, ApcContext );
1890
1891     info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof *info );
1892     if (!info)
1893         return STATUS_NO_MEMORY;
1894
1895     info->FileHandle = FileHandle;
1896     info->Event      = Event;
1897     info->Buffer     = Buffer;
1898     info->BufferSize = BufferSize;
1899     info->ApcRoutine = ApcRoutine;
1900     info->ApcContext = ApcContext;
1901
1902     SERVER_START_REQ( read_directory_changes )
1903     {
1904         req->handle     = FileHandle;
1905         req->event      = Event;
1906         req->filter     = CompletionFilter;
1907         req->want_data  = (Buffer != NULL);
1908         req->subtree    = WatchTree;
1909         req->io_apc     = read_changes_apc;
1910         req->io_sb      = IoStatusBlock;
1911         req->io_user    = info;
1912         status = wine_server_call( req );
1913     }
1914     SERVER_END_REQ;
1915
1916     if (status != STATUS_PENDING)
1917         RtlFreeHeap( GetProcessHeap(), 0, info );
1918
1919     return status;
1920 }