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