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