ntdll/tests: Add a FILE_APPEND_DATA test.
[wine.git] / dlls / ntdll / directory.c
blob711a0b0ad5e143f3ef306602b67c95d31de30e0b
1 /*
2 * NTDLL directory functions
4 * Copyright 1993 Erik Bos
5 * Copyright 2003 Eric Pouech
6 * Copyright 1996, 2004 Alexandre Julliard
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.
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.
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
23 #include "config.h"
24 #include "wine/port.h"
26 #include <assert.h>
27 #include <sys/types.h>
28 #ifdef HAVE_DIRENT_H
29 # include <dirent.h>
30 #endif
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <limits.h>
38 #ifdef HAVE_MNTENT_H
39 #include <mntent.h>
40 #endif
41 #ifdef HAVE_SYS_STAT_H
42 # include <sys/stat.h>
43 #endif
44 #ifdef HAVE_SYS_SYSCALL_H
45 # include <sys/syscall.h>
46 #endif
47 #ifdef HAVE_SYS_IOCTL_H
48 #include <sys/ioctl.h>
49 #endif
50 #ifdef HAVE_LINUX_IOCTL_H
51 #include <linux/ioctl.h>
52 #endif
53 #ifdef HAVE_LINUX_MAJOR_H
54 # include <linux/major.h>
55 #endif
56 #ifdef HAVE_SYS_PARAM_H
57 #include <sys/param.h>
58 #endif
59 #ifdef HAVE_SYS_MOUNT_H
60 #include <sys/mount.h>
61 #endif
62 #include <time.h>
63 #ifdef HAVE_UNISTD_H
64 # include <unistd.h>
65 #endif
67 #define NONAMELESSUNION
68 #define NONAMELESSSTRUCT
69 #include "ntstatus.h"
70 #define WIN32_NO_STATUS
71 #include "windef.h"
72 #include "winnt.h"
73 #include "winternl.h"
74 #include "ntdll_misc.h"
75 #include "wine/unicode.h"
76 #include "wine/server.h"
77 #include "wine/list.h"
78 #include "wine/library.h"
79 #include "wine/debug.h"
81 WINE_DEFAULT_DEBUG_CHANNEL(file);
83 /* just in case... */
84 #undef VFAT_IOCTL_READDIR_BOTH
85 #undef USE_GETDENTS
87 #ifdef linux
89 /* We want the real kernel dirent structure, not the libc one */
90 typedef struct
92 long d_ino;
93 long d_off;
94 unsigned short d_reclen;
95 char d_name[256];
96 } KERNEL_DIRENT;
98 /* Define the VFAT ioctl to get both short and long file names */
99 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
101 #ifndef O_DIRECTORY
102 # define O_DIRECTORY 0200000 /* must be directory */
103 #endif
105 #ifdef SYS_getdents64
106 typedef struct
108 ULONG64 d_ino;
109 LONG64 d_off;
110 unsigned short d_reclen;
111 unsigned char d_type;
112 char d_name[256];
113 } KERNEL_DIRENT64;
115 static inline int getdents64( int fd, char *de, unsigned int size )
117 return syscall( SYS_getdents64, fd, de, size );
119 #define USE_GETDENTS
120 #endif
122 #endif /* linux */
124 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
125 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
127 #define INVALID_NT_CHARS '*','?','<','>','|','"'
128 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
130 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
132 #define MAX_IGNORED_FILES 4
134 struct file_identity
136 dev_t dev;
137 ino_t ino;
140 static struct file_identity ignored_files[MAX_IGNORED_FILES];
141 static int ignored_files_count;
143 union file_directory_info
145 ULONG next;
146 FILE_DIRECTORY_INFORMATION dir;
147 FILE_BOTH_DIRECTORY_INFORMATION both;
148 FILE_FULL_DIRECTORY_INFORMATION full;
149 FILE_ID_BOTH_DIRECTORY_INFORMATION id_both;
150 FILE_ID_FULL_DIRECTORY_INFORMATION id_full;
153 static int show_dot_files = -1;
155 /* at some point we may want to allow Winelib apps to set this */
156 static const int is_case_sensitive = FALSE;
158 UNICODE_STRING windows_dir = { 0, 0, NULL }; /* windows directory */
159 UNICODE_STRING system_dir = { 0, 0, NULL }; /* system directory */
161 static struct file_identity curdir;
162 static struct file_identity windir;
164 static RTL_CRITICAL_SECTION dir_section;
165 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
167 0, 0, &dir_section,
168 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
169 0, 0, { (DWORD_PTR)(__FILE__ ": dir_section") }
171 static RTL_CRITICAL_SECTION dir_section = { &critsect_debug, -1, 0, 0, 0, 0 };
174 /* check if a given Unicode char is OK in a DOS short name */
175 static inline BOOL is_invalid_dos_char( WCHAR ch )
177 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
178 if (ch > 0x7f) return TRUE;
179 return strchrW( invalid_chars, ch ) != NULL;
182 /* check if the device can be a mounted volume */
183 static inline int is_valid_mounted_device( const struct stat *st )
185 #if defined(linux) || defined(__sun__)
186 return S_ISBLK( st->st_mode );
187 #else
188 /* disks are char devices on *BSD */
189 return S_ISCHR( st->st_mode );
190 #endif
193 static inline void ignore_file( const char *name )
195 struct stat st;
196 assert( ignored_files_count < MAX_IGNORED_FILES );
197 if (!stat( name, &st ))
199 ignored_files[ignored_files_count].dev = st.st_dev;
200 ignored_files[ignored_files_count].ino = st.st_ino;
201 ignored_files_count++;
205 static inline BOOL is_same_file( const struct file_identity *file, const struct stat *st )
207 return st->st_dev == file->dev && st->st_ino == file->ino;
210 static inline BOOL is_ignored_file( const struct stat *st )
212 unsigned int i;
214 for (i = 0; i < ignored_files_count; i++)
215 if (is_same_file( &ignored_files[i], st )) return TRUE;
216 return FALSE;
219 static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned int len )
221 switch (class)
223 case FileDirectoryInformation:
224 return (FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[len] ) + 3) & ~3;
225 case FileBothDirectoryInformation:
226 return (FIELD_OFFSET( FILE_BOTH_DIRECTORY_INFORMATION, FileName[len] ) + 3) & ~3;
227 case FileFullDirectoryInformation:
228 return (FIELD_OFFSET( FILE_FULL_DIRECTORY_INFORMATION, FileName[len] ) + 3) & ~3;
229 case FileIdBothDirectoryInformation:
230 return (FIELD_OFFSET( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[len] ) + 3) & ~3;
231 case FileIdFullDirectoryInformation:
232 return (FIELD_OFFSET( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[len] ) + 3) & ~3;
233 default:
234 assert(0);
235 return 0;
239 static inline unsigned int max_dir_info_size( FILE_INFORMATION_CLASS class )
241 return dir_info_size( class, MAX_DIR_ENTRY_LEN );
245 /* support for a directory queue for filesystem searches */
247 struct dir_name
249 struct list entry;
250 char name[1];
253 static struct list dir_queue = LIST_INIT( dir_queue );
255 static NTSTATUS add_dir_to_queue( const char *name )
257 int len = strlen( name ) + 1;
258 struct dir_name *dir = RtlAllocateHeap( GetProcessHeap(), 0,
259 FIELD_OFFSET( struct dir_name, name[len] ));
260 if (!dir) return STATUS_NO_MEMORY;
261 strcpy( dir->name, name );
262 list_add_tail( &dir_queue, &dir->entry );
263 return STATUS_SUCCESS;
266 static NTSTATUS next_dir_in_queue( char *name )
268 struct list *head = list_head( &dir_queue );
269 if (head)
271 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
272 strcpy( name, dir->name );
273 list_remove( &dir->entry );
274 RtlFreeHeap( GetProcessHeap(), 0, dir );
275 return STATUS_SUCCESS;
277 return STATUS_OBJECT_NAME_NOT_FOUND;
280 static void flush_dir_queue(void)
282 struct list *head;
284 while ((head = list_head( &dir_queue )))
286 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
287 list_remove( &dir->entry );
288 RtlFreeHeap( GetProcessHeap(), 0, dir );
293 /***********************************************************************
294 * get_default_com_device
296 * Return the default device to use for serial ports.
298 static char *get_default_com_device( int num )
300 char *ret = NULL;
302 if (!num || num > 9) return ret;
303 #ifdef linux
304 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/ttyS0") );
305 if (ret)
307 strcpy( ret, "/dev/ttyS0" );
308 ret[strlen(ret) - 1] = '0' + num - 1;
310 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
311 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/cuad0") );
312 if (ret)
314 strcpy( ret, "/dev/cuad0" );
315 ret[strlen(ret) - 1] = '0' + num - 1;
317 #else
318 FIXME( "no known default for device com%d\n", num );
319 #endif
320 return ret;
324 /***********************************************************************
325 * get_default_lpt_device
327 * Return the default device to use for parallel ports.
329 static char *get_default_lpt_device( int num )
331 char *ret = NULL;
333 if (!num || num > 9) return ret;
334 #ifdef linux
335 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/lp0") );
336 if (ret)
338 strcpy( ret, "/dev/lp0" );
339 ret[strlen(ret) - 1] = '0' + num - 1;
341 #else
342 FIXME( "no known default for device lpt%d\n", num );
343 #endif
344 return ret;
348 /***********************************************************************
349 * DIR_get_drives_info
351 * Retrieve device/inode number for all the drives. Helper for find_drive_root.
353 unsigned int DIR_get_drives_info( struct drive_info info[MAX_DOS_DRIVES] )
355 static struct drive_info cache[MAX_DOS_DRIVES];
356 static time_t last_update;
357 static unsigned int nb_drives;
358 unsigned int ret;
359 time_t now = time(NULL);
361 RtlEnterCriticalSection( &dir_section );
362 if (now != last_update)
364 const char *config_dir = wine_get_config_dir();
365 char *buffer, *p;
366 struct stat st;
367 unsigned int i;
369 if ((buffer = RtlAllocateHeap( GetProcessHeap(), 0,
370 strlen(config_dir) + sizeof("/dosdevices/a:") )))
372 strcpy( buffer, config_dir );
373 strcat( buffer, "/dosdevices/a:" );
374 p = buffer + strlen(buffer) - 2;
376 for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
378 *p = 'a' + i;
379 if (!stat( buffer, &st ))
381 cache[i].dev = st.st_dev;
382 cache[i].ino = st.st_ino;
383 nb_drives++;
385 else
387 cache[i].dev = 0;
388 cache[i].ino = 0;
391 RtlFreeHeap( GetProcessHeap(), 0, buffer );
393 last_update = now;
395 memcpy( info, cache, sizeof(cache) );
396 ret = nb_drives;
397 RtlLeaveCriticalSection( &dir_section );
398 return ret;
402 /***********************************************************************
403 * parse_mount_entries
405 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
408 #ifdef sun
409 #include <sys/vfstab.h>
410 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
412 struct vfstab entry;
413 struct stat st;
414 char *device;
416 while (! getvfsent( f, &entry ))
418 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
419 if (!strcmp( entry.vfs_fstype, "nfs" ) ||
420 !strcmp( entry.vfs_fstype, "smbfs" ) ||
421 !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
423 if (stat( entry.vfs_mountp, &st ) == -1) continue;
424 if (st.st_dev != dev || st.st_ino != ino) continue;
425 if (!strcmp( entry.vfs_fstype, "fd" ))
427 if ((device = strstr( entry.vfs_mntopts, "dev=" )))
429 char *p = strchr( device + 4, ',' );
430 if (p) *p = 0;
431 return device + 4;
434 else
435 return entry.vfs_special;
437 return NULL;
439 #endif
441 #ifdef linux
442 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
444 struct mntent *entry;
445 struct stat st;
446 char *device;
448 while ((entry = getmntent( f )))
450 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
451 if (!strcmp( entry->mnt_type, "nfs" ) ||
452 !strcmp( entry->mnt_type, "smbfs" ) ||
453 !strcmp( entry->mnt_type, "ncpfs" )) continue;
455 if (stat( entry->mnt_dir, &st ) == -1) continue;
456 if (st.st_dev != dev || st.st_ino != ino) continue;
457 if (!strcmp( entry->mnt_type, "supermount" ))
459 if ((device = strstr( entry->mnt_opts, "dev=" )))
461 char *p = strchr( device + 4, ',' );
462 if (p) *p = 0;
463 return device + 4;
466 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
468 /* if device is a regular file check for a loop mount */
469 if ((device = strstr( entry->mnt_opts, "loop=" )))
471 char *p = strchr( device + 5, ',' );
472 if (p) *p = 0;
473 return device + 5;
476 else
477 return entry->mnt_fsname;
479 return NULL;
481 #endif
483 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
484 #include <fstab.h>
485 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
487 struct fstab *entry;
488 struct stat st;
490 while ((entry = getfsent()))
492 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
493 if (!strcmp( entry->fs_vfstype, "nfs" ) ||
494 !strcmp( entry->fs_vfstype, "smbfs" ) ||
495 !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
497 if (stat( entry->fs_file, &st ) == -1) continue;
498 if (st.st_dev != dev || st.st_ino != ino) continue;
499 return entry->fs_spec;
501 return NULL;
503 #endif
505 #ifdef sun
506 #include <sys/mnttab.h>
507 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
509 struct mnttab entry;
510 struct stat st;
511 char *device;
514 while (( ! getmntent( f, &entry) ))
516 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
517 if (!strcmp( entry.mnt_fstype, "nfs" ) ||
518 !strcmp( entry.mnt_fstype, "smbfs" ) ||
519 !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
521 if (stat( entry.mnt_mountp, &st ) == -1) continue;
522 if (st.st_dev != dev || st.st_ino != ino) continue;
523 if (!strcmp( entry.mnt_fstype, "fd" ))
525 if ((device = strstr( entry.mnt_mntopts, "dev=" )))
527 char *p = strchr( device + 4, ',' );
528 if (p) *p = 0;
529 return device + 4;
532 else
533 return entry.mnt_special;
535 return NULL;
537 #endif
539 /***********************************************************************
540 * get_default_drive_device
542 * Return the default device to use for a given drive mount point.
544 static char *get_default_drive_device( const char *root )
546 char *ret = NULL;
548 #ifdef linux
549 FILE *f;
550 char *device = NULL;
551 int fd, res = -1;
552 struct stat st;
554 /* try to open it first to force it to get mounted */
555 if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
557 res = fstat( fd, &st );
558 close( fd );
560 /* now try normal stat just in case */
561 if (res == -1) res = stat( root, &st );
562 if (res == -1) return NULL;
564 RtlEnterCriticalSection( &dir_section );
566 if ((f = fopen( "/etc/mtab", "r" )))
568 device = parse_mount_entries( f, st.st_dev, st.st_ino );
569 endmntent( f );
571 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
572 if (!device && (f = fopen( "/etc/fstab", "r" )))
574 device = parse_mount_entries( f, st.st_dev, st.st_ino );
575 endmntent( f );
577 if (device)
579 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
580 if (ret) strcpy( ret, device );
582 RtlLeaveCriticalSection( &dir_section );
584 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ )
585 char *device = NULL;
586 int fd, res = -1;
587 struct stat st;
589 /* try to open it first to force it to get mounted */
590 if ((fd = open( root, O_RDONLY )) != -1)
592 res = fstat( fd, &st );
593 close( fd );
595 /* now try normal stat just in case */
596 if (res == -1) res = stat( root, &st );
597 if (res == -1) return NULL;
599 RtlEnterCriticalSection( &dir_section );
601 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
602 * pass NULL. Leave the argument in for symmetry.
604 device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
605 if (device)
607 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
608 if (ret) strcpy( ret, device );
610 RtlLeaveCriticalSection( &dir_section );
612 #elif defined( sun )
613 FILE *f;
614 char *device = NULL;
615 int fd, res = -1;
616 struct stat st;
618 /* try to open it first to force it to get mounted */
619 if ((fd = open( root, O_RDONLY )) != -1)
621 res = fstat( fd, &st );
622 close( fd );
624 /* now try normal stat just in case */
625 if (res == -1) res = stat( root, &st );
626 if (res == -1) return NULL;
628 RtlEnterCriticalSection( &dir_section );
630 if ((f = fopen( "/etc/mnttab", "r" )))
632 device = parse_mount_entries( f, st.st_dev, st.st_ino);
633 fclose( f );
635 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
636 if (!device && (f = fopen( "/etc/vfstab", "r" )))
638 device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
639 fclose( f );
641 if (device)
643 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
644 if (ret) strcpy( ret, device );
646 RtlLeaveCriticalSection( &dir_section );
648 #elif defined(__APPLE__)
649 struct statfs *mntStat;
650 struct stat st;
651 int i;
652 int mntSize;
653 dev_t dev;
654 ino_t ino;
655 static const char path_bsd_device[] = "/dev/disk";
656 int res;
658 res = stat( root, &st );
659 if (res == -1) return NULL;
661 dev = st.st_dev;
662 ino = st.st_ino;
664 RtlEnterCriticalSection( &dir_section );
666 mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
668 for (i = 0; i < mntSize && !ret; i++)
670 if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
671 if (st.st_dev != dev || st.st_ino != ino) continue;
673 /* FIXME add support for mounted network drive */
674 if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
676 /* set return value to the corresponding raw BSD node */
677 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
678 if (ret)
680 strcpy(ret, "/dev/r");
681 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
685 RtlLeaveCriticalSection( &dir_section );
686 #else
687 static int warned;
688 if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
689 #endif
690 return ret;
694 /***********************************************************************
695 * get_device_mount_point
697 * Return the current mount point for a device.
699 static char *get_device_mount_point( dev_t dev )
701 char *ret = NULL;
703 #ifdef linux
704 FILE *f;
706 RtlEnterCriticalSection( &dir_section );
708 if ((f = fopen( "/etc/mtab", "r" )))
710 struct mntent *entry;
711 struct stat st;
712 char *p, *device;
714 while ((entry = getmntent( f )))
716 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
717 if (!strcmp( entry->mnt_type, "nfs" ) ||
718 !strcmp( entry->mnt_type, "smbfs" ) ||
719 !strcmp( entry->mnt_type, "ncpfs" )) continue;
721 if (!strcmp( entry->mnt_type, "supermount" ))
723 if ((device = strstr( entry->mnt_opts, "dev=" )))
725 device += 4;
726 if ((p = strchr( device, ',' ))) *p = 0;
729 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
731 /* if device is a regular file check for a loop mount */
732 if ((device = strstr( entry->mnt_opts, "loop=" )))
734 device += 5;
735 if ((p = strchr( device, ',' ))) *p = 0;
738 else device = entry->mnt_fsname;
740 if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
742 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry->mnt_dir) + 1 );
743 if (ret) strcpy( ret, entry->mnt_dir );
744 break;
747 endmntent( f );
749 RtlLeaveCriticalSection( &dir_section );
750 #elif defined(__APPLE__)
751 struct statfs *entry;
752 struct stat st;
753 int i, size;
755 RtlEnterCriticalSection( &dir_section );
757 size = getmntinfo( &entry, MNT_NOWAIT );
758 for (i = 0; i < size; i++)
760 if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
761 if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
763 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry[i].f_mntfromname) + 1 );
764 if (ret) strcpy( ret, entry[i].f_mntfromname );
765 break;
768 RtlLeaveCriticalSection( &dir_section );
769 #else
770 static int warned;
771 if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
772 #endif
773 return ret;
777 /***********************************************************************
778 * init_options
780 * Initialize the show_dot_files options.
782 static void init_options(void)
784 static const WCHAR WineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e',0};
785 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
786 char tmp[80];
787 HANDLE root, hkey;
788 DWORD dummy;
789 OBJECT_ATTRIBUTES attr;
790 UNICODE_STRING nameW;
792 show_dot_files = 0;
794 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
795 attr.Length = sizeof(attr);
796 attr.RootDirectory = root;
797 attr.ObjectName = &nameW;
798 attr.Attributes = 0;
799 attr.SecurityDescriptor = NULL;
800 attr.SecurityQualityOfService = NULL;
801 RtlInitUnicodeString( &nameW, WineW );
803 /* @@ Wine registry key: HKCU\Software\Wine */
804 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
806 RtlInitUnicodeString( &nameW, ShowDotFilesW );
807 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
809 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
810 show_dot_files = IS_OPTION_TRUE( str[0] );
812 NtClose( hkey );
814 NtClose( root );
816 /* a couple of directories that we don't want to return in directory searches */
817 ignore_file( wine_get_config_dir() );
818 ignore_file( "/dev" );
819 ignore_file( "/proc" );
820 #ifdef linux
821 ignore_file( "/sys" );
822 #endif
826 /***********************************************************************
827 * DIR_is_hidden_file
829 * Check if the specified file should be hidden based on its name and the show dot files option.
831 BOOL DIR_is_hidden_file( const UNICODE_STRING *name )
833 WCHAR *p, *end;
835 if (show_dot_files == -1) init_options();
836 if (show_dot_files) return FALSE;
838 end = p = name->Buffer + name->Length/sizeof(WCHAR);
839 while (p > name->Buffer && IS_SEPARATOR(p[-1])) p--;
840 while (p > name->Buffer && !IS_SEPARATOR(p[-1])) p--;
841 if (p == end || *p != '.') return FALSE;
842 /* make sure it isn't '.' or '..' */
843 if (p + 1 == end) return FALSE;
844 if (p[1] == '.' && p + 2 == end) return FALSE;
845 return TRUE;
849 /***********************************************************************
850 * hash_short_file_name
852 * Transform a Unix file name into a hashed DOS name. If the name is a valid
853 * DOS name, it is converted to upper-case; otherwise it is replaced by a
854 * hashed version that fits in 8.3 format.
855 * 'buffer' must be at least 12 characters long.
856 * Returns length of short name in bytes; short name is NOT null-terminated.
858 static ULONG hash_short_file_name( const UNICODE_STRING *name, LPWSTR buffer )
860 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
862 LPCWSTR p, ext, end = name->Buffer + name->Length / sizeof(WCHAR);
863 LPWSTR dst;
864 unsigned short hash;
865 int i;
867 /* Compute the hash code of the file name */
868 /* If you know something about hash functions, feel free to */
869 /* insert a better algorithm here... */
870 if (!is_case_sensitive)
872 for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
873 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
874 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
876 else
878 for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
879 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
880 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
883 /* Find last dot for start of the extension */
884 for (p = name->Buffer + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
886 /* Copy first 4 chars, replacing invalid chars with '_' */
887 for (i = 4, p = name->Buffer, dst = buffer; i > 0; i--, p++)
889 if (p == end || p == ext) break;
890 *dst++ = is_invalid_dos_char(*p) ? '_' : toupperW(*p);
892 /* Pad to 5 chars with '~' */
893 while (i-- >= 0) *dst++ = '~';
895 /* Insert hash code converted to 3 ASCII chars */
896 *dst++ = hash_chars[(hash >> 10) & 0x1f];
897 *dst++ = hash_chars[(hash >> 5) & 0x1f];
898 *dst++ = hash_chars[hash & 0x1f];
900 /* Copy the first 3 chars of the extension (if any) */
901 if (ext)
903 *dst++ = '.';
904 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
905 *dst++ = is_invalid_dos_char(*ext) ? '_' : toupperW(*ext);
907 return dst - buffer;
911 /***********************************************************************
912 * match_filename
914 * Check a long file name against a mask.
916 * Tests (done in W95 DOS shell - case insensitive):
917 * *.txt test1.test.txt *
918 * *st1* test1.txt *
919 * *.t??????.t* test1.ta.tornado.txt *
920 * *tornado* test1.ta.tornado.txt *
921 * t*t test1.ta.tornado.txt *
922 * ?est* test1.txt *
923 * ?est??? test1.txt -
924 * *test1.txt* test1.txt *
925 * h?l?o*t.dat hellothisisatest.dat *
927 static BOOLEAN match_filename( const UNICODE_STRING *name_str, const UNICODE_STRING *mask_str )
929 int mismatch;
930 const WCHAR *name = name_str->Buffer;
931 const WCHAR *mask = mask_str->Buffer;
932 const WCHAR *name_end = name + name_str->Length / sizeof(WCHAR);
933 const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
934 const WCHAR *lastjoker = NULL;
935 const WCHAR *next_to_retry = NULL;
937 TRACE("(%s, %s)\n", debugstr_us(name_str), debugstr_us(mask_str));
939 while (name < name_end && mask < mask_end)
941 switch(*mask)
943 case '*':
944 mask++;
945 while (mask < mask_end && *mask == '*') mask++; /* Skip consecutive '*' */
946 if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
947 lastjoker = mask;
949 /* skip to the next match after the joker(s) */
950 if (is_case_sensitive)
951 while (name < name_end && (*name != *mask)) name++;
952 else
953 while (name < name_end && (toupperW(*name) != toupperW(*mask))) name++;
954 next_to_retry = name;
955 break;
956 case '?':
957 mask++;
958 name++;
959 break;
960 default:
961 if (is_case_sensitive) mismatch = (*mask != *name);
962 else mismatch = (toupperW(*mask) != toupperW(*name));
964 if (!mismatch)
966 mask++;
967 name++;
968 if (mask == mask_end)
970 if (name == name_end) return TRUE;
971 if (lastjoker) mask = lastjoker;
974 else /* mismatch ! */
976 if (lastjoker) /* we had an '*', so we can try unlimitedly */
978 mask = lastjoker;
980 /* this scan sequence was a mismatch, so restart
981 * 1 char after the first char we checked last time */
982 next_to_retry++;
983 name = next_to_retry;
985 else return FALSE; /* bad luck */
987 break;
990 while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
991 mask++; /* Ignore trailing '.' or '*' in mask */
992 return (name == name_end && mask == mask_end);
996 /***********************************************************************
997 * append_entry
999 * helper for NtQueryDirectoryFile
1001 static union file_directory_info *append_entry( void *info_ptr, IO_STATUS_BLOCK *io, ULONG max_length,
1002 const char *long_name, const char *short_name,
1003 const UNICODE_STRING *mask, FILE_INFORMATION_CLASS class )
1005 union file_directory_info *info;
1006 int i, long_len, short_len, total_len;
1007 struct stat st;
1008 WCHAR long_nameW[MAX_DIR_ENTRY_LEN];
1009 WCHAR short_nameW[12];
1010 WCHAR *filename;
1011 UNICODE_STRING str;
1012 ULONG attributes = 0;
1014 io->u.Status = STATUS_SUCCESS;
1015 long_len = ntdll_umbstowcs( 0, long_name, strlen(long_name), long_nameW, MAX_DIR_ENTRY_LEN );
1016 if (long_len == -1) return NULL;
1018 str.Buffer = long_nameW;
1019 str.Length = long_len * sizeof(WCHAR);
1020 str.MaximumLength = sizeof(long_nameW);
1022 if (short_name)
1024 short_len = ntdll_umbstowcs( 0, short_name, strlen(short_name),
1025 short_nameW, sizeof(short_nameW) / sizeof(WCHAR) );
1026 if (short_len == -1) short_len = sizeof(short_nameW) / sizeof(WCHAR);
1028 else /* generate a short name if necessary */
1030 BOOLEAN spaces;
1032 short_len = 0;
1033 if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
1034 short_len = hash_short_file_name( &str, short_nameW );
1037 TRACE( "long %s short %s mask %s\n",
1038 debugstr_us(&str), debugstr_wn(short_nameW, short_len), debugstr_us(mask) );
1040 if (mask && !match_filename( &str, mask ))
1042 if (!short_len) return NULL; /* no short name to match */
1043 str.Buffer = short_nameW;
1044 str.Length = short_len * sizeof(WCHAR);
1045 str.MaximumLength = sizeof(short_nameW);
1046 if (!match_filename( &str, mask )) return NULL;
1049 if (lstat( long_name, &st ) == -1) return NULL;
1050 if (S_ISLNK( st.st_mode ))
1052 if (stat( long_name, &st ) == -1) return NULL;
1053 if (S_ISDIR( st.st_mode )) attributes |= FILE_ATTRIBUTE_REPARSE_POINT;
1055 if (is_ignored_file( &st ))
1057 TRACE( "ignoring file %s\n", long_name );
1058 return NULL;
1060 if (!show_dot_files && long_name[0] == '.' && long_name[1] && (long_name[1] != '.' || long_name[2]))
1061 attributes |= FILE_ATTRIBUTE_HIDDEN;
1063 total_len = dir_info_size( class, long_len );
1064 if (io->Information + total_len > max_length)
1066 total_len = max_length - io->Information;
1067 io->u.Status = STATUS_BUFFER_OVERFLOW;
1069 info = (union file_directory_info *)((char *)info_ptr + io->Information);
1070 if (st.st_dev != curdir.dev) st.st_ino = 0; /* ignore inode if on a different device */
1071 /* all the structures start with a FileDirectoryInformation layout */
1072 fill_stat_info( &st, info, class );
1073 info->dir.NextEntryOffset = total_len;
1074 info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
1075 info->dir.FileAttributes |= attributes;
1077 switch (class)
1079 case FileDirectoryInformation:
1080 info->dir.FileNameLength = long_len * sizeof(WCHAR);
1081 filename = info->dir.FileName;
1082 break;
1084 case FileFullDirectoryInformation:
1085 info->full.EaSize = 0; /* FIXME */
1086 info->full.FileNameLength = long_len * sizeof(WCHAR);
1087 filename = info->full.FileName;
1088 break;
1090 case FileIdFullDirectoryInformation:
1091 info->id_full.EaSize = 0; /* FIXME */
1092 info->id_full.FileNameLength = long_len * sizeof(WCHAR);
1093 filename = info->id_full.FileName;
1094 break;
1096 case FileBothDirectoryInformation:
1097 info->both.EaSize = 0; /* FIXME */
1098 info->both.ShortNameLength = short_len * sizeof(WCHAR);
1099 for (i = 0; i < short_len; i++) info->both.ShortName[i] = toupperW(short_nameW[i]);
1100 info->both.FileNameLength = long_len * sizeof(WCHAR);
1101 filename = info->both.FileName;
1102 break;
1104 case FileIdBothDirectoryInformation:
1105 info->id_both.EaSize = 0; /* FIXME */
1106 info->id_both.ShortNameLength = short_len * sizeof(WCHAR);
1107 for (i = 0; i < short_len; i++) info->id_both.ShortName[i] = toupperW(short_nameW[i]);
1108 info->id_both.FileNameLength = long_len * sizeof(WCHAR);
1109 filename = info->id_both.FileName;
1110 break;
1112 default:
1113 assert(0);
1114 return NULL;
1116 memcpy( filename, long_nameW, total_len - ((char *)filename - (char *)info) );
1117 io->Information += total_len;
1118 return info;
1122 #ifdef VFAT_IOCTL_READDIR_BOTH
1124 /***********************************************************************
1125 * start_vfat_ioctl
1127 * Wrapper for the VFAT ioctl to work around various kernel bugs.
1128 * dir_section must be held by caller.
1130 static KERNEL_DIRENT *start_vfat_ioctl( int fd )
1132 static KERNEL_DIRENT *de;
1133 int res;
1135 if (!de)
1137 const size_t page_size = getpagesize();
1138 SIZE_T size = 2 * sizeof(*de) + page_size;
1139 void *addr = NULL;
1141 if (NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_RESERVE, PAGE_READWRITE ))
1142 return NULL;
1143 /* commit only the size needed for the dir entries */
1144 /* this leaves an extra unaccessible page, which should make the kernel */
1145 /* fail with -EFAULT before it stomps all over our memory */
1146 de = addr;
1147 size = 2 * sizeof(*de);
1148 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_COMMIT, PAGE_READWRITE );
1151 /* set d_reclen to 65535 to work around an AFS kernel bug */
1152 de[0].d_reclen = 65535;
1153 res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
1154 if (res == -1)
1156 if (errno != ENOENT) return NULL; /* VFAT ioctl probably not supported */
1157 de[0].d_reclen = 0; /* eof */
1159 else if (!res && de[0].d_reclen == 65535) return NULL; /* AFS bug */
1161 return de;
1165 /***********************************************************************
1166 * read_directory_vfat
1168 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
1170 static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1171 BOOLEAN single_entry, const UNICODE_STRING *mask,
1172 BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1175 size_t len;
1176 KERNEL_DIRENT *de;
1177 union file_directory_info *info, *last_info = NULL;
1179 io->u.Status = STATUS_SUCCESS;
1181 if (restart_scan) lseek( fd, 0, SEEK_SET );
1183 if (length < max_dir_info_size(class)) /* we may have to return a partial entry here */
1185 off_t old_pos = lseek( fd, 0, SEEK_CUR );
1187 if (!(de = start_vfat_ioctl( fd ))) return -1; /* not supported */
1189 while (de[0].d_reclen)
1191 /* make sure names are null-terminated to work around an x86-64 kernel bug */
1192 len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1193 de[0].d_name[len] = 0;
1194 len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1195 de[1].d_name[len] = 0;
1197 if (de[1].d_name[0])
1198 info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask, class );
1199 else
1200 info = append_entry( buffer, io, length, de[0].d_name, NULL, mask, class );
1201 if (info)
1203 last_info = info;
1204 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1205 lseek( fd, old_pos, SEEK_SET ); /* restore pos to previous entry */
1206 break;
1208 old_pos = lseek( fd, 0, SEEK_CUR );
1209 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
1212 else /* we'll only return full entries, no need to worry about overflow */
1214 if (!(de = start_vfat_ioctl( fd ))) return -1; /* not supported */
1216 while (de[0].d_reclen)
1218 /* make sure names are null-terminated to work around an x86-64 kernel bug */
1219 len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1220 de[0].d_name[len] = 0;
1221 len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1222 de[1].d_name[len] = 0;
1224 if (de[1].d_name[0])
1225 info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask, class );
1226 else
1227 info = append_entry( buffer, io, length, de[0].d_name, NULL, mask, class );
1228 if (info)
1230 last_info = info;
1231 if (single_entry) break;
1232 /* check if we still have enough space for the largest possible entry */
1233 if (io->Information + max_dir_info_size(class) > length) break;
1235 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
1239 if (last_info) last_info->next = 0;
1240 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1241 return 0;
1243 #endif /* VFAT_IOCTL_READDIR_BOTH */
1246 /***********************************************************************
1247 * read_directory_getdents
1249 * Read a directory using the Linux getdents64 system call; helper for NtQueryDirectoryFile.
1251 #ifdef USE_GETDENTS
1252 static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1253 BOOLEAN single_entry, const UNICODE_STRING *mask,
1254 BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1256 off_t old_pos = 0;
1257 size_t size = length;
1258 int res, fake_dot_dot = 1;
1259 char *data, local_buffer[8192];
1260 KERNEL_DIRENT64 *de;
1261 union file_directory_info *info, *last_info = NULL;
1263 if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1265 size = sizeof(local_buffer);
1266 data = local_buffer;
1269 if (restart_scan) lseek( fd, 0, SEEK_SET );
1270 else if (length < max_dir_info_size(class)) /* we may have to return a partial entry here */
1272 old_pos = lseek( fd, 0, SEEK_CUR );
1273 if (old_pos == -1 && errno == ENOENT)
1275 io->u.Status = STATUS_NO_MORE_FILES;
1276 res = 0;
1277 goto done;
1281 io->u.Status = STATUS_SUCCESS;
1283 res = getdents64( fd, data, size );
1284 if (res == -1)
1286 if (errno != ENOSYS)
1288 io->u.Status = FILE_GetNtStatus();
1289 res = 0;
1291 goto done;
1294 de = (KERNEL_DIRENT64 *)data;
1296 if (restart_scan)
1298 /* check if we got . and .. from getdents */
1299 if (res > 0)
1301 if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
1303 KERNEL_DIRENT64 *next_de = (KERNEL_DIRENT64 *)(data + de->d_reclen);
1304 if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
1307 /* make sure we have enough room for both entries */
1308 if (fake_dot_dot)
1310 const ULONG min_info_size = dir_info_size( class, 1 ) + dir_info_size( class, 2 );
1311 if (length < min_info_size || single_entry)
1313 FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
1314 fake_dot_dot = 0;
1318 if (fake_dot_dot)
1320 if ((info = append_entry( buffer, io, length, ".", NULL, mask, class )))
1321 last_info = info;
1322 if ((info = append_entry( buffer, io, length, "..", NULL, mask, class )))
1323 last_info = info;
1325 /* check if we still have enough space for the largest possible entry */
1326 if (last_info && io->Information + max_dir_info_size(class) > length)
1328 lseek( fd, 0, SEEK_SET ); /* reset pos to first entry */
1329 res = 0;
1334 while (res > 0)
1336 res -= de->d_reclen;
1337 if (de->d_ino &&
1338 !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
1339 (info = append_entry( buffer, io, length, de->d_name, NULL, mask, class )))
1341 last_info = info;
1342 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1344 lseek( fd, old_pos, SEEK_SET ); /* restore pos to previous entry */
1345 break;
1347 /* check if we still have enough space for the largest possible entry */
1348 if (single_entry || io->Information + max_dir_info_size(class) > length)
1350 if (res > 0) lseek( fd, de->d_off, SEEK_SET ); /* set pos to next entry */
1351 break;
1354 old_pos = de->d_off;
1355 /* move on to the next entry */
1356 if (res > 0) de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen);
1357 else
1359 res = getdents64( fd, data, size );
1360 de = (KERNEL_DIRENT64 *)data;
1364 if (last_info) last_info->next = 0;
1365 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1366 res = 0;
1367 done:
1368 if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1369 return res;
1372 #elif defined HAVE_GETDIRENTRIES
1374 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1376 /* Darwin doesn't provide a version of getdirentries with support for 64-bit
1377 * inodes. When 64-bit inodes are enabled, the getdirentries symbol is mapped
1378 * to _getdirentries_is_not_available_when_64_bit_inodes_are_in_effect so that
1379 * we get link errors if we try to use it. We still need getdirentries, but we
1380 * don't need it to support 64-bit inodes. So, we use the legacy getdirentries
1381 * with 32-bit inodes. We have to be careful to use a corresponding dirent
1382 * structure, too.
1384 int darwin_legacy_getdirentries(int, char *, int, long *) __asm("_getdirentries");
1385 #define getdirentries darwin_legacy_getdirentries
1387 struct darwin_legacy_dirent {
1388 __uint32_t d_ino;
1389 __uint16_t d_reclen;
1390 __uint8_t d_type;
1391 __uint8_t d_namlen;
1392 char d_name[__DARWIN_MAXNAMLEN + 1];
1394 #define dirent darwin_legacy_dirent
1396 #endif
1398 /***********************************************************************
1399 * wine_getdirentries
1401 * Wrapper for the BSD getdirentries system call to fix a bug in the
1402 * Mac OS X version. For some file systems (at least Apple Filing
1403 * Protocol a.k.a. AFP), getdirentries resets the file position to 0
1404 * when it's about to return 0 (no more entries). So, a subsequent
1405 * getdirentries call starts over at the beginning again, causing an
1406 * infinite loop.
1408 static inline int wine_getdirentries(int fd, char *buf, int nbytes, long *basep)
1410 int res = getdirentries(fd, buf, nbytes, basep);
1411 #ifdef __APPLE__
1412 if (res == 0)
1413 lseek(fd, *basep, SEEK_SET);
1414 #endif
1415 return res;
1418 /***********************************************************************
1419 * read_directory_getdirentries
1421 * Read a directory using the BSD getdirentries system call; helper for NtQueryDirectoryFile.
1423 static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1424 BOOLEAN single_entry, const UNICODE_STRING *mask,
1425 BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1427 long restart_pos;
1428 ULONG_PTR restart_info_pos = 0;
1429 size_t size, initial_size = length;
1430 int res, fake_dot_dot = 1;
1431 char *data, local_buffer[8192];
1432 struct dirent *de;
1433 union file_directory_info *info, *last_info = NULL, *restart_last_info = NULL;
1435 size = initial_size;
1436 data = local_buffer;
1437 if (size > sizeof(local_buffer) && !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1439 io->u.Status = STATUS_NO_MEMORY;
1440 return io->u.Status;
1443 if (restart_scan) lseek( fd, 0, SEEK_SET );
1445 io->u.Status = STATUS_SUCCESS;
1447 /* FIXME: should make sure size is larger than filesystem block size */
1448 res = wine_getdirentries( fd, data, size, &restart_pos );
1449 if (res == -1)
1451 io->u.Status = FILE_GetNtStatus();
1452 res = 0;
1453 goto done;
1456 de = (struct dirent *)data;
1458 if (restart_scan)
1460 /* check if we got . and .. from getdirentries */
1461 if (res > 0)
1463 if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
1465 struct dirent *next_de = (struct dirent *)(data + de->d_reclen);
1466 if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
1469 /* make sure we have enough room for both entries */
1470 if (fake_dot_dot)
1472 const ULONG min_info_size = dir_info_size( class, 1 ) + dir_info_size( class, 2 );
1473 if (length < min_info_size || single_entry)
1475 FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
1476 fake_dot_dot = 0;
1480 if (fake_dot_dot)
1482 if ((info = append_entry( buffer, io, length, ".", NULL, mask, class )))
1483 last_info = info;
1484 if ((info = append_entry( buffer, io, length, "..", NULL, mask, class )))
1485 last_info = info;
1487 restart_last_info = last_info;
1488 restart_info_pos = io->Information;
1490 /* check if we still have enough space for the largest possible entry */
1491 if (last_info && io->Information + max_dir_info_size(class) > length)
1493 lseek( fd, 0, SEEK_SET ); /* reset pos to first entry */
1494 res = 0;
1499 while (res > 0)
1501 res -= de->d_reclen;
1502 if (de->d_fileno &&
1503 !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
1504 ((info = append_entry( buffer, io, length, de->d_name, NULL, mask, class ))))
1506 last_info = info;
1507 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1509 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1510 if (restart_info_pos) /* if we have a complete read already, return it */
1512 io->u.Status = STATUS_SUCCESS;
1513 io->Information = restart_info_pos;
1514 last_info = restart_last_info;
1515 break;
1517 /* otherwise restart from the start with a smaller size */
1518 size = (char *)de - data;
1519 if (!size) break;
1520 io->Information = 0;
1521 last_info = NULL;
1522 goto restart;
1524 /* if we have to return but the buffer contains more data, restart with a smaller size */
1525 if (res > 0 && (single_entry || io->Information + max_dir_info_size(class) > length))
1527 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1528 size = (char *)de - data;
1529 io->Information = restart_info_pos;
1530 last_info = restart_last_info;
1531 goto restart;
1534 /* move on to the next entry */
1535 if (res > 0)
1537 de = (struct dirent *)((char *)de + de->d_reclen);
1538 continue;
1540 if (size < initial_size) break; /* already restarted once, give up now */
1541 size = min( size, length - io->Information );
1542 /* if size is too small don't bother to continue */
1543 if (size < max_dir_info_size(class) && last_info) break;
1544 restart_last_info = last_info;
1545 restart_info_pos = io->Information;
1546 restart:
1547 res = wine_getdirentries( fd, data, size, &restart_pos );
1548 de = (struct dirent *)data;
1551 if (last_info) last_info->next = 0;
1552 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1553 res = 0;
1554 done:
1555 if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1556 return res;
1559 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1560 #undef getdirentries
1561 #undef dirent
1562 #endif
1564 #endif /* HAVE_GETDIRENTRIES */
1567 /***********************************************************************
1568 * read_directory_readdir
1570 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
1572 static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1573 BOOLEAN single_entry, const UNICODE_STRING *mask,
1574 BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1576 DIR *dir;
1577 off_t i, old_pos = 0;
1578 struct dirent *de;
1579 union file_directory_info *info, *last_info = NULL;
1581 if (!(dir = opendir( "." )))
1583 io->u.Status = FILE_GetNtStatus();
1584 return;
1587 if (!restart_scan)
1589 old_pos = lseek( fd, 0, SEEK_CUR );
1590 /* skip the right number of entries */
1591 for (i = 0; i < old_pos - 2; i++)
1593 if (!readdir( dir ))
1595 closedir( dir );
1596 io->u.Status = STATUS_NO_MORE_FILES;
1597 return;
1601 io->u.Status = STATUS_SUCCESS;
1603 for (;;)
1605 if (old_pos == 0)
1606 info = append_entry( buffer, io, length, ".", NULL, mask, class );
1607 else if (old_pos == 1)
1608 info = append_entry( buffer, io, length, "..", NULL, mask, class );
1609 else if ((de = readdir( dir )))
1611 if (strcmp( de->d_name, "." ) && strcmp( de->d_name, ".." ))
1612 info = append_entry( buffer, io, length, de->d_name, NULL, mask, class );
1613 else
1614 info = NULL;
1616 else
1617 break;
1618 old_pos++;
1619 if (info)
1621 last_info = info;
1622 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1624 old_pos--; /* restore pos to previous entry */
1625 break;
1627 if (single_entry) break;
1628 /* check if we still have enough space for the largest possible entry */
1629 if (io->Information + max_dir_info_size(class) > length) break;
1633 lseek( fd, old_pos, SEEK_SET ); /* store dir offset as filepos for fd */
1634 closedir( dir );
1636 if (last_info) last_info->next = 0;
1637 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1640 /***********************************************************************
1641 * read_directory_stat
1643 * Read a single file from a directory by determining whether the file
1644 * identified by mask exists using stat.
1646 static int read_directory_stat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1647 BOOLEAN single_entry, const UNICODE_STRING *mask,
1648 BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
1650 int unix_len, ret, used_default;
1651 char *unix_name;
1652 struct stat st;
1654 TRACE("trying optimisation for file %s\n", debugstr_us( mask ));
1656 unix_len = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), NULL, 0, NULL, NULL );
1657 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len + 1)))
1659 io->u.Status = STATUS_NO_MEMORY;
1660 return 0;
1662 ret = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), unix_name, unix_len,
1663 NULL, &used_default );
1664 if (ret > 0 && !used_default)
1666 unix_name[ret] = 0;
1667 if (restart_scan)
1669 lseek( fd, 0, SEEK_SET );
1671 else if (lseek( fd, 0, SEEK_CUR ) != 0)
1673 io->u.Status = STATUS_NO_MORE_FILES;
1674 ret = 0;
1675 goto done;
1678 ret = stat( unix_name, &st );
1679 if (!ret)
1681 union file_directory_info *info = append_entry( buffer, io, length, unix_name, NULL, NULL, class );
1682 if (info)
1684 info->next = 0;
1685 if (io->u.Status != STATUS_BUFFER_OVERFLOW) lseek( fd, 1, SEEK_CUR );
1687 else io->u.Status = STATUS_NO_MORE_FILES;
1690 else ret = -1;
1692 done:
1693 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1695 TRACE("returning %d\n", ret);
1697 return ret;
1701 static inline WCHAR *mempbrkW( const WCHAR *ptr, const WCHAR *accept, size_t n )
1703 const WCHAR *end;
1704 for (end = ptr + n; ptr < end; ptr++) if (strchrW( accept, *ptr )) return (WCHAR *)ptr;
1705 return NULL;
1708 /******************************************************************************
1709 * NtQueryDirectoryFile [NTDLL.@]
1710 * ZwQueryDirectoryFile [NTDLL.@]
1712 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
1713 PIO_APC_ROUTINE apc_routine, PVOID apc_context,
1714 PIO_STATUS_BLOCK io,
1715 PVOID buffer, ULONG length,
1716 FILE_INFORMATION_CLASS info_class,
1717 BOOLEAN single_entry,
1718 PUNICODE_STRING mask,
1719 BOOLEAN restart_scan )
1721 int cwd, fd, needs_close;
1722 static const WCHAR wszWildcards[] = { '*','?',0 };
1724 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
1725 handle, event, apc_routine, apc_context, io, buffer,
1726 length, info_class, single_entry, debugstr_us(mask),
1727 restart_scan);
1729 if (event || apc_routine)
1731 FIXME( "Unsupported yet option\n" );
1732 return io->u.Status = STATUS_NOT_IMPLEMENTED;
1734 switch (info_class)
1736 case FileDirectoryInformation:
1737 case FileBothDirectoryInformation:
1738 case FileFullDirectoryInformation:
1739 case FileIdBothDirectoryInformation:
1740 case FileIdFullDirectoryInformation:
1741 if (length < dir_info_size( info_class, 1 )) return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
1742 break;
1743 default:
1744 FIXME( "Unsupported file info class %d\n", info_class );
1745 return io->u.Status = STATUS_NOT_IMPLEMENTED;
1748 if ((io->u.Status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
1749 return io->u.Status;
1751 io->Information = 0;
1753 RtlEnterCriticalSection( &dir_section );
1755 if (show_dot_files == -1) init_options();
1757 cwd = open( ".", O_RDONLY );
1758 if (fchdir( fd ) != -1)
1760 struct stat st;
1761 fstat( fd, &st );
1762 curdir.dev = st.st_dev;
1763 curdir.ino = st.st_ino;
1764 #ifdef VFAT_IOCTL_READDIR_BOTH
1765 if ((read_directory_vfat( fd, io, buffer, length, single_entry,
1766 mask, restart_scan, info_class )) != -1) goto done;
1767 #endif
1768 if (mask && !mempbrkW( mask->Buffer, wszWildcards, mask->Length / sizeof(WCHAR) ) &&
1769 read_directory_stat( fd, io, buffer, length, single_entry,
1770 mask, restart_scan, info_class ) != -1) goto done;
1771 #ifdef USE_GETDENTS
1772 if ((read_directory_getdents( fd, io, buffer, length, single_entry,
1773 mask, restart_scan, info_class )) != -1) goto done;
1774 #elif defined HAVE_GETDIRENTRIES
1775 if ((read_directory_getdirentries( fd, io, buffer, length, single_entry,
1776 mask, restart_scan, info_class )) != -1) goto done;
1777 #endif
1778 read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan, info_class );
1780 done:
1781 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
1783 else io->u.Status = FILE_GetNtStatus();
1785 RtlLeaveCriticalSection( &dir_section );
1787 if (needs_close) close( fd );
1788 if (cwd != -1) close( cwd );
1789 TRACE( "=> %x (%ld)\n", io->u.Status, io->Information );
1790 return io->u.Status;
1794 /***********************************************************************
1795 * find_file_in_dir
1797 * Find a file in a directory the hard way, by doing a case-insensitive search.
1798 * The file found is appended to unix_name at pos.
1799 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
1801 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
1802 int check_case, int *is_win_dir )
1804 WCHAR buffer[MAX_DIR_ENTRY_LEN];
1805 UNICODE_STRING str;
1806 BOOLEAN spaces;
1807 DIR *dir;
1808 struct dirent *de;
1809 struct stat st;
1810 int ret, used_default, is_name_8_dot_3;
1812 /* try a shortcut for this directory */
1814 unix_name[pos++] = '/';
1815 ret = ntdll_wcstoumbs( 0, name, length, unix_name + pos, MAX_DIR_ENTRY_LEN,
1816 NULL, &used_default );
1817 /* if we used the default char, the Unix name won't round trip properly back to Unicode */
1818 /* so it cannot match the file we are looking for */
1819 if (ret >= 0 && !used_default)
1821 unix_name[pos + ret] = 0;
1822 if (!stat( unix_name, &st ))
1824 if (is_win_dir) *is_win_dir = is_same_file( &windir, &st );
1825 return STATUS_SUCCESS;
1828 if (check_case) goto not_found; /* we want an exact match */
1830 if (pos > 1) unix_name[pos - 1] = 0;
1831 else unix_name[1] = 0; /* keep the initial slash */
1833 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
1835 str.Buffer = (WCHAR *)name;
1836 str.Length = length * sizeof(WCHAR);
1837 str.MaximumLength = str.Length;
1838 is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
1840 /* now look for it through the directory */
1842 #ifdef VFAT_IOCTL_READDIR_BOTH
1843 if (is_name_8_dot_3)
1845 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
1846 if (fd != -1)
1848 KERNEL_DIRENT *de;
1850 RtlEnterCriticalSection( &dir_section );
1851 if ((de = start_vfat_ioctl( fd )))
1853 unix_name[pos - 1] = '/';
1854 while (de[0].d_reclen)
1856 /* make sure names are null-terminated to work around an x86-64 kernel bug */
1857 size_t len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1858 de[0].d_name[len] = 0;
1859 len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1860 de[1].d_name[len] = 0;
1862 if (de[1].d_name[0])
1864 ret = ntdll_umbstowcs( 0, de[1].d_name, strlen(de[1].d_name),
1865 buffer, MAX_DIR_ENTRY_LEN );
1866 if (ret == length && !memicmpW( buffer, name, length))
1868 strcpy( unix_name + pos, de[1].d_name );
1869 RtlLeaveCriticalSection( &dir_section );
1870 close( fd );
1871 goto success;
1874 ret = ntdll_umbstowcs( 0, de[0].d_name, strlen(de[0].d_name),
1875 buffer, MAX_DIR_ENTRY_LEN );
1876 if (ret == length && !memicmpW( buffer, name, length))
1878 strcpy( unix_name + pos,
1879 de[1].d_name[0] ? de[1].d_name : de[0].d_name );
1880 RtlLeaveCriticalSection( &dir_section );
1881 close( fd );
1882 goto success;
1884 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
1886 RtlLeaveCriticalSection( &dir_section );
1887 close( fd );
1888 goto not_found;
1892 RtlLeaveCriticalSection( &dir_section );
1893 close( fd );
1895 /* fall through to normal handling */
1897 #endif /* VFAT_IOCTL_READDIR_BOTH */
1899 if (!(dir = opendir( unix_name )))
1901 if (errno == ENOENT) return STATUS_OBJECT_PATH_NOT_FOUND;
1902 else return FILE_GetNtStatus();
1904 unix_name[pos - 1] = '/';
1905 str.Buffer = buffer;
1906 str.MaximumLength = sizeof(buffer);
1907 while ((de = readdir( dir )))
1909 ret = ntdll_umbstowcs( 0, de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
1910 if (ret == length && !memicmpW( buffer, name, length ))
1912 strcpy( unix_name + pos, de->d_name );
1913 closedir( dir );
1914 goto success;
1917 if (!is_name_8_dot_3) continue;
1919 str.Length = ret * sizeof(WCHAR);
1920 if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
1922 WCHAR short_nameW[12];
1923 ret = hash_short_file_name( &str, short_nameW );
1924 if (ret == length && !memicmpW( short_nameW, name, length ))
1926 strcpy( unix_name + pos, de->d_name );
1927 closedir( dir );
1928 goto success;
1932 closedir( dir );
1933 goto not_found; /* avoid warning */
1935 not_found:
1936 unix_name[pos - 1] = 0;
1937 return STATUS_OBJECT_PATH_NOT_FOUND;
1939 success:
1940 if (is_win_dir && !stat( unix_name, &st )) *is_win_dir = is_same_file( &windir, &st );
1941 return STATUS_SUCCESS;
1945 #ifndef _WIN64
1947 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
1948 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
1949 static const WCHAR driversstoreW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','s','t','o','r','e',0};
1950 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
1951 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
1952 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
1953 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
1954 static const WCHAR syswow64W[] = {'s','y','s','w','o','w','6','4',0};
1955 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
1956 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
1957 static const WCHAR wow_regeditW[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
1959 static struct
1961 const WCHAR *source;
1962 const WCHAR *dos_target;
1963 const char *unix_target;
1964 } redirects[] =
1966 { catrootW, NULL, NULL },
1967 { catroot2W, NULL, NULL },
1968 { driversstoreW, NULL, NULL },
1969 { driversetcW, NULL, NULL },
1970 { logfilesW, NULL, NULL },
1971 { spoolW, NULL, NULL },
1972 { system32W, syswow64W, NULL },
1973 { sysnativeW, system32W, NULL },
1974 { regeditW, wow_regeditW, NULL }
1977 static unsigned int nb_redirects;
1980 /***********************************************************************
1981 * get_redirect_target
1983 * Find the target unix name for a redirected dir.
1985 static const char *get_redirect_target( const char *windows_dir, const WCHAR *name )
1987 int used_default, len, pos, win_len = strlen( windows_dir );
1988 char *unix_name, *unix_target = NULL;
1989 NTSTATUS status;
1991 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, win_len + MAX_DIR_ENTRY_LEN + 2 )))
1992 return NULL;
1993 memcpy( unix_name, windows_dir, win_len );
1994 pos = win_len;
1996 while (*name)
1998 const WCHAR *end, *next;
2000 for (end = name; *end; end++) if (IS_SEPARATOR(*end)) break;
2001 for (next = end; *next; next++) if (!IS_SEPARATOR(*next)) break;
2003 status = find_file_in_dir( unix_name, pos, name, end - name, FALSE, NULL );
2004 if (status == STATUS_OBJECT_PATH_NOT_FOUND && !*next) /* not finding last element is ok */
2006 len = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2007 MAX_DIR_ENTRY_LEN - (pos - win_len), NULL, &used_default );
2008 if (len > 0 && !used_default)
2010 unix_name[pos] = '/';
2011 pos += len + 1;
2012 unix_name[pos] = 0;
2013 break;
2016 if (status) goto done;
2017 pos += strlen( unix_name + pos );
2018 name = next;
2021 if ((unix_target = RtlAllocateHeap( GetProcessHeap(), 0, pos - win_len )))
2022 memcpy( unix_target, unix_name + win_len + 1, pos - win_len );
2024 done:
2025 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2026 return unix_target;
2030 /***********************************************************************
2031 * init_redirects
2033 static void init_redirects(void)
2035 UNICODE_STRING nt_name;
2036 ANSI_STRING unix_name;
2037 NTSTATUS status;
2038 struct stat st;
2039 unsigned int i;
2041 if (!RtlDosPathNameToNtPathName_U( windows_dir.Buffer, &nt_name, NULL, NULL ))
2043 ERR( "can't convert %s\n", debugstr_us(&windows_dir) );
2044 return;
2046 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN_IF, FALSE );
2047 RtlFreeUnicodeString( &nt_name );
2048 if (status)
2050 ERR( "cannot open %s (%x)\n", debugstr_us(&windows_dir), status );
2051 return;
2053 if (!stat( unix_name.Buffer, &st ))
2055 windir.dev = st.st_dev;
2056 windir.ino = st.st_ino;
2057 nb_redirects = sizeof(redirects) / sizeof(redirects[0]);
2058 for (i = 0; i < nb_redirects; i++)
2060 if (!redirects[i].dos_target) continue;
2061 redirects[i].unix_target = get_redirect_target( unix_name.Buffer, redirects[i].dos_target );
2062 TRACE( "%s -> %s\n", debugstr_w(redirects[i].source), redirects[i].unix_target );
2065 RtlFreeAnsiString( &unix_name );
2070 /***********************************************************************
2071 * match_redirect
2073 * Check if path matches a redirect name. If yes, return matched length.
2075 static int match_redirect( const WCHAR *path, int len, const WCHAR *redir, int check_case )
2077 int i = 0;
2079 while (i < len && *redir)
2081 if (IS_SEPARATOR(path[i]))
2083 if (*redir++ != '\\') return 0;
2084 while (i < len && IS_SEPARATOR(path[i])) i++;
2085 continue; /* move on to next path component */
2087 else if (check_case)
2089 if (path[i] != *redir) return 0;
2091 else
2093 if (tolowerW(path[i]) != tolowerW(*redir)) return 0;
2095 i++;
2096 redir++;
2098 if (*redir) return 0;
2099 if (i < len && !IS_SEPARATOR(path[i])) return 0;
2100 while (i < len && IS_SEPARATOR(path[i])) i++;
2101 return i;
2105 /***********************************************************************
2106 * get_redirect_path
2108 * Retrieve the Unix path corresponding to a redirected path if any.
2110 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, int check_case )
2112 unsigned int i;
2113 int len;
2115 for (i = 0; i < nb_redirects; i++)
2117 if ((len = match_redirect( name, length, redirects[i].source, check_case )))
2119 if (!redirects[i].unix_target) break;
2120 unix_name[pos++] = '/';
2121 strcpy( unix_name + pos, redirects[i].unix_target );
2122 return len;
2125 return 0;
2128 #else /* _WIN64 */
2130 /* there are no redirects on 64-bit */
2132 static const unsigned int nb_redirects = 0;
2134 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, int check_case )
2136 return 0;
2139 #endif
2141 /***********************************************************************
2142 * DIR_init_windows_dir
2144 void DIR_init_windows_dir( const WCHAR *win, const WCHAR *sys )
2146 /* FIXME: should probably store paths as NT file names */
2148 RtlCreateUnicodeString( &windows_dir, win );
2149 RtlCreateUnicodeString( &system_dir, sys );
2151 #ifndef _WIN64
2152 if (is_wow64) init_redirects();
2153 #endif
2157 /******************************************************************************
2158 * get_dos_device
2160 * Get the Unix path of a DOS device.
2162 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *unix_name_ret )
2164 const char *config_dir = wine_get_config_dir();
2165 struct stat st;
2166 char *unix_name, *new_name, *dev;
2167 unsigned int i;
2168 int unix_len;
2170 /* make sure the device name is ASCII */
2171 for (i = 0; i < name_len; i++)
2172 if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
2174 unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
2176 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2177 return STATUS_NO_MEMORY;
2179 strcpy( unix_name, config_dir );
2180 strcat( unix_name, "/dosdevices/" );
2181 dev = unix_name + strlen(unix_name);
2183 for (i = 0; i < name_len; i++) dev[i] = (char)tolowerW(name[i]);
2184 dev[i] = 0;
2186 /* special case for drive devices */
2187 if (name_len == 2 && dev[1] == ':')
2189 dev[i++] = ':';
2190 dev[i] = 0;
2193 for (;;)
2195 if (!stat( unix_name, &st ))
2197 TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
2198 unix_name_ret->Buffer = unix_name;
2199 unix_name_ret->Length = strlen(unix_name);
2200 unix_name_ret->MaximumLength = unix_len;
2201 return STATUS_SUCCESS;
2203 if (!dev) break;
2205 /* now try some defaults for it */
2206 if (!strcmp( dev, "aux" ))
2208 strcpy( dev, "com1" );
2209 continue;
2211 if (!strcmp( dev, "prn" ))
2213 strcpy( dev, "lpt1" );
2214 continue;
2216 if (!strcmp( dev, "nul" ))
2218 strcpy( unix_name, "/dev/null" );
2219 dev = NULL; /* last try */
2220 continue;
2223 new_name = NULL;
2224 if (dev[1] == ':' && dev[2] == ':') /* drive device */
2226 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
2227 new_name = get_default_drive_device( unix_name );
2229 else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( atoi(dev + 3 ));
2230 else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( atoi(dev + 3 ));
2232 if (!new_name) break;
2234 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2235 unix_name = new_name;
2236 unix_len = strlen(unix_name) + 1;
2237 dev = NULL; /* last try */
2239 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2240 return STATUS_BAD_DEVICE_TYPE;
2244 /* return the length of the DOS namespace prefix if any */
2245 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
2247 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
2248 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2250 if (name->Length > sizeof(nt_prefixW) &&
2251 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
2252 return sizeof(nt_prefixW) / sizeof(WCHAR);
2254 if (name->Length > sizeof(dosdev_prefixW) &&
2255 !memicmpW( name->Buffer, dosdev_prefixW, sizeof(dosdev_prefixW)/sizeof(WCHAR) ))
2256 return sizeof(dosdev_prefixW) / sizeof(WCHAR);
2258 return 0;
2262 /******************************************************************************
2263 * find_file_id
2265 * Recursively search directories from the dir queue for a given inode.
2267 static NTSTATUS find_file_id( ANSI_STRING *unix_name, ULONGLONG file_id, dev_t dev )
2269 unsigned int pos;
2270 DIR *dir;
2271 struct dirent *de;
2272 NTSTATUS status;
2273 struct stat st;
2275 while (!(status = next_dir_in_queue( unix_name->Buffer )))
2277 if (!(dir = opendir( unix_name->Buffer ))) continue;
2278 TRACE( "searching %s for %s\n", unix_name->Buffer, wine_dbgstr_longlong(file_id) );
2279 pos = strlen( unix_name->Buffer );
2280 if (pos + MAX_DIR_ENTRY_LEN >= unix_name->MaximumLength/sizeof(WCHAR))
2282 char *new = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name->Buffer,
2283 unix_name->MaximumLength * 2 );
2284 if (!new)
2286 closedir( dir );
2287 return STATUS_NO_MEMORY;
2289 unix_name->MaximumLength *= 2;
2290 unix_name->Buffer = new;
2292 unix_name->Buffer[pos++] = '/';
2293 while ((de = readdir( dir )))
2295 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2296 strcpy( unix_name->Buffer + pos, de->d_name );
2297 if (lstat( unix_name->Buffer, &st ) == -1) continue;
2298 if (st.st_dev != dev) continue;
2299 if (st.st_ino == file_id)
2301 closedir( dir );
2302 return STATUS_SUCCESS;
2304 if (!S_ISDIR( st.st_mode )) continue;
2305 if ((status = add_dir_to_queue( unix_name->Buffer )) != STATUS_SUCCESS)
2307 closedir( dir );
2308 return status;
2311 closedir( dir );
2313 return status;
2317 /******************************************************************************
2318 * file_id_to_unix_file_name
2320 * Lookup a file from its file id instead of its name.
2322 NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name )
2324 enum server_fd_type type;
2325 int old_cwd, root_fd, needs_close;
2326 NTSTATUS status;
2327 ULONGLONG file_id;
2328 struct stat st, root_st;
2330 if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
2331 if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
2332 memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
2334 unix_name->MaximumLength = 2 * MAX_DIR_ENTRY_LEN + 4;
2335 if (!(unix_name->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, unix_name->MaximumLength )))
2336 return STATUS_NO_MEMORY;
2337 strcpy( unix_name->Buffer, "." );
2339 if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
2340 goto done;
2342 if (type != FD_TYPE_DIR)
2344 status = STATUS_OBJECT_TYPE_MISMATCH;
2345 goto done;
2348 fstat( root_fd, &root_st );
2349 if (root_st.st_ino == file_id) /* shortcut for "." */
2351 status = STATUS_SUCCESS;
2352 goto done;
2355 RtlEnterCriticalSection( &dir_section );
2356 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
2358 /* shortcut for ".." */
2359 if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
2361 strcpy( unix_name->Buffer, ".." );
2362 status = STATUS_SUCCESS;
2364 else
2366 status = add_dir_to_queue( "." );
2367 if (!status)
2368 status = find_file_id( unix_name, file_id, root_st.st_dev );
2369 if (!status) /* get rid of "./" prefix */
2370 memmove( unix_name->Buffer, unix_name->Buffer + 2, strlen(unix_name->Buffer) - 1 );
2371 flush_dir_queue();
2373 if (fchdir( old_cwd ) == -1) chdir( "/" );
2375 else status = FILE_GetNtStatus();
2376 RtlLeaveCriticalSection( &dir_section );
2377 if (old_cwd != -1) close( old_cwd );
2379 done:
2380 if (status == STATUS_SUCCESS)
2382 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name->Buffer) );
2383 unix_name->Length = strlen( unix_name->Buffer );
2385 else
2387 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
2388 RtlFreeHeap( GetProcessHeap(), 0, unix_name->Buffer );
2390 if (needs_close) close( root_fd );
2391 return status;
2395 /******************************************************************************
2396 * lookup_unix_name
2398 * Helper for nt_to_unix_file_name
2400 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
2401 UINT disposition, BOOLEAN check_case )
2403 NTSTATUS status;
2404 int ret, used_default, len;
2405 struct stat st;
2406 char *unix_name = *buffer;
2407 const BOOL redirect = nb_redirects && ntdll_get_thread_data()->wow64_redir;
2409 /* try a shortcut first */
2411 ret = ntdll_wcstoumbs( 0, name, name_len, unix_name + pos, unix_len - pos - 1,
2412 NULL, &used_default );
2414 while (name_len && IS_SEPARATOR(*name))
2416 name++;
2417 name_len--;
2420 if (ret >= 0 && !used_default) /* if we used the default char the name didn't convert properly */
2422 char *p;
2423 unix_name[pos + ret] = 0;
2424 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
2425 if (!redirect || (!strstr( unix_name, "/windows/") && strncmp( unix_name, "windows/", 8 )))
2427 if (!stat( unix_name, &st ))
2429 /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */
2430 if (disposition == FILE_CREATE)
2431 return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED;
2432 return STATUS_SUCCESS;
2437 if (!name_len) /* empty name -> drive root doesn't exist */
2438 return STATUS_OBJECT_PATH_NOT_FOUND;
2439 if (check_case && !redirect && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
2440 return STATUS_OBJECT_NAME_NOT_FOUND;
2442 /* now do it component by component */
2444 while (name_len)
2446 const WCHAR *end, *next;
2447 int is_win_dir = 0;
2449 end = name;
2450 while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
2451 next = end;
2452 while (next < name + name_len && IS_SEPARATOR(*next)) next++;
2453 name_len -= next - name;
2455 /* grow the buffer if needed */
2457 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
2459 char *new_name;
2460 unix_len += 2 * MAX_DIR_ENTRY_LEN;
2461 if (!(new_name = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name, unix_len )))
2462 return STATUS_NO_MEMORY;
2463 unix_name = *buffer = new_name;
2466 status = find_file_in_dir( unix_name, pos, name, end - name,
2467 check_case, redirect ? &is_win_dir : NULL );
2469 /* if this is the last element, not finding it is not necessarily fatal */
2470 if (!name_len)
2472 if (status == STATUS_OBJECT_PATH_NOT_FOUND)
2474 status = STATUS_OBJECT_NAME_NOT_FOUND;
2475 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
2477 ret = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2478 MAX_DIR_ENTRY_LEN, NULL, &used_default );
2479 if (ret > 0 && !used_default)
2481 unix_name[pos] = '/';
2482 unix_name[pos + 1 + ret] = 0;
2483 status = STATUS_NO_SUCH_FILE;
2484 break;
2488 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
2490 status = STATUS_OBJECT_NAME_COLLISION;
2494 if (status != STATUS_SUCCESS) break;
2496 pos += strlen( unix_name + pos );
2497 name = next;
2499 if (is_win_dir && (len = get_redirect_path( unix_name, pos, name, name_len, check_case )))
2501 name += len;
2502 name_len -= len;
2503 pos += strlen( unix_name + pos );
2504 TRACE( "redirecting -> %s + %s\n", debugstr_a(unix_name), debugstr_w(name) );
2508 return status;
2512 /******************************************************************************
2513 * nt_to_unix_file_name_attr
2515 NTSTATUS nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name_ret,
2516 UINT disposition )
2518 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
2519 enum server_fd_type type;
2520 int old_cwd, root_fd, needs_close;
2521 const WCHAR *name, *p;
2522 char *unix_name;
2523 int name_len, unix_len;
2524 NTSTATUS status;
2525 BOOLEAN check_case = !(attr->Attributes & OBJ_CASE_INSENSITIVE);
2527 if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
2528 return wine_nt_to_unix_file_name( attr->ObjectName, unix_name_ret, disposition, check_case );
2530 name = attr->ObjectName->Buffer;
2531 name_len = attr->ObjectName->Length / sizeof(WCHAR);
2533 if (name_len && IS_SEPARATOR(name[0])) return STATUS_INVALID_PARAMETER;
2535 /* check for invalid characters */
2536 for (p = name; p < name + name_len; p++)
2537 if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
2539 unix_len = ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
2540 unix_len += MAX_DIR_ENTRY_LEN + 3;
2541 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2542 return STATUS_NO_MEMORY;
2543 unix_name[0] = '.';
2545 if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
2547 if (type != FD_TYPE_DIR)
2549 if (needs_close) close( root_fd );
2550 status = STATUS_BAD_DEVICE_TYPE;
2552 else
2554 RtlEnterCriticalSection( &dir_section );
2555 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
2557 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1,
2558 disposition, check_case );
2559 if (fchdir( old_cwd ) == -1) chdir( "/" );
2561 else status = FILE_GetNtStatus();
2562 RtlLeaveCriticalSection( &dir_section );
2563 if (old_cwd != -1) close( old_cwd );
2564 if (needs_close) close( root_fd );
2567 else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
2569 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
2571 TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
2572 unix_name_ret->Buffer = unix_name;
2573 unix_name_ret->Length = strlen(unix_name);
2574 unix_name_ret->MaximumLength = unix_len;
2576 else
2578 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
2579 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2581 return status;
2585 /******************************************************************************
2586 * wine_nt_to_unix_file_name (NTDLL.@) Not a Windows API
2588 * Convert a file name from NT namespace to Unix namespace.
2590 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
2591 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
2592 * returned, but the unix name is still filled in properly.
2594 NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
2595 UINT disposition, BOOLEAN check_case )
2597 static const WCHAR unixW[] = {'u','n','i','x'};
2598 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
2600 NTSTATUS status = STATUS_SUCCESS;
2601 const char *config_dir = wine_get_config_dir();
2602 const WCHAR *name, *p;
2603 struct stat st;
2604 char *unix_name;
2605 int pos, ret, name_len, unix_len, prefix_len, used_default;
2606 WCHAR prefix[MAX_DIR_ENTRY_LEN];
2607 BOOLEAN is_unix = FALSE;
2609 name = nameW->Buffer;
2610 name_len = nameW->Length / sizeof(WCHAR);
2612 if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
2614 if (!(pos = get_dos_prefix_len( nameW )))
2615 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
2617 name += pos;
2618 name_len -= pos;
2620 /* check for sub-directory */
2621 for (pos = 0; pos < name_len; pos++)
2623 if (IS_SEPARATOR(name[pos])) break;
2624 if (name[pos] < 32 || strchrW( invalid_charsW, name[pos] ))
2625 return STATUS_OBJECT_NAME_INVALID;
2627 if (pos > MAX_DIR_ENTRY_LEN)
2628 return STATUS_OBJECT_NAME_INVALID;
2630 if (pos == name_len) /* no subdir, plain DOS device */
2631 return get_dos_device( name, name_len, unix_name_ret );
2633 for (prefix_len = 0; prefix_len < pos; prefix_len++)
2634 prefix[prefix_len] = tolowerW(name[prefix_len]);
2636 name += prefix_len;
2637 name_len -= prefix_len;
2639 /* check for invalid characters (all chars except 0 are valid for unix) */
2640 is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
2641 if (is_unix)
2643 for (p = name; p < name + name_len; p++)
2644 if (!*p) return STATUS_OBJECT_NAME_INVALID;
2645 check_case = TRUE;
2647 else
2649 for (p = name; p < name + name_len; p++)
2650 if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
2653 unix_len = ntdll_wcstoumbs( 0, prefix, prefix_len, NULL, 0, NULL, NULL );
2654 unix_len += ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
2655 unix_len += MAX_DIR_ENTRY_LEN + 3;
2656 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
2657 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2658 return STATUS_NO_MEMORY;
2659 strcpy( unix_name, config_dir );
2660 strcat( unix_name, "/dosdevices/" );
2661 pos = strlen(unix_name);
2663 ret = ntdll_wcstoumbs( 0, prefix, prefix_len, unix_name + pos, unix_len - pos - 1,
2664 NULL, &used_default );
2665 if (!ret || used_default)
2667 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2668 return STATUS_OBJECT_NAME_INVALID;
2670 pos += ret;
2672 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
2674 if (prefix_len != 2 || prefix[1] != ':')
2676 unix_name[pos] = 0;
2677 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
2679 if (!is_unix)
2681 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2682 return STATUS_BAD_DEVICE_TYPE;
2684 pos = 0; /* fall back to unix root */
2688 status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, check_case );
2689 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
2691 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
2692 unix_name_ret->Buffer = unix_name;
2693 unix_name_ret->Length = strlen(unix_name);
2694 unix_name_ret->MaximumLength = unix_len;
2696 else
2698 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
2699 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2701 return status;
2705 /******************************************************************
2706 * RtlWow64EnableFsRedirection (NTDLL.@)
2708 NTSTATUS WINAPI RtlWow64EnableFsRedirection( BOOLEAN enable )
2710 if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
2711 ntdll_get_thread_data()->wow64_redir = enable;
2712 return STATUS_SUCCESS;
2716 /******************************************************************
2717 * RtlWow64EnableFsRedirectionEx (NTDLL.@)
2719 NTSTATUS WINAPI RtlWow64EnableFsRedirectionEx( ULONG disable, ULONG *old_value )
2721 if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
2722 *old_value = !ntdll_get_thread_data()->wow64_redir;
2723 ntdll_get_thread_data()->wow64_redir = !disable;
2724 return STATUS_SUCCESS;
2728 /******************************************************************
2729 * RtlDoesFileExists_U (NTDLL.@)
2731 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
2733 UNICODE_STRING nt_name;
2734 FILE_BASIC_INFORMATION basic_info;
2735 OBJECT_ATTRIBUTES attr;
2736 BOOLEAN ret;
2738 if (!RtlDosPathNameToNtPathName_U( file_name, &nt_name, NULL, NULL )) return FALSE;
2740 attr.Length = sizeof(attr);
2741 attr.RootDirectory = 0;
2742 attr.ObjectName = &nt_name;
2743 attr.Attributes = OBJ_CASE_INSENSITIVE;
2744 attr.SecurityDescriptor = NULL;
2745 attr.SecurityQualityOfService = NULL;
2747 ret = NtQueryAttributesFile(&attr, &basic_info) == STATUS_SUCCESS;
2749 RtlFreeUnicodeString( &nt_name );
2750 return ret;
2754 /***********************************************************************
2755 * DIR_unmount_device
2757 * Unmount the specified device.
2759 NTSTATUS DIR_unmount_device( HANDLE handle )
2761 NTSTATUS status;
2762 int unix_fd, needs_close;
2764 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
2766 struct stat st;
2767 char *mount_point = NULL;
2769 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
2770 status = STATUS_INVALID_PARAMETER;
2771 else
2773 if ((mount_point = get_device_mount_point( st.st_rdev )))
2775 #ifdef __APPLE__
2776 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
2777 #else
2778 static const char umount[] = "umount >/dev/null 2>&1 ";
2779 #endif
2780 char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount));
2781 if (cmd)
2783 strcpy( cmd, umount );
2784 strcat( cmd, mount_point );
2785 system( cmd );
2786 RtlFreeHeap( GetProcessHeap(), 0, cmd );
2787 #ifdef linux
2788 /* umount will fail to release the loop device since we still have
2789 a handle to it, so we release it here */
2790 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
2791 #endif
2793 RtlFreeHeap( GetProcessHeap(), 0, mount_point );
2796 if (needs_close) close( unix_fd );
2798 return status;
2802 /******************************************************************************
2803 * DIR_get_unix_cwd
2805 * Retrieve the Unix name of the current directory; helper for wine_unix_to_nt_file_name.
2806 * Returned value must be freed by caller.
2808 NTSTATUS DIR_get_unix_cwd( char **cwd )
2810 int old_cwd, unix_fd, needs_close;
2811 CURDIR *curdir;
2812 HANDLE handle;
2813 NTSTATUS status;
2815 RtlAcquirePebLock();
2817 if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */
2818 curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
2819 else
2820 curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
2822 if (!(handle = curdir->Handle))
2824 UNICODE_STRING dirW;
2825 OBJECT_ATTRIBUTES attr;
2826 IO_STATUS_BLOCK io;
2828 if (!RtlDosPathNameToNtPathName_U( curdir->DosPath.Buffer, &dirW, NULL, NULL ))
2830 status = STATUS_OBJECT_NAME_INVALID;
2831 goto done;
2833 attr.Length = sizeof(attr);
2834 attr.RootDirectory = 0;
2835 attr.Attributes = OBJ_CASE_INSENSITIVE;
2836 attr.ObjectName = &dirW;
2837 attr.SecurityDescriptor = NULL;
2838 attr.SecurityQualityOfService = NULL;
2840 status = NtOpenFile( &handle, 0, &attr, &io, 0,
2841 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
2842 RtlFreeUnicodeString( &dirW );
2843 if (status != STATUS_SUCCESS) goto done;
2846 if ((status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )) == STATUS_SUCCESS)
2848 RtlEnterCriticalSection( &dir_section );
2850 if ((old_cwd = open(".", O_RDONLY)) != -1 && fchdir( unix_fd ) != -1)
2852 unsigned int size = 512;
2854 for (;;)
2856 if (!(*cwd = RtlAllocateHeap( GetProcessHeap(), 0, size )))
2858 status = STATUS_NO_MEMORY;
2859 break;
2861 if (getcwd( *cwd, size )) break;
2862 RtlFreeHeap( GetProcessHeap(), 0, *cwd );
2863 if (errno != ERANGE)
2865 status = STATUS_OBJECT_PATH_INVALID;
2866 break;
2868 size *= 2;
2870 if (fchdir( old_cwd ) == -1) chdir( "/" );
2872 else status = FILE_GetNtStatus();
2874 RtlLeaveCriticalSection( &dir_section );
2875 if (old_cwd != -1) close( old_cwd );
2876 if (needs_close) close( unix_fd );
2878 if (!curdir->Handle) NtClose( handle );
2880 done:
2881 RtlReleasePebLock();
2882 return status;
2885 struct read_changes_info
2887 HANDLE FileHandle;
2888 PVOID Buffer;
2889 ULONG BufferSize;
2890 PIO_APC_ROUTINE apc;
2891 void *apc_arg;
2894 /* callback for ioctl user APC */
2895 static void WINAPI read_changes_user_apc( void *arg, IO_STATUS_BLOCK *io, ULONG reserved )
2897 struct read_changes_info *info = arg;
2898 if (info->apc) info->apc( info->apc_arg, io, reserved );
2899 RtlFreeHeap( GetProcessHeap(), 0, info );
2902 static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, void **apc )
2904 struct read_changes_info *info = user;
2905 char path[PATH_MAX];
2906 NTSTATUS ret = STATUS_SUCCESS;
2907 int len, action, i;
2909 SERVER_START_REQ( read_change )
2911 req->handle = wine_server_obj_handle( info->FileHandle );
2912 wine_server_set_reply( req, path, PATH_MAX );
2913 ret = wine_server_call( req );
2914 action = reply->action;
2915 len = wine_server_reply_size( reply );
2917 SERVER_END_REQ;
2919 if (ret == STATUS_SUCCESS && info->Buffer &&
2920 (info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
2922 PFILE_NOTIFY_INFORMATION pfni;
2924 pfni = info->Buffer;
2926 /* convert to an NT style path */
2927 for (i=0; i<len; i++)
2928 if (path[i] == '/')
2929 path[i] = '\\';
2931 len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
2932 info->BufferSize - sizeof (*pfni) );
2934 pfni->NextEntryOffset = 0;
2935 pfni->Action = action;
2936 pfni->FileNameLength = len * sizeof (WCHAR);
2937 pfni->FileName[len] = 0;
2938 len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
2940 else
2942 ret = STATUS_NOTIFY_ENUM_DIR;
2943 len = 0;
2946 iosb->u.Status = ret;
2947 iosb->Information = len;
2948 *apc = read_changes_user_apc;
2949 return ret;
2952 #define FILE_NOTIFY_ALL ( \
2953 FILE_NOTIFY_CHANGE_FILE_NAME | \
2954 FILE_NOTIFY_CHANGE_DIR_NAME | \
2955 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
2956 FILE_NOTIFY_CHANGE_SIZE | \
2957 FILE_NOTIFY_CHANGE_LAST_WRITE | \
2958 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
2959 FILE_NOTIFY_CHANGE_CREATION | \
2960 FILE_NOTIFY_CHANGE_SECURITY )
2962 /******************************************************************************
2963 * NtNotifyChangeDirectoryFile [NTDLL.@]
2965 NTSTATUS WINAPI
2966 NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
2967 PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
2968 PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
2969 ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
2971 struct read_changes_info *info;
2972 NTSTATUS status;
2973 ULONG_PTR cvalue = ApcRoutine ? 0 : (ULONG_PTR)ApcContext;
2975 TRACE("%p %p %p %p %p %p %u %u %d\n",
2976 FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
2977 Buffer, BufferSize, CompletionFilter, WatchTree );
2979 if (!IoStatusBlock)
2980 return STATUS_ACCESS_VIOLATION;
2982 if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
2983 return STATUS_INVALID_PARAMETER;
2985 info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof *info );
2986 if (!info)
2987 return STATUS_NO_MEMORY;
2989 info->FileHandle = FileHandle;
2990 info->Buffer = Buffer;
2991 info->BufferSize = BufferSize;
2992 info->apc = ApcRoutine;
2993 info->apc_arg = ApcContext;
2995 SERVER_START_REQ( read_directory_changes )
2997 req->filter = CompletionFilter;
2998 req->want_data = (Buffer != NULL);
2999 req->subtree = WatchTree;
3000 req->async.handle = wine_server_obj_handle( FileHandle );
3001 req->async.callback = wine_server_client_ptr( read_changes_apc );
3002 req->async.iosb = wine_server_client_ptr( IoStatusBlock );
3003 req->async.arg = wine_server_client_ptr( info );
3004 req->async.event = wine_server_obj_handle( Event );
3005 req->async.cvalue = cvalue;
3006 status = wine_server_call( req );
3008 SERVER_END_REQ;
3010 if (status != STATUS_PENDING)
3011 RtlFreeHeap( GetProcessHeap(), 0, info );
3013 return status;