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