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