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