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