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