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