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