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