msvcrt: NULL terminate program arguments list in __getmainargs.
[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 static inline int dir_reclen(struct dirent *de)
1713 {
1714 #ifdef HAVE_STRUCT_DIRENT_D_RECLEN
1715     return de->d_reclen;
1716 #else
1717     return _DIRENT_RECLEN(de->d_namlen);
1718 #endif
1719 }
1720
1721 /***********************************************************************
1722  *           read_directory_getdirentries
1723  *
1724  * Read a directory using the BSD getdirentries system call; helper for NtQueryDirectoryFile.
1725  */
1726 static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1727                                          BOOLEAN single_entry, const UNICODE_STRING *mask,
1728                                          BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1729 {
1730     long restart_pos;
1731     ULONG_PTR restart_info_pos = 0;
1732     size_t size, initial_size = length;
1733     int res, fake_dot_dot = 1;
1734     char *data, local_buffer[8192];
1735     struct dirent *de;
1736     union file_directory_info *info, *last_info = NULL, *restart_last_info = NULL;
1737
1738     size = initial_size;
1739     data = local_buffer;
1740     if (size > sizeof(local_buffer) && !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1741     {
1742         io->u.Status = STATUS_NO_MEMORY;
1743         return io->u.Status;
1744     }
1745
1746     if (restart_scan) lseek( fd, 0, SEEK_SET );
1747
1748     io->u.Status = STATUS_SUCCESS;
1749
1750     /* FIXME: should make sure size is larger than filesystem block size */
1751     res = wine_getdirentries( fd, data, size, &restart_pos );
1752     if (res == -1)
1753     {
1754         io->u.Status = FILE_GetNtStatus();
1755         res = 0;
1756         goto done;
1757     }
1758
1759     de = (struct dirent *)data;
1760
1761     if (restart_scan)
1762     {
1763         /* check if we got . and .. from getdirentries */
1764         if (res > 0)
1765         {
1766             if (!strcmp( de->d_name, "." ) && res > dir_reclen(de))
1767             {
1768                 struct dirent *next_de = (struct dirent *)(data + dir_reclen(de));
1769                 if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
1770             }
1771         }
1772         /* make sure we have enough room for both entries */
1773         if (fake_dot_dot)
1774         {
1775             const ULONG min_info_size = dir_info_size( class, 1 ) + dir_info_size( class, 2 );
1776             if (length < min_info_size || single_entry)
1777             {
1778                 FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
1779                 fake_dot_dot = 0;
1780             }
1781         }
1782
1783         if (fake_dot_dot)
1784         {
1785             if ((info = append_entry( buffer, io, length, ".", NULL, mask, class )))
1786                 last_info = info;
1787             if ((info = append_entry( buffer, io, length, "..", NULL, mask, class )))
1788                 last_info = info;
1789
1790             restart_last_info = last_info;
1791             restart_info_pos = io->Information;
1792
1793             /* check if we still have enough space for the largest possible entry */
1794             if (last_info && io->Information + max_dir_info_size(class) > length)
1795             {
1796                 lseek( fd, 0, SEEK_SET );  /* reset pos to first entry */
1797                 res = 0;
1798             }
1799         }
1800     }
1801
1802     while (res > 0)
1803     {
1804         res -= dir_reclen(de);
1805         if (de->d_fileno &&
1806             !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
1807             ((info = append_entry( buffer, io, length, de->d_name, NULL, mask, class ))))
1808         {
1809             last_info = info;
1810             if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1811             {
1812                 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1813                 if (restart_info_pos)  /* if we have a complete read already, return it */
1814                 {
1815                     io->u.Status = STATUS_SUCCESS;
1816                     io->Information = restart_info_pos;
1817                     last_info = restart_last_info;
1818                     break;
1819                 }
1820                 /* otherwise restart from the start with a smaller size */
1821                 size = (char *)de - data;
1822                 if (!size) break;
1823                 io->Information = 0;
1824                 last_info = NULL;
1825                 goto restart;
1826             }
1827             /* if we have to return but the buffer contains more data, restart with a smaller size */
1828             if (res > 0 && (single_entry || io->Information + max_dir_info_size(class) > length))
1829             {
1830                 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1831                 size = (char *)de + dir_reclen(de) - data;
1832                 io->Information = restart_info_pos;
1833                 last_info = restart_last_info;
1834                 goto restart;
1835             }
1836         }
1837         /* move on to the next entry */
1838         if (res > 0)
1839         {
1840             de = (struct dirent *)((char *)de + dir_reclen(de));
1841             continue;
1842         }
1843         if (size < initial_size) break;  /* already restarted once, give up now */
1844         size = min( size, length - io->Information );
1845         /* if size is too small don't bother to continue */
1846         if (size < max_dir_info_size(class) && last_info) break;
1847         restart_last_info = last_info;
1848         restart_info_pos = io->Information;
1849     restart:
1850         res = wine_getdirentries( fd, data, size, &restart_pos );
1851         de = (struct dirent *)data;
1852     }
1853
1854     if (last_info) last_info->next = 0;
1855     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1856     res = 0;
1857 done:
1858     if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1859     return res;
1860 }
1861
1862 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1863 #undef getdirentries
1864 #undef dirent
1865 #endif
1866
1867 #endif  /* HAVE_GETDIRENTRIES */
1868
1869
1870 /***********************************************************************
1871  *           read_directory_readdir
1872  *
1873  * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
1874  */
1875 static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1876                                     BOOLEAN single_entry, const UNICODE_STRING *mask,
1877                                     BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1878 {
1879     DIR *dir;
1880     off_t i, old_pos = 0;
1881     struct dirent *de;
1882     union file_directory_info *info, *last_info = NULL;
1883
1884     if (!(dir = opendir( "." )))
1885     {
1886         io->u.Status = FILE_GetNtStatus();
1887         return;
1888     }
1889
1890     if (!restart_scan)
1891     {
1892         old_pos = lseek( fd, 0, SEEK_CUR );
1893         /* skip the right number of entries */
1894         for (i = 0; i < old_pos - 2; i++)
1895         {
1896             if (!readdir( dir ))
1897             {
1898                 closedir( dir );
1899                 io->u.Status = STATUS_NO_MORE_FILES;
1900                 return;
1901             }
1902         }
1903     }
1904     io->u.Status = STATUS_SUCCESS;
1905
1906     for (;;)
1907     {
1908         if (old_pos == 0)
1909             info = append_entry( buffer, io, length, ".", NULL, mask, class );
1910         else if (old_pos == 1)
1911             info = append_entry( buffer, io, length, "..", NULL, mask, class );
1912         else if ((de = readdir( dir )))
1913         {
1914             if (strcmp( de->d_name, "." ) && strcmp( de->d_name, ".." ))
1915                 info = append_entry( buffer, io, length, de->d_name, NULL, mask, class );
1916             else
1917                 info = NULL;
1918         }
1919         else
1920             break;
1921         old_pos++;
1922         if (info)
1923         {
1924             last_info = info;
1925             if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1926             {
1927                 old_pos--;  /* restore pos to previous entry */
1928                 break;
1929             }
1930             if (single_entry) break;
1931             /* check if we still have enough space for the largest possible entry */
1932             if (io->Information + max_dir_info_size(class) > length) break;
1933         }
1934     }
1935
1936     lseek( fd, old_pos, SEEK_SET );  /* store dir offset as filepos for fd */
1937     closedir( dir );
1938
1939     if (last_info) last_info->next = 0;
1940     else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1941 }
1942
1943 /***********************************************************************
1944  *           read_directory_stat
1945  *
1946  * Read a single file from a directory by determining whether the file
1947  * identified by mask exists using stat.
1948  */
1949 static int read_directory_stat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1950                                 BOOLEAN single_entry, const UNICODE_STRING *mask,
1951                                 BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1952 {
1953     int unix_len, ret, used_default;
1954     char *unix_name;
1955     struct stat st;
1956
1957     TRACE("trying optimisation for file %s\n", debugstr_us( mask ));
1958
1959     unix_len = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), NULL, 0, NULL, NULL );
1960     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len + 1)))
1961     {
1962         io->u.Status = STATUS_NO_MEMORY;
1963         return 0;
1964     }
1965     ret = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), unix_name, unix_len,
1966                            NULL, &used_default );
1967     if (ret > 0 && !used_default)
1968     {
1969         unix_name[ret] = 0;
1970         if (restart_scan)
1971         {
1972             lseek( fd, 0, SEEK_SET );
1973         }
1974         else if (lseek( fd, 0, SEEK_CUR ) != 0)
1975         {
1976             io->u.Status = STATUS_NO_MORE_FILES;
1977             ret = 0;
1978             goto done;
1979         }
1980
1981         ret = stat( unix_name, &st );
1982         if (!ret)
1983         {
1984             union file_directory_info *info = append_entry( buffer, io, length, unix_name, NULL, NULL, class );
1985             if (info)
1986             {
1987                 info->next = 0;
1988                 if (io->u.Status != STATUS_BUFFER_OVERFLOW) lseek( fd, 1, SEEK_CUR );
1989             }
1990             else io->u.Status = STATUS_NO_MORE_FILES;
1991         }
1992     }
1993     else ret = -1;
1994
1995 done:
1996     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1997
1998     TRACE("returning %d\n", ret);
1999
2000     return ret;
2001 }
2002
2003
2004 static inline WCHAR *mempbrkW( const WCHAR *ptr, const WCHAR *accept, size_t n )
2005 {
2006     const WCHAR *end;
2007     for (end = ptr + n; ptr < end; ptr++) if (strchrW( accept, *ptr )) return (WCHAR *)ptr;
2008     return NULL;
2009 }
2010
2011 /******************************************************************************
2012  *  NtQueryDirectoryFile        [NTDLL.@]
2013  *  ZwQueryDirectoryFile        [NTDLL.@]
2014  */
2015 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
2016                                       PIO_APC_ROUTINE apc_routine, PVOID apc_context,
2017                                       PIO_STATUS_BLOCK io,
2018                                       PVOID buffer, ULONG length,
2019                                       FILE_INFORMATION_CLASS info_class,
2020                                       BOOLEAN single_entry,
2021                                       PUNICODE_STRING mask,
2022                                       BOOLEAN restart_scan )
2023 {
2024     int cwd, fd, needs_close;
2025     static const WCHAR wszWildcards[] = { '*','?',0 };
2026
2027     TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2028           handle, event, apc_routine, apc_context, io, buffer,
2029           length, info_class, single_entry, debugstr_us(mask),
2030           restart_scan);
2031
2032     if (event || apc_routine)
2033     {
2034         FIXME( "Unsupported yet option\n" );
2035         return io->u.Status = STATUS_NOT_IMPLEMENTED;
2036     }
2037     switch (info_class)
2038     {
2039     case FileDirectoryInformation:
2040     case FileBothDirectoryInformation:
2041     case FileFullDirectoryInformation:
2042     case FileIdBothDirectoryInformation:
2043     case FileIdFullDirectoryInformation:
2044         if (length < dir_info_size( info_class, 1 )) return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
2045         if (!buffer) return io->u.Status = STATUS_ACCESS_VIOLATION;
2046         break;
2047     default:
2048         FIXME( "Unsupported file info class %d\n", info_class );
2049         return io->u.Status = STATUS_NOT_IMPLEMENTED;
2050     }
2051
2052     if ((io->u.Status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
2053         return io->u.Status;
2054
2055     io->Information = 0;
2056
2057     RtlEnterCriticalSection( &dir_section );
2058
2059     if (show_dot_files == -1) init_options();
2060
2061     cwd = open( ".", O_RDONLY );
2062     if (fchdir( fd ) != -1)
2063     {
2064         struct stat st;
2065         fstat( fd, &st );
2066         curdir.dev = st.st_dev;
2067         curdir.ino = st.st_ino;
2068 #ifdef VFAT_IOCTL_READDIR_BOTH
2069         if ((read_directory_vfat( fd, io, buffer, length, single_entry,
2070                                   mask, restart_scan, info_class )) != -1) goto done;
2071 #endif
2072         if (mask && !mempbrkW( mask->Buffer, wszWildcards, mask->Length / sizeof(WCHAR) ) &&
2073             read_directory_stat( fd, io, buffer, length, single_entry,
2074                                  mask, restart_scan, info_class ) != -1) goto done;
2075 #ifdef USE_GETDENTS
2076         if ((read_directory_getdents( fd, io, buffer, length, single_entry,
2077                                       mask, restart_scan, info_class )) != -1) goto done;
2078 #elif defined HAVE_GETDIRENTRIES
2079         if ((read_directory_getdirentries( fd, io, buffer, length, single_entry,
2080                                            mask, restart_scan, info_class )) != -1) goto done;
2081 #endif
2082         read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan, info_class );
2083
2084     done:
2085         if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
2086     }
2087     else io->u.Status = FILE_GetNtStatus();
2088
2089     RtlLeaveCriticalSection( &dir_section );
2090
2091     if (needs_close) close( fd );
2092     if (cwd != -1) close( cwd );
2093     TRACE( "=> %x (%ld)\n", io->u.Status, io->Information );
2094     return io->u.Status;
2095 }
2096
2097
2098 /***********************************************************************
2099  *           find_file_in_dir
2100  *
2101  * Find a file in a directory the hard way, by doing a case-insensitive search.
2102  * The file found is appended to unix_name at pos.
2103  * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2104  */
2105 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
2106                                   int check_case, int *is_win_dir )
2107 {
2108     WCHAR buffer[MAX_DIR_ENTRY_LEN];
2109     UNICODE_STRING str;
2110     BOOLEAN spaces;
2111     DIR *dir;
2112     struct dirent *de;
2113     struct stat st;
2114     int ret, used_default, is_name_8_dot_3;
2115
2116     /* try a shortcut for this directory */
2117
2118     unix_name[pos++] = '/';
2119     ret = ntdll_wcstoumbs( 0, name, length, unix_name + pos, MAX_DIR_ENTRY_LEN,
2120                            NULL, &used_default );
2121     /* if we used the default char, the Unix name won't round trip properly back to Unicode */
2122     /* so it cannot match the file we are looking for */
2123     if (ret >= 0 && !used_default)
2124     {
2125         unix_name[pos + ret] = 0;
2126         if (!stat( unix_name, &st ))
2127         {
2128             if (is_win_dir) *is_win_dir = is_same_file( &windir, &st );
2129             return STATUS_SUCCESS;
2130         }
2131     }
2132     if (check_case) goto not_found;  /* we want an exact match */
2133
2134     if (pos > 1) unix_name[pos - 1] = 0;
2135     else unix_name[1] = 0;  /* keep the initial slash */
2136
2137     /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2138
2139     str.Buffer = (WCHAR *)name;
2140     str.Length = length * sizeof(WCHAR);
2141     str.MaximumLength = str.Length;
2142     is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
2143
2144     if (!is_name_8_dot_3 && !get_dir_case_sensitivity( unix_name )) goto not_found;
2145
2146     /* now look for it through the directory */
2147
2148 #ifdef VFAT_IOCTL_READDIR_BOTH
2149     if (is_name_8_dot_3)
2150     {
2151         int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
2152         if (fd != -1)
2153         {
2154             KERNEL_DIRENT *kde;
2155
2156             RtlEnterCriticalSection( &dir_section );
2157             if ((kde = start_vfat_ioctl( fd )))
2158             {
2159                 unix_name[pos - 1] = '/';
2160                 while (kde[0].d_reclen)
2161                 {
2162                     /* make sure names are null-terminated to work around an x86-64 kernel bug */
2163                     size_t len = min(kde[0].d_reclen, sizeof(kde[0].d_name) - 1 );
2164                     kde[0].d_name[len] = 0;
2165                     len = min(kde[1].d_reclen, sizeof(kde[1].d_name) - 1 );
2166                     kde[1].d_name[len] = 0;
2167
2168                     if (kde[1].d_name[0])
2169                     {
2170                         ret = ntdll_umbstowcs( 0, kde[1].d_name, strlen(kde[1].d_name),
2171                                                buffer, MAX_DIR_ENTRY_LEN );
2172                         if (ret == length && !memicmpW( buffer, name, length))
2173                         {
2174                             strcpy( unix_name + pos, kde[1].d_name );
2175                             RtlLeaveCriticalSection( &dir_section );
2176                             close( fd );
2177                             goto success;
2178                         }
2179                     }
2180                     ret = ntdll_umbstowcs( 0, kde[0].d_name, strlen(kde[0].d_name),
2181                                            buffer, MAX_DIR_ENTRY_LEN );
2182                     if (ret == length && !memicmpW( buffer, name, length))
2183                     {
2184                         strcpy( unix_name + pos,
2185                                 kde[1].d_name[0] ? kde[1].d_name : kde[0].d_name );
2186                         RtlLeaveCriticalSection( &dir_section );
2187                         close( fd );
2188                         goto success;
2189                     }
2190                     if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) == -1)
2191                     {
2192                         RtlLeaveCriticalSection( &dir_section );
2193                         close( fd );
2194                         goto not_found;
2195                     }
2196                 }
2197             }
2198             RtlLeaveCriticalSection( &dir_section );
2199             close( fd );
2200         }
2201         /* fall through to normal handling */
2202     }
2203 #endif /* VFAT_IOCTL_READDIR_BOTH */
2204
2205     if (!(dir = opendir( unix_name )))
2206     {
2207         if (errno == ENOENT) return STATUS_OBJECT_PATH_NOT_FOUND;
2208         else return FILE_GetNtStatus();
2209     }
2210     unix_name[pos - 1] = '/';
2211     str.Buffer = buffer;
2212     str.MaximumLength = sizeof(buffer);
2213     while ((de = readdir( dir )))
2214     {
2215         ret = ntdll_umbstowcs( 0, de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
2216         if (ret == length && !memicmpW( buffer, name, length ))
2217         {
2218             strcpy( unix_name + pos, de->d_name );
2219             closedir( dir );
2220             goto success;
2221         }
2222
2223         if (!is_name_8_dot_3) continue;
2224
2225         str.Length = ret * sizeof(WCHAR);
2226         if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
2227         {
2228             WCHAR short_nameW[12];
2229             ret = hash_short_file_name( &str, short_nameW );
2230             if (ret == length && !memicmpW( short_nameW, name, length ))
2231             {
2232                 strcpy( unix_name + pos, de->d_name );
2233                 closedir( dir );
2234                 goto success;
2235             }
2236         }
2237     }
2238     closedir( dir );
2239
2240 not_found:
2241     unix_name[pos - 1] = 0;
2242     return STATUS_OBJECT_PATH_NOT_FOUND;
2243
2244 success:
2245     if (is_win_dir && !stat( unix_name, &st )) *is_win_dir = is_same_file( &windir, &st );
2246     return STATUS_SUCCESS;
2247 }
2248
2249
2250 #ifndef _WIN64
2251
2252 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2253 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2254 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};
2255 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2256 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2257 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2258 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
2259 static const WCHAR syswow64W[] = {'s','y','s','w','o','w','6','4',0};
2260 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
2261 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2262 static const WCHAR wow_regeditW[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
2263
2264 static struct
2265 {
2266     const WCHAR *source;
2267     const WCHAR *dos_target;
2268     const char *unix_target;
2269 } redirects[] =
2270 {
2271     { catrootW, NULL, NULL },
2272     { catroot2W, NULL, NULL },
2273     { driversstoreW, NULL, NULL },
2274     { driversetcW, NULL, NULL },
2275     { logfilesW, NULL, NULL },
2276     { spoolW, NULL, NULL },
2277     { system32W, syswow64W, NULL },
2278     { sysnativeW, system32W, NULL },
2279     { regeditW, wow_regeditW, NULL }
2280 };
2281
2282 static unsigned int nb_redirects;
2283
2284
2285 /***********************************************************************
2286  *           get_redirect_target
2287  *
2288  * Find the target unix name for a redirected dir.
2289  */
2290 static const char *get_redirect_target( const char *windows_dir, const WCHAR *name )
2291 {
2292     int used_default, len, pos, win_len = strlen( windows_dir );
2293     char *unix_name, *unix_target = NULL;
2294     NTSTATUS status;
2295
2296     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, win_len + MAX_DIR_ENTRY_LEN + 2 )))
2297         return NULL;
2298     memcpy( unix_name, windows_dir, win_len );
2299     pos = win_len;
2300
2301     while (*name)
2302     {
2303         const WCHAR *end, *next;
2304
2305         for (end = name; *end; end++) if (IS_SEPARATOR(*end)) break;
2306         for (next = end; *next; next++) if (!IS_SEPARATOR(*next)) break;
2307
2308         status = find_file_in_dir( unix_name, pos, name, end - name, FALSE, NULL );
2309         if (status == STATUS_OBJECT_PATH_NOT_FOUND && !*next)  /* not finding last element is ok */
2310         {
2311             len = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2312                                    MAX_DIR_ENTRY_LEN - (pos - win_len), NULL, &used_default );
2313             if (len > 0 && !used_default)
2314             {
2315                 unix_name[pos] = '/';
2316                 pos += len + 1;
2317                 unix_name[pos] = 0;
2318                 break;
2319             }
2320         }
2321         if (status) goto done;
2322         pos += strlen( unix_name + pos );
2323         name = next;
2324     }
2325
2326     if ((unix_target = RtlAllocateHeap( GetProcessHeap(), 0, pos - win_len )))
2327         memcpy( unix_target, unix_name + win_len + 1, pos - win_len );
2328
2329 done:
2330     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2331     return unix_target;
2332 }
2333
2334
2335 /***********************************************************************
2336  *           init_redirects
2337  */
2338 static void init_redirects(void)
2339 {
2340     UNICODE_STRING nt_name;
2341     ANSI_STRING unix_name;
2342     NTSTATUS status;
2343     struct stat st;
2344     unsigned int i;
2345
2346     if (!RtlDosPathNameToNtPathName_U( user_shared_data->NtSystemRoot, &nt_name, NULL, NULL ))
2347     {
2348         ERR( "can't convert %s\n", debugstr_w(user_shared_data->NtSystemRoot) );
2349         return;
2350     }
2351     status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN_IF, FALSE );
2352     RtlFreeUnicodeString( &nt_name );
2353     if (status)
2354     {
2355         ERR( "cannot open %s (%x)\n", debugstr_w(user_shared_data->NtSystemRoot), status );
2356         return;
2357     }
2358     if (!stat( unix_name.Buffer, &st ))
2359     {
2360         windir.dev = st.st_dev;
2361         windir.ino = st.st_ino;
2362         nb_redirects = sizeof(redirects) / sizeof(redirects[0]);
2363         for (i = 0; i < nb_redirects; i++)
2364         {
2365             if (!redirects[i].dos_target) continue;
2366             redirects[i].unix_target = get_redirect_target( unix_name.Buffer, redirects[i].dos_target );
2367             TRACE( "%s -> %s\n", debugstr_w(redirects[i].source), redirects[i].unix_target );
2368         }
2369     }
2370     RtlFreeAnsiString( &unix_name );
2371
2372 }
2373
2374
2375 /***********************************************************************
2376  *           match_redirect
2377  *
2378  * Check if path matches a redirect name. If yes, return matched length.
2379  */
2380 static int match_redirect( const WCHAR *path, int len, const WCHAR *redir, int check_case )
2381 {
2382     int i = 0;
2383
2384     while (i < len && *redir)
2385     {
2386         if (IS_SEPARATOR(path[i]))
2387         {
2388             if (*redir++ != '\\') return 0;
2389             while (i < len && IS_SEPARATOR(path[i])) i++;
2390             continue;  /* move on to next path component */
2391         }
2392         else if (check_case)
2393         {
2394             if (path[i] != *redir) return 0;
2395         }
2396         else
2397         {
2398             if (tolowerW(path[i]) != tolowerW(*redir)) return 0;
2399         }
2400         i++;
2401         redir++;
2402     }
2403     if (*redir) return 0;
2404     if (i < len && !IS_SEPARATOR(path[i])) return 0;
2405     while (i < len && IS_SEPARATOR(path[i])) i++;
2406     return i;
2407 }
2408
2409
2410 /***********************************************************************
2411  *           get_redirect_path
2412  *
2413  * Retrieve the Unix path corresponding to a redirected path if any.
2414  */
2415 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, int check_case )
2416 {
2417     unsigned int i;
2418     int len;
2419
2420     for (i = 0; i < nb_redirects; i++)
2421     {
2422         if ((len = match_redirect( name, length, redirects[i].source, check_case )))
2423         {
2424             if (!redirects[i].unix_target) break;
2425             unix_name[pos++] = '/';
2426             strcpy( unix_name + pos, redirects[i].unix_target );
2427             return len;
2428         }
2429     }
2430     return 0;
2431 }
2432
2433 #else  /* _WIN64 */
2434
2435 /* there are no redirects on 64-bit */
2436
2437 static const unsigned int nb_redirects = 0;
2438
2439 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, int check_case )
2440 {
2441     return 0;
2442 }
2443
2444 #endif
2445
2446 /***********************************************************************
2447  *           DIR_init_windows_dir
2448  */
2449 void DIR_init_windows_dir( const WCHAR *win, const WCHAR *sys )
2450 {
2451     /* FIXME: should probably store paths as NT file names */
2452
2453     RtlCreateUnicodeString( &system_dir, sys );
2454
2455 #ifndef _WIN64
2456     if (is_wow64) init_redirects();
2457 #endif
2458 }
2459
2460
2461 /******************************************************************************
2462  *           get_dos_device
2463  *
2464  * Get the Unix path of a DOS device.
2465  */
2466 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *unix_name_ret )
2467 {
2468     const char *config_dir = wine_get_config_dir();
2469     struct stat st;
2470     char *unix_name, *new_name, *dev;
2471     unsigned int i;
2472     int unix_len;
2473
2474     /* make sure the device name is ASCII */
2475     for (i = 0; i < name_len; i++)
2476         if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
2477
2478     unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
2479
2480     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2481         return STATUS_NO_MEMORY;
2482
2483     strcpy( unix_name, config_dir );
2484     strcat( unix_name, "/dosdevices/" );
2485     dev = unix_name + strlen(unix_name);
2486
2487     for (i = 0; i < name_len; i++) dev[i] = (char)tolowerW(name[i]);
2488     dev[i] = 0;
2489
2490     /* special case for drive devices */
2491     if (name_len == 2 && dev[1] == ':')
2492     {
2493         dev[i++] = ':';
2494         dev[i] = 0;
2495     }
2496
2497     for (;;)
2498     {
2499         if (!stat( unix_name, &st ))
2500         {
2501             TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
2502             unix_name_ret->Buffer = unix_name;
2503             unix_name_ret->Length = strlen(unix_name);
2504             unix_name_ret->MaximumLength = unix_len;
2505             return STATUS_SUCCESS;
2506         }
2507         if (!dev) break;
2508
2509         /* now try some defaults for it */
2510         if (!strcmp( dev, "aux" ))
2511         {
2512             strcpy( dev, "com1" );
2513             continue;
2514         }
2515         if (!strcmp( dev, "prn" ))
2516         {
2517             strcpy( dev, "lpt1" );
2518             continue;
2519         }
2520         if (!strcmp( dev, "nul" ))
2521         {
2522             strcpy( unix_name, "/dev/null" );
2523             dev = NULL; /* last try */
2524             continue;
2525         }
2526
2527         new_name = NULL;
2528         if (dev[1] == ':' && dev[2] == ':')  /* drive device */
2529         {
2530             dev[2] = 0;  /* remove last ':' to get the drive mount point symlink */
2531             new_name = get_default_drive_device( unix_name );
2532         }
2533         else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( atoi(dev + 3 ));
2534         else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( atoi(dev + 3 ));
2535
2536         if (!new_name) break;
2537
2538         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2539         unix_name = new_name;
2540         unix_len = strlen(unix_name) + 1;
2541         dev = NULL; /* last try */
2542     }
2543     RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2544     return STATUS_BAD_DEVICE_TYPE;
2545 }
2546
2547
2548 /* return the length of the DOS namespace prefix if any */
2549 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
2550 {
2551     static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
2552     static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2553
2554     if (name->Length > sizeof(nt_prefixW) &&
2555         !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
2556         return sizeof(nt_prefixW) / sizeof(WCHAR);
2557
2558     if (name->Length > sizeof(dosdev_prefixW) &&
2559         !memicmpW( name->Buffer, dosdev_prefixW, sizeof(dosdev_prefixW)/sizeof(WCHAR) ))
2560         return sizeof(dosdev_prefixW) / sizeof(WCHAR);
2561
2562     return 0;
2563 }
2564
2565
2566 /******************************************************************************
2567  *           find_file_id
2568  *
2569  * Recursively search directories from the dir queue for a given inode.
2570  */
2571 static NTSTATUS find_file_id( ANSI_STRING *unix_name, ULONGLONG file_id, dev_t dev )
2572 {
2573     unsigned int pos;
2574     DIR *dir;
2575     struct dirent *de;
2576     NTSTATUS status;
2577     struct stat st;
2578
2579     while (!(status = next_dir_in_queue( unix_name->Buffer )))
2580     {
2581         if (!(dir = opendir( unix_name->Buffer ))) continue;
2582         TRACE( "searching %s for %s\n", unix_name->Buffer, wine_dbgstr_longlong(file_id) );
2583         pos = strlen( unix_name->Buffer );
2584         if (pos + MAX_DIR_ENTRY_LEN >= unix_name->MaximumLength/sizeof(WCHAR))
2585         {
2586             char *new = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name->Buffer,
2587                                            unix_name->MaximumLength * 2 );
2588             if (!new)
2589             {
2590                 closedir( dir );
2591                 return STATUS_NO_MEMORY;
2592             }
2593             unix_name->MaximumLength *= 2;
2594             unix_name->Buffer = new;
2595         }
2596         unix_name->Buffer[pos++] = '/';
2597         while ((de = readdir( dir )))
2598         {
2599             if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2600             strcpy( unix_name->Buffer + pos, de->d_name );
2601             if (lstat( unix_name->Buffer, &st ) == -1) continue;
2602             if (st.st_dev != dev) continue;
2603             if (st.st_ino == file_id)
2604             {
2605                 closedir( dir );
2606                 return STATUS_SUCCESS;
2607             }
2608             if (!S_ISDIR( st.st_mode )) continue;
2609             if ((status = add_dir_to_queue( unix_name->Buffer )) != STATUS_SUCCESS)
2610             {
2611                 closedir( dir );
2612                 return status;
2613             }
2614         }
2615         closedir( dir );
2616     }
2617     return status;
2618 }
2619
2620
2621 /******************************************************************************
2622  *           file_id_to_unix_file_name
2623  *
2624  * Lookup a file from its file id instead of its name.
2625  */
2626 NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name )
2627 {
2628     enum server_fd_type type;
2629     int old_cwd, root_fd, needs_close;
2630     NTSTATUS status;
2631     ULONGLONG file_id;
2632     struct stat st, root_st;
2633
2634     if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
2635     if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
2636     memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
2637
2638     unix_name->MaximumLength = 2 * MAX_DIR_ENTRY_LEN + 4;
2639     if (!(unix_name->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, unix_name->MaximumLength )))
2640         return STATUS_NO_MEMORY;
2641     strcpy( unix_name->Buffer, "." );
2642
2643     if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
2644         goto done;
2645
2646     if (type != FD_TYPE_DIR)
2647     {
2648         status = STATUS_OBJECT_TYPE_MISMATCH;
2649         goto done;
2650     }
2651
2652     fstat( root_fd, &root_st );
2653     if (root_st.st_ino == file_id)  /* shortcut for "." */
2654     {
2655         status = STATUS_SUCCESS;
2656         goto done;
2657     }
2658
2659     RtlEnterCriticalSection( &dir_section );
2660     if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
2661     {
2662         /* shortcut for ".." */
2663         if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
2664         {
2665             strcpy( unix_name->Buffer, ".." );
2666             status = STATUS_SUCCESS;
2667         }
2668         else
2669         {
2670             status = add_dir_to_queue( "." );
2671             if (!status)
2672                 status = find_file_id( unix_name, file_id, root_st.st_dev );
2673             if (!status)  /* get rid of "./" prefix */
2674                 memmove( unix_name->Buffer, unix_name->Buffer + 2, strlen(unix_name->Buffer) - 1 );
2675             flush_dir_queue();
2676         }
2677         if (fchdir( old_cwd ) == -1) chdir( "/" );
2678     }
2679     else status = FILE_GetNtStatus();
2680     RtlLeaveCriticalSection( &dir_section );
2681     if (old_cwd != -1) close( old_cwd );
2682
2683 done:
2684     if (status == STATUS_SUCCESS)
2685     {
2686         TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name->Buffer) );
2687         unix_name->Length = strlen( unix_name->Buffer );
2688     }
2689     else
2690     {
2691         TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
2692         RtlFreeHeap( GetProcessHeap(), 0, unix_name->Buffer );
2693     }
2694     if (needs_close) close( root_fd );
2695     return status;
2696 }
2697
2698
2699 /******************************************************************************
2700  *           lookup_unix_name
2701  *
2702  * Helper for nt_to_unix_file_name
2703  */
2704 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
2705                                   UINT disposition, BOOLEAN check_case )
2706 {
2707     NTSTATUS status;
2708     int ret, used_default, len;
2709     struct stat st;
2710     char *unix_name = *buffer;
2711     const BOOL redirect = nb_redirects && ntdll_get_thread_data()->wow64_redir;
2712
2713     /* try a shortcut first */
2714
2715     ret = ntdll_wcstoumbs( 0, name, name_len, unix_name + pos, unix_len - pos - 1,
2716                            NULL, &used_default );
2717
2718     while (name_len && IS_SEPARATOR(*name))
2719     {
2720         name++;
2721         name_len--;
2722     }
2723
2724     if (ret >= 0 && !used_default)  /* if we used the default char the name didn't convert properly */
2725     {
2726         char *p;
2727         unix_name[pos + ret] = 0;
2728         for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
2729         if (!redirect || (!strstr( unix_name, "/windows/") && strncmp( unix_name, "windows/", 8 )))
2730         {
2731             if (!stat( unix_name, &st ))
2732             {
2733                 /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */
2734                 if (disposition == FILE_CREATE)
2735                     return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED;
2736                 return STATUS_SUCCESS;
2737             }
2738         }
2739     }
2740
2741     if (!name_len)  /* empty name -> drive root doesn't exist */
2742         return STATUS_OBJECT_PATH_NOT_FOUND;
2743     if (check_case && !redirect && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
2744         return STATUS_OBJECT_NAME_NOT_FOUND;
2745
2746     /* now do it component by component */
2747
2748     while (name_len)
2749     {
2750         const WCHAR *end, *next;
2751         int is_win_dir = 0;
2752
2753         end = name;
2754         while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
2755         next = end;
2756         while (next < name + name_len && IS_SEPARATOR(*next)) next++;
2757         name_len -= next - name;
2758
2759         /* grow the buffer if needed */
2760
2761         if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
2762         {
2763             char *new_name;
2764             unix_len += 2 * MAX_DIR_ENTRY_LEN;
2765             if (!(new_name = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name, unix_len )))
2766                 return STATUS_NO_MEMORY;
2767             unix_name = *buffer = new_name;
2768         }
2769
2770         status = find_file_in_dir( unix_name, pos, name, end - name,
2771                                    check_case, redirect ? &is_win_dir : NULL );
2772
2773         /* if this is the last element, not finding it is not necessarily fatal */
2774         if (!name_len)
2775         {
2776             if (status == STATUS_OBJECT_PATH_NOT_FOUND)
2777             {
2778                 status = STATUS_OBJECT_NAME_NOT_FOUND;
2779                 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
2780                 {
2781                     ret = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2782                                            MAX_DIR_ENTRY_LEN, NULL, &used_default );
2783                     if (ret > 0 && !used_default)
2784                     {
2785                         unix_name[pos] = '/';
2786                         unix_name[pos + 1 + ret] = 0;
2787                         status = STATUS_NO_SUCH_FILE;
2788                         break;
2789                     }
2790                 }
2791             }
2792             else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
2793             {
2794                 status = STATUS_OBJECT_NAME_COLLISION;
2795             }
2796         }
2797
2798         if (status != STATUS_SUCCESS) break;
2799
2800         pos += strlen( unix_name + pos );
2801         name = next;
2802
2803         if (is_win_dir && (len = get_redirect_path( unix_name, pos, name, name_len, check_case )))
2804         {
2805             name += len;
2806             name_len -= len;
2807             pos += strlen( unix_name + pos );
2808             TRACE( "redirecting -> %s + %s\n", debugstr_a(unix_name), debugstr_w(name) );
2809         }
2810     }
2811
2812     return status;
2813 }
2814
2815
2816 /******************************************************************************
2817  *           nt_to_unix_file_name_attr
2818  */
2819 NTSTATUS nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name_ret,
2820                                     UINT disposition )
2821 {
2822     static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
2823     enum server_fd_type type;
2824     int old_cwd, root_fd, needs_close;
2825     const WCHAR *name, *p;
2826     char *unix_name;
2827     int name_len, unix_len;
2828     NTSTATUS status;
2829     BOOLEAN check_case = !(attr->Attributes & OBJ_CASE_INSENSITIVE);
2830
2831     if (!attr->RootDirectory)  /* without root dir fall back to normal lookup */
2832         return wine_nt_to_unix_file_name( attr->ObjectName, unix_name_ret, disposition, check_case );
2833
2834     name     = attr->ObjectName->Buffer;
2835     name_len = attr->ObjectName->Length / sizeof(WCHAR);
2836
2837     if (name_len && IS_SEPARATOR(name[0])) return STATUS_INVALID_PARAMETER;
2838
2839     /* check for invalid characters */
2840     for (p = name; p < name + name_len; p++)
2841         if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
2842
2843     unix_len = ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
2844     unix_len += MAX_DIR_ENTRY_LEN + 3;
2845     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2846         return STATUS_NO_MEMORY;
2847     unix_name[0] = '.';
2848
2849     if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
2850     {
2851         if (type != FD_TYPE_DIR)
2852         {
2853             if (needs_close) close( root_fd );
2854             status = STATUS_BAD_DEVICE_TYPE;
2855         }
2856         else
2857         {
2858             RtlEnterCriticalSection( &dir_section );
2859             if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
2860             {
2861                 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1,
2862                                            disposition, check_case );
2863                 if (fchdir( old_cwd ) == -1) chdir( "/" );
2864             }
2865             else status = FILE_GetNtStatus();
2866             RtlLeaveCriticalSection( &dir_section );
2867             if (old_cwd != -1) close( old_cwd );
2868             if (needs_close) close( root_fd );
2869         }
2870     }
2871     else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
2872
2873     if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
2874     {
2875         TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
2876         unix_name_ret->Buffer = unix_name;
2877         unix_name_ret->Length = strlen(unix_name);
2878         unix_name_ret->MaximumLength = unix_len;
2879     }
2880     else
2881     {
2882         TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
2883         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2884     }
2885     return status;
2886 }
2887
2888
2889 /******************************************************************************
2890  *           wine_nt_to_unix_file_name  (NTDLL.@) Not a Windows API
2891  *
2892  * Convert a file name from NT namespace to Unix namespace.
2893  *
2894  * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
2895  * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
2896  * returned, but the unix name is still filled in properly.
2897  */
2898 NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
2899                                           UINT disposition, BOOLEAN check_case )
2900 {
2901     static const WCHAR unixW[] = {'u','n','i','x'};
2902     static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
2903
2904     NTSTATUS status = STATUS_SUCCESS;
2905     const char *config_dir = wine_get_config_dir();
2906     const WCHAR *name, *p;
2907     struct stat st;
2908     char *unix_name;
2909     int pos, ret, name_len, unix_len, prefix_len, used_default;
2910     WCHAR prefix[MAX_DIR_ENTRY_LEN];
2911     BOOLEAN is_unix = FALSE;
2912
2913     name     = nameW->Buffer;
2914     name_len = nameW->Length / sizeof(WCHAR);
2915
2916     if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
2917
2918     if (!(pos = get_dos_prefix_len( nameW )))
2919         return STATUS_BAD_DEVICE_TYPE;  /* no DOS prefix, assume NT native name */
2920
2921     name += pos;
2922     name_len -= pos;
2923
2924     /* check for sub-directory */
2925     for (pos = 0; pos < name_len; pos++)
2926     {
2927         if (IS_SEPARATOR(name[pos])) break;
2928         if (name[pos] < 32 || strchrW( invalid_charsW, name[pos] ))
2929             return STATUS_OBJECT_NAME_INVALID;
2930     }
2931     if (pos > MAX_DIR_ENTRY_LEN)
2932         return STATUS_OBJECT_NAME_INVALID;
2933
2934     if (pos == name_len)  /* no subdir, plain DOS device */
2935         return get_dos_device( name, name_len, unix_name_ret );
2936
2937     for (prefix_len = 0; prefix_len < pos; prefix_len++)
2938         prefix[prefix_len] = tolowerW(name[prefix_len]);
2939
2940     name += prefix_len;
2941     name_len -= prefix_len;
2942
2943     /* check for invalid characters (all chars except 0 are valid for unix) */
2944     is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
2945     if (is_unix)
2946     {
2947         for (p = name; p < name + name_len; p++)
2948             if (!*p) return STATUS_OBJECT_NAME_INVALID;
2949         check_case = TRUE;
2950     }
2951     else
2952     {
2953         for (p = name; p < name + name_len; p++)
2954             if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
2955     }
2956
2957     unix_len = ntdll_wcstoumbs( 0, prefix, prefix_len, NULL, 0, NULL, NULL );
2958     unix_len += ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
2959     unix_len += MAX_DIR_ENTRY_LEN + 3;
2960     unix_len += strlen(config_dir) + sizeof("/dosdevices/");
2961     if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2962         return STATUS_NO_MEMORY;
2963     strcpy( unix_name, config_dir );
2964     strcat( unix_name, "/dosdevices/" );
2965     pos = strlen(unix_name);
2966
2967     ret = ntdll_wcstoumbs( 0, prefix, prefix_len, unix_name + pos, unix_len - pos - 1,
2968                            NULL, &used_default );
2969     if (!ret || used_default)
2970     {
2971         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2972         return STATUS_OBJECT_NAME_INVALID;
2973     }
2974     pos += ret;
2975
2976     /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
2977
2978     if (prefix_len != 2 || prefix[1] != ':')
2979     {
2980         unix_name[pos] = 0;
2981         if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
2982         {
2983             if (!is_unix)
2984             {
2985                 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2986                 return STATUS_BAD_DEVICE_TYPE;
2987             }
2988             pos = 0;  /* fall back to unix root */
2989         }
2990     }
2991
2992     status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, check_case );
2993     if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
2994     {
2995         TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
2996         unix_name_ret->Buffer = unix_name;
2997         unix_name_ret->Length = strlen(unix_name);
2998         unix_name_ret->MaximumLength = unix_len;
2999     }
3000     else
3001     {
3002         TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3003         RtlFreeHeap( GetProcessHeap(), 0, unix_name );
3004     }
3005     return status;
3006 }
3007
3008
3009 /******************************************************************
3010  *              RtlWow64EnableFsRedirection   (NTDLL.@)
3011  */
3012 NTSTATUS WINAPI RtlWow64EnableFsRedirection( BOOLEAN enable )
3013 {
3014     if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
3015     ntdll_get_thread_data()->wow64_redir = enable;
3016     return STATUS_SUCCESS;
3017 }
3018
3019
3020 /******************************************************************
3021  *              RtlWow64EnableFsRedirectionEx   (NTDLL.@)
3022  */
3023 NTSTATUS WINAPI RtlWow64EnableFsRedirectionEx( ULONG disable, ULONG *old_value )
3024 {
3025     if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
3026     if (((ULONG_PTR)old_value >> 16) == 0) return STATUS_ACCESS_VIOLATION;
3027
3028     *old_value = !ntdll_get_thread_data()->wow64_redir;
3029     ntdll_get_thread_data()->wow64_redir = !disable;
3030     return STATUS_SUCCESS;
3031 }
3032
3033
3034 /******************************************************************
3035  *              RtlDoesFileExists_U   (NTDLL.@)
3036  */
3037 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
3038 {
3039     UNICODE_STRING nt_name;
3040     FILE_BASIC_INFORMATION basic_info;
3041     OBJECT_ATTRIBUTES attr;
3042     BOOLEAN ret;
3043
3044     if (!RtlDosPathNameToNtPathName_U( file_name, &nt_name, NULL, NULL )) return FALSE;
3045
3046     attr.Length = sizeof(attr);
3047     attr.RootDirectory = 0;
3048     attr.ObjectName = &nt_name;
3049     attr.Attributes = OBJ_CASE_INSENSITIVE;
3050     attr.SecurityDescriptor = NULL;
3051     attr.SecurityQualityOfService = NULL;
3052
3053     ret = NtQueryAttributesFile(&attr, &basic_info) == STATUS_SUCCESS;
3054
3055     RtlFreeUnicodeString( &nt_name );
3056     return ret;
3057 }
3058
3059
3060 /***********************************************************************
3061  *           DIR_unmount_device
3062  *
3063  * Unmount the specified device.
3064  */
3065 NTSTATUS DIR_unmount_device( HANDLE handle )
3066 {
3067     NTSTATUS status;
3068     int unix_fd, needs_close;
3069
3070     if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
3071     {
3072         struct stat st;
3073         char *mount_point = NULL;
3074
3075         if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
3076             status = STATUS_INVALID_PARAMETER;
3077         else
3078         {
3079             if ((mount_point = get_device_mount_point( st.st_rdev )))
3080             {
3081 #ifdef __APPLE__
3082                 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
3083 #else
3084                 static const char umount[] = "umount >/dev/null 2>&1 ";
3085 #endif
3086                 char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount));
3087                 if (cmd)
3088                 {
3089                     strcpy( cmd, umount );
3090                     strcat( cmd, mount_point );
3091                     system( cmd );
3092                     RtlFreeHeap( GetProcessHeap(), 0, cmd );
3093 #ifdef linux
3094                     /* umount will fail to release the loop device since we still have
3095                        a handle to it, so we release it here */
3096                     if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3097 #endif
3098                 }
3099                 RtlFreeHeap( GetProcessHeap(), 0, mount_point );
3100             }
3101         }
3102         if (needs_close) close( unix_fd );
3103     }
3104     return status;
3105 }
3106
3107
3108 /******************************************************************************
3109  *           DIR_get_unix_cwd
3110  *
3111  * Retrieve the Unix name of the current directory; helper for wine_unix_to_nt_file_name.
3112  * Returned value must be freed by caller.
3113  */
3114 NTSTATUS DIR_get_unix_cwd( char **cwd )
3115 {
3116     int old_cwd, unix_fd, needs_close;
3117     CURDIR *curdir;
3118     HANDLE handle;
3119     NTSTATUS status;
3120
3121     RtlAcquirePebLock();
3122
3123     if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
3124         curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
3125     else
3126         curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
3127
3128     if (!(handle = curdir->Handle))
3129     {
3130         UNICODE_STRING dirW;
3131         OBJECT_ATTRIBUTES attr;
3132         IO_STATUS_BLOCK io;
3133
3134         if (!RtlDosPathNameToNtPathName_U( curdir->DosPath.Buffer, &dirW, NULL, NULL ))
3135         {
3136             status = STATUS_OBJECT_NAME_INVALID;
3137             goto done;
3138         }
3139         attr.Length = sizeof(attr);
3140         attr.RootDirectory = 0;
3141         attr.Attributes = OBJ_CASE_INSENSITIVE;
3142         attr.ObjectName = &dirW;
3143         attr.SecurityDescriptor = NULL;
3144         attr.SecurityQualityOfService = NULL;
3145
3146         status = NtOpenFile( &handle, 0, &attr, &io, 0,
3147                              FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
3148         RtlFreeUnicodeString( &dirW );
3149         if (status != STATUS_SUCCESS) goto done;
3150     }
3151
3152     if ((status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )) == STATUS_SUCCESS)
3153     {
3154         RtlEnterCriticalSection( &dir_section );
3155
3156         if ((old_cwd = open(".", O_RDONLY)) != -1 && fchdir( unix_fd ) != -1)
3157         {
3158             unsigned int size = 512;
3159
3160             for (;;)
3161             {
3162                 if (!(*cwd = RtlAllocateHeap( GetProcessHeap(), 0, size )))
3163                 {
3164                     status = STATUS_NO_MEMORY;
3165                     break;
3166                 }
3167                 if (getcwd( *cwd, size )) break;
3168                 RtlFreeHeap( GetProcessHeap(), 0, *cwd );
3169                 if (errno != ERANGE)
3170                 {
3171                     status = STATUS_OBJECT_PATH_INVALID;
3172                     break;
3173                 }
3174                 size *= 2;
3175             }
3176             if (fchdir( old_cwd ) == -1) chdir( "/" );
3177         }
3178         else status = FILE_GetNtStatus();
3179
3180         RtlLeaveCriticalSection( &dir_section );
3181         if (old_cwd != -1) close( old_cwd );
3182         if (needs_close) close( unix_fd );
3183     }
3184     if (!curdir->Handle) NtClose( handle );
3185
3186 done:
3187     RtlReleasePebLock();
3188     return status;
3189 }
3190
3191 struct read_changes_info
3192 {
3193     HANDLE FileHandle;
3194     PVOID Buffer;
3195     ULONG BufferSize;
3196     PIO_APC_ROUTINE apc;
3197     void           *apc_arg;
3198 };
3199
3200 /* callback for ioctl user APC */
3201 static void WINAPI read_changes_user_apc( void *arg, IO_STATUS_BLOCK *io, ULONG reserved )
3202 {
3203     struct read_changes_info *info = arg;
3204     if (info->apc) info->apc( info->apc_arg, io, reserved );
3205     RtlFreeHeap( GetProcessHeap(), 0, info );
3206 }
3207
3208 static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, void **apc )
3209 {
3210     struct read_changes_info *info = user;
3211     char data[PATH_MAX];
3212     NTSTATUS ret;
3213     int size;
3214
3215     SERVER_START_REQ( read_change )
3216     {
3217         req->handle = wine_server_obj_handle( info->FileHandle );
3218         wine_server_set_reply( req, data, PATH_MAX );
3219         ret = wine_server_call( req );
3220         size = wine_server_reply_size( reply );
3221     }
3222     SERVER_END_REQ;
3223
3224     if (ret == STATUS_SUCCESS && info->Buffer)
3225     {
3226         PFILE_NOTIFY_INFORMATION pfni = info->Buffer;
3227         int i, left = info->BufferSize;
3228         DWORD *last_entry_offset = NULL;
3229         struct filesystem_event *event = (struct filesystem_event*)data;
3230
3231         while (size && left >= sizeof(*pfni))
3232         {
3233             /* convert to an NT style path */
3234             for (i=0; i<event->len; i++)
3235                 if (event->name[i] == '/')
3236                     event->name[i] = '\\';
3237
3238             pfni->Action = event->action;
3239             pfni->FileNameLength = ntdll_umbstowcs( 0, event->name, event->len, pfni->FileName,
3240                     (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR));
3241             last_entry_offset = &pfni->NextEntryOffset;
3242
3243             if(pfni->FileNameLength == -1 || pfni->FileNameLength == -2)
3244                 break;
3245
3246             i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
3247             pfni->FileNameLength *= sizeof(WCHAR);
3248             pfni->NextEntryOffset = i;
3249             pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
3250             left -= i;
3251
3252             i = (offsetof(struct filesystem_event, name[event->len])
3253                     + sizeof(int)-1) / sizeof(int) * sizeof(int);
3254             event = (struct filesystem_event*)((char*)event + i);
3255             size -= i;
3256         }
3257
3258         if (size)
3259         {
3260             ret = STATUS_NOTIFY_ENUM_DIR;
3261             size = 0;
3262         }
3263         else
3264         {
3265             *last_entry_offset = 0;
3266             size = info->BufferSize - left;
3267         }
3268     }
3269     else
3270     {
3271         ret = STATUS_NOTIFY_ENUM_DIR;
3272         size = 0;
3273     }
3274
3275     iosb->u.Status = ret;
3276     iosb->Information = size;
3277     *apc = read_changes_user_apc;
3278     return ret;
3279 }
3280
3281 #define FILE_NOTIFY_ALL        (  \
3282  FILE_NOTIFY_CHANGE_FILE_NAME   | \
3283  FILE_NOTIFY_CHANGE_DIR_NAME    | \
3284  FILE_NOTIFY_CHANGE_ATTRIBUTES  | \
3285  FILE_NOTIFY_CHANGE_SIZE        | \
3286  FILE_NOTIFY_CHANGE_LAST_WRITE  | \
3287  FILE_NOTIFY_CHANGE_LAST_ACCESS | \
3288  FILE_NOTIFY_CHANGE_CREATION    | \
3289  FILE_NOTIFY_CHANGE_SECURITY   )
3290
3291 /******************************************************************************
3292  *  NtNotifyChangeDirectoryFile [NTDLL.@]
3293  */
3294 NTSTATUS WINAPI
3295 NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
3296         PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
3297         PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
3298         ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
3299 {
3300     struct read_changes_info *info;
3301     NTSTATUS status;
3302     ULONG_PTR cvalue = ApcRoutine ? 0 : (ULONG_PTR)ApcContext;
3303
3304     TRACE("%p %p %p %p %p %p %u %u %d\n",
3305           FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
3306           Buffer, BufferSize, CompletionFilter, WatchTree );
3307
3308     if (!IoStatusBlock)
3309         return STATUS_ACCESS_VIOLATION;
3310
3311     if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
3312         return STATUS_INVALID_PARAMETER;
3313
3314     info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof *info );
3315     if (!info)
3316         return STATUS_NO_MEMORY;
3317
3318     info->FileHandle = FileHandle;
3319     info->Buffer     = Buffer;
3320     info->BufferSize = BufferSize;
3321     info->apc        = ApcRoutine;
3322     info->apc_arg    = ApcContext;
3323
3324     SERVER_START_REQ( read_directory_changes )
3325     {
3326         req->filter     = CompletionFilter;
3327         req->want_data  = (Buffer != NULL);
3328         req->subtree    = WatchTree;
3329         req->async.handle   = wine_server_obj_handle( FileHandle );
3330         req->async.callback = wine_server_client_ptr( read_changes_apc );
3331         req->async.iosb     = wine_server_client_ptr( IoStatusBlock );
3332         req->async.arg      = wine_server_client_ptr( info );
3333         req->async.event    = wine_server_obj_handle( Event );
3334         req->async.cvalue   = cvalue;
3335         status = wine_server_call( req );
3336     }
3337     SERVER_END_REQ;
3338
3339     if (status != STATUS_PENDING)
3340         RtlFreeHeap( GetProcessHeap(), 0, info );
3341
3342     return status;
3343 }