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