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