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
24 #include "wine/port.h"
27 #include <sys/types.h>
41 #ifdef HAVE_SYS_STAT_H
42 # include <sys/stat.h>
44 #ifdef HAVE_SYS_IOCTL_H
45 #include <sys/ioctl.h>
47 #ifdef HAVE_LINUX_IOCTL_H
48 #include <linux/ioctl.h>
50 #ifdef HAVE_LINUX_MAJOR_H
51 # include <linux/major.h>
53 #ifdef HAVE_SYS_PARAM_H
54 #include <sys/param.h>
56 #ifdef HAVE_SYS_MOUNT_H
57 #include <sys/mount.h>
64 #define NONAMELESSUNION
65 #define NONAMELESSSTRUCT
67 #define WIN32_NO_STATUS
71 #include "ntdll_misc.h"
72 #include "wine/unicode.h"
73 #include "wine/server.h"
74 #include "wine/library.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(file);
80 #undef VFAT_IOCTL_READDIR_BOTH
85 /* We want the real kernel dirent structure, not the libc one */
90 unsigned short d_reclen;
94 /* Define the VFAT ioctl to get both short and long file names */
95 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
98 # define O_DIRECTORY 0200000 /* must be directory */
107 unsigned short d_reclen;
108 unsigned char d_type;
112 static inline int getdents64( int fd, char *de, unsigned int size )
115 __asm__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
117 : "0" (220 /*NR_getdents64*/), "r" (fd), "c" (de), "d" (size)
132 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
133 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
135 #define INVALID_NT_CHARS '*','?','<','>','|','"'
136 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
138 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
140 #define MAX_IGNORED_FILES 4
148 static struct file_identity ignored_files[MAX_IGNORED_FILES];
149 static int ignored_files_count;
151 union file_directory_info
154 FILE_BOTH_DIRECTORY_INFORMATION both;
157 static const unsigned int max_dir_info_size = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[MAX_DIR_ENTRY_LEN] );
159 static int show_dot_files = -1;
161 /* at some point we may want to allow Winelib apps to set this */
162 static const int is_case_sensitive = FALSE;
164 UNICODE_STRING windows_dir = { 0, 0, NULL }; /* windows directory */
165 UNICODE_STRING system_dir = { 0, 0, NULL }; /* system directory */
167 static struct file_identity windir;
169 static RTL_CRITICAL_SECTION dir_section;
170 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
173 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
174 0, 0, { (DWORD_PTR)(__FILE__ ": dir_section") }
176 static RTL_CRITICAL_SECTION dir_section = { &critsect_debug, -1, 0, 0, 0, 0 };
179 /* check if a given Unicode char is OK in a DOS short name */
180 static inline BOOL is_invalid_dos_char( WCHAR ch )
182 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
183 if (ch > 0x7f) return TRUE;
184 return strchrW( invalid_chars, ch ) != NULL;
187 /* check if the device can be a mounted volume */
188 static inline int is_valid_mounted_device( const struct stat *st )
190 #if defined(linux) || defined(__sun__)
191 return S_ISBLK( st->st_mode );
193 /* disks are char devices on *BSD */
194 return S_ISCHR( st->st_mode );
198 static inline void ignore_file( const char *name )
201 assert( ignored_files_count < MAX_IGNORED_FILES );
202 if (!stat( name, &st ))
204 ignored_files[ignored_files_count].dev = st.st_dev;
205 ignored_files[ignored_files_count].ino = st.st_ino;
206 ignored_files_count++;
210 static inline BOOL is_same_file( const struct file_identity *file, const struct stat *st )
212 return st->st_dev == file->dev && st->st_ino == file->ino;
215 static inline BOOL is_ignored_file( const struct stat *st )
219 for (i = 0; i < ignored_files_count; i++)
220 if (is_same_file( &ignored_files[i], st )) return TRUE;
224 /***********************************************************************
225 * get_default_com_device
227 * Return the default device to use for serial ports.
229 static char *get_default_com_device( int num )
233 if (!num || num > 9) return ret;
235 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/ttyS0") );
238 strcpy( ret, "/dev/ttyS0" );
239 ret[strlen(ret) - 1] = '0' + num - 1;
241 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
242 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/cuad0") );
245 strcpy( ret, "/dev/cuad0" );
246 ret[strlen(ret) - 1] = '0' + num - 1;
249 FIXME( "no known default for device com%d\n", num );
255 /***********************************************************************
256 * get_default_lpt_device
258 * Return the default device to use for parallel ports.
260 static char *get_default_lpt_device( int num )
264 if (!num || num > 9) return ret;
266 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/lp0") );
269 strcpy( ret, "/dev/lp0" );
270 ret[strlen(ret) - 1] = '0' + num - 1;
273 FIXME( "no known default for device lpt%d\n", num );
279 /***********************************************************************
280 * DIR_get_drives_info
282 * Retrieve device/inode number for all the drives. Helper for find_drive_root.
284 unsigned int DIR_get_drives_info( struct drive_info info[MAX_DOS_DRIVES] )
286 static struct drive_info cache[MAX_DOS_DRIVES];
287 static time_t last_update;
288 static unsigned int nb_drives;
290 time_t now = time(NULL);
292 RtlEnterCriticalSection( &dir_section );
293 if (now != last_update)
295 const char *config_dir = wine_get_config_dir();
300 if ((buffer = RtlAllocateHeap( GetProcessHeap(), 0,
301 strlen(config_dir) + sizeof("/dosdevices/a:") )))
303 strcpy( buffer, config_dir );
304 strcat( buffer, "/dosdevices/a:" );
305 p = buffer + strlen(buffer) - 2;
307 for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
310 if (!stat( buffer, &st ))
312 cache[i].dev = st.st_dev;
313 cache[i].ino = st.st_ino;
322 RtlFreeHeap( GetProcessHeap(), 0, buffer );
326 memcpy( info, cache, sizeof(cache) );
328 RtlLeaveCriticalSection( &dir_section );
333 /***********************************************************************
334 * parse_mount_entries
336 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
340 #include <sys/vfstab.h>
341 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
347 while (! getvfsent( f, &entry ))
349 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
350 if (!strcmp( entry.vfs_fstype, "nfs" ) ||
351 !strcmp( entry.vfs_fstype, "smbfs" ) ||
352 !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
354 if (stat( entry.vfs_mountp, &st ) == -1) continue;
355 if (st.st_dev != dev || st.st_ino != ino) continue;
356 if (!strcmp( entry.vfs_fstype, "fd" ))
358 if ((device = strstr( entry.vfs_mntopts, "dev=" )))
360 char *p = strchr( device + 4, ',' );
366 return entry.vfs_special;
373 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
375 struct mntent *entry;
379 while ((entry = getmntent( f )))
381 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
382 if (!strcmp( entry->mnt_type, "nfs" ) ||
383 !strcmp( entry->mnt_type, "smbfs" ) ||
384 !strcmp( entry->mnt_type, "ncpfs" )) continue;
386 if (stat( entry->mnt_dir, &st ) == -1) continue;
387 if (st.st_dev != dev || st.st_ino != ino) continue;
388 if (!strcmp( entry->mnt_type, "supermount" ))
390 if ((device = strstr( entry->mnt_opts, "dev=" )))
392 char *p = strchr( device + 4, ',' );
397 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
399 /* if device is a regular file check for a loop mount */
400 if ((device = strstr( entry->mnt_opts, "loop=" )))
402 char *p = strchr( device + 5, ',' );
408 return entry->mnt_fsname;
414 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
416 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
421 while ((entry = getfsent()))
423 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
424 if (!strcmp( entry->fs_vfstype, "nfs" ) ||
425 !strcmp( entry->fs_vfstype, "smbfs" ) ||
426 !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
428 if (stat( entry->fs_file, &st ) == -1) continue;
429 if (st.st_dev != dev || st.st_ino != ino) continue;
430 return entry->fs_spec;
437 #include <sys/mnttab.h>
438 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
445 while (( ! getmntent( f, &entry) ))
447 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
448 if (!strcmp( entry.mnt_fstype, "nfs" ) ||
449 !strcmp( entry.mnt_fstype, "smbfs" ) ||
450 !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
452 if (stat( entry.mnt_mountp, &st ) == -1) continue;
453 if (st.st_dev != dev || st.st_ino != ino) continue;
454 if (!strcmp( entry.mnt_fstype, "fd" ))
456 if ((device = strstr( entry.mnt_mntopts, "dev=" )))
458 char *p = strchr( device + 4, ',' );
464 return entry.mnt_special;
470 /***********************************************************************
471 * get_default_drive_device
473 * Return the default device to use for a given drive mount point.
475 static char *get_default_drive_device( const char *root )
485 /* try to open it first to force it to get mounted */
486 if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
488 res = fstat( fd, &st );
491 /* now try normal stat just in case */
492 if (res == -1) res = stat( root, &st );
493 if (res == -1) return NULL;
495 RtlEnterCriticalSection( &dir_section );
497 if ((f = fopen( "/etc/mtab", "r" )))
499 device = parse_mount_entries( f, st.st_dev, st.st_ino );
502 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
503 if (!device && (f = fopen( "/etc/fstab", "r" )))
505 device = parse_mount_entries( f, st.st_dev, st.st_ino );
510 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
511 if (ret) strcpy( ret, device );
513 RtlLeaveCriticalSection( &dir_section );
515 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ )
520 /* try to open it first to force it to get mounted */
521 if ((fd = open( root, O_RDONLY )) != -1)
523 res = fstat( fd, &st );
526 /* now try normal stat just in case */
527 if (res == -1) res = stat( root, &st );
528 if (res == -1) return NULL;
530 RtlEnterCriticalSection( &dir_section );
532 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
533 * pass NULL. Leave the argument in for symmetry.
535 device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
538 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
539 if (ret) strcpy( ret, device );
541 RtlLeaveCriticalSection( &dir_section );
549 /* try to open it first to force it to get mounted */
550 if ((fd = open( root, O_RDONLY )) != -1)
552 res = fstat( fd, &st );
555 /* now try normal stat just in case */
556 if (res == -1) res = stat( root, &st );
557 if (res == -1) return NULL;
559 RtlEnterCriticalSection( &dir_section );
561 if ((f = fopen( "/etc/mnttab", "r" )))
563 device = parse_mount_entries( f, st.st_dev, st.st_ino);
566 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
567 if (!device && (f = fopen( "/etc/vfstab", "r" )))
569 device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
574 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
575 if (ret) strcpy( ret, device );
577 RtlLeaveCriticalSection( &dir_section );
579 #elif defined(__APPLE__)
580 struct statfs *mntStat;
586 static const char path_bsd_device[] = "/dev/disk";
589 res = stat( root, &st );
590 if (res == -1) return NULL;
595 RtlEnterCriticalSection( &dir_section );
597 mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
599 for (i = 0; i < mntSize && !ret; i++)
601 if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
602 if (st.st_dev != dev || st.st_ino != ino) continue;
604 /* FIXME add support for mounted network drive */
605 if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
607 /* set return value to the corresponding raw BSD node */
608 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
611 strcpy(ret, "/dev/r");
612 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
616 RtlLeaveCriticalSection( &dir_section );
619 if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
625 /***********************************************************************
626 * get_device_mount_point
628 * Return the current mount point for a device.
630 static char *get_device_mount_point( dev_t dev )
637 RtlEnterCriticalSection( &dir_section );
639 if ((f = fopen( "/etc/mtab", "r" )))
641 struct mntent *entry;
645 while ((entry = getmntent( f )))
647 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
648 if (!strcmp( entry->mnt_type, "nfs" ) ||
649 !strcmp( entry->mnt_type, "smbfs" ) ||
650 !strcmp( entry->mnt_type, "ncpfs" )) continue;
652 if (!strcmp( entry->mnt_type, "supermount" ))
654 if ((device = strstr( entry->mnt_opts, "dev=" )))
657 if ((p = strchr( device, ',' ))) *p = 0;
660 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
662 /* if device is a regular file check for a loop mount */
663 if ((device = strstr( entry->mnt_opts, "loop=" )))
666 if ((p = strchr( device, ',' ))) *p = 0;
669 else device = entry->mnt_fsname;
671 if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
673 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry->mnt_dir) + 1 );
674 if (ret) strcpy( ret, entry->mnt_dir );
680 RtlLeaveCriticalSection( &dir_section );
681 #elif defined(__APPLE__)
682 struct statfs *entry;
686 RtlEnterCriticalSection( &dir_section );
688 size = getmntinfo( &entry, MNT_NOWAIT );
689 for (i = 0; i < size; i++)
691 if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
692 if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
694 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry[i].f_mntfromname) + 1 );
695 if (ret) strcpy( ret, entry[i].f_mntfromname );
699 RtlLeaveCriticalSection( &dir_section );
702 if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
708 /***********************************************************************
711 * Initialize the show_dot_files options.
713 static void init_options(void)
715 static const WCHAR WineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e',0};
716 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
720 OBJECT_ATTRIBUTES attr;
721 UNICODE_STRING nameW;
725 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
726 attr.Length = sizeof(attr);
727 attr.RootDirectory = root;
728 attr.ObjectName = &nameW;
730 attr.SecurityDescriptor = NULL;
731 attr.SecurityQualityOfService = NULL;
732 RtlInitUnicodeString( &nameW, WineW );
734 /* @@ Wine registry key: HKCU\Software\Wine */
735 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
737 RtlInitUnicodeString( &nameW, ShowDotFilesW );
738 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
740 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
741 show_dot_files = IS_OPTION_TRUE( str[0] );
747 /* a couple of directories that we don't want to return in directory searches */
748 ignore_file( wine_get_config_dir() );
749 ignore_file( "/dev" );
750 ignore_file( "/proc" );
752 ignore_file( "/sys" );
757 /***********************************************************************
760 * Check if the specified file should be hidden based on its name and the show dot files option.
762 BOOL DIR_is_hidden_file( const UNICODE_STRING *name )
766 if (show_dot_files == -1) init_options();
767 if (show_dot_files) return FALSE;
769 end = p = name->Buffer + name->Length/sizeof(WCHAR);
770 while (p > name->Buffer && IS_SEPARATOR(p[-1])) p--;
771 while (p > name->Buffer && !IS_SEPARATOR(p[-1])) p--;
772 if (p == end || *p != '.') return FALSE;
773 /* make sure it isn't '.' or '..' */
774 if (p + 1 == end) return FALSE;
775 if (p[1] == '.' && p + 2 == end) return FALSE;
780 /***********************************************************************
781 * hash_short_file_name
783 * Transform a Unix file name into a hashed DOS name. If the name is a valid
784 * DOS name, it is converted to upper-case; otherwise it is replaced by a
785 * hashed version that fits in 8.3 format.
786 * 'buffer' must be at least 12 characters long.
787 * Returns length of short name in bytes; short name is NOT null-terminated.
789 static ULONG hash_short_file_name( const UNICODE_STRING *name, LPWSTR buffer )
791 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
793 LPCWSTR p, ext, end = name->Buffer + name->Length / sizeof(WCHAR);
798 /* Compute the hash code of the file name */
799 /* If you know something about hash functions, feel free to */
800 /* insert a better algorithm here... */
801 if (!is_case_sensitive)
803 for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
804 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
805 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
809 for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
810 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
811 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
814 /* Find last dot for start of the extension */
815 for (p = name->Buffer + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
817 /* Copy first 4 chars, replacing invalid chars with '_' */
818 for (i = 4, p = name->Buffer, dst = buffer; i > 0; i--, p++)
820 if (p == end || p == ext) break;
821 *dst++ = is_invalid_dos_char(*p) ? '_' : toupperW(*p);
823 /* Pad to 5 chars with '~' */
824 while (i-- >= 0) *dst++ = '~';
826 /* Insert hash code converted to 3 ASCII chars */
827 *dst++ = hash_chars[(hash >> 10) & 0x1f];
828 *dst++ = hash_chars[(hash >> 5) & 0x1f];
829 *dst++ = hash_chars[hash & 0x1f];
831 /* Copy the first 3 chars of the extension (if any) */
835 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
836 *dst++ = is_invalid_dos_char(*ext) ? '_' : toupperW(*ext);
842 /***********************************************************************
845 * Check a long file name against a mask.
847 * Tests (done in W95 DOS shell - case insensitive):
848 * *.txt test1.test.txt *
850 * *.t??????.t* test1.ta.tornado.txt *
851 * *tornado* test1.ta.tornado.txt *
852 * t*t test1.ta.tornado.txt *
854 * ?est??? test1.txt -
855 * *test1.txt* test1.txt *
856 * h?l?o*t.dat hellothisisatest.dat *
858 static BOOLEAN match_filename( const UNICODE_STRING *name_str, const UNICODE_STRING *mask_str )
861 const WCHAR *name = name_str->Buffer;
862 const WCHAR *mask = mask_str->Buffer;
863 const WCHAR *name_end = name + name_str->Length / sizeof(WCHAR);
864 const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
865 const WCHAR *lastjoker = NULL;
866 const WCHAR *next_to_retry = NULL;
868 TRACE("(%s, %s)\n", debugstr_us(name_str), debugstr_us(mask_str));
870 while (name < name_end && mask < mask_end)
876 while (mask < mask_end && *mask == '*') mask++; /* Skip consecutive '*' */
877 if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
880 /* skip to the next match after the joker(s) */
881 if (is_case_sensitive)
882 while (name < name_end && (*name != *mask)) name++;
884 while (name < name_end && (toupperW(*name) != toupperW(*mask))) name++;
885 next_to_retry = name;
892 if (is_case_sensitive) mismatch = (*mask != *name);
893 else mismatch = (toupperW(*mask) != toupperW(*name));
899 if (mask == mask_end)
901 if (name == name_end) return TRUE;
902 if (lastjoker) mask = lastjoker;
905 else /* mismatch ! */
907 if (lastjoker) /* we had an '*', so we can try unlimitedly */
911 /* this scan sequence was a mismatch, so restart
912 * 1 char after the first char we checked last time */
914 name = next_to_retry;
916 else return FALSE; /* bad luck */
921 while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
922 mask++; /* Ignore trailing '.' or '*' in mask */
923 return (name == name_end && mask == mask_end);
927 /***********************************************************************
930 * helper for NtQueryDirectoryFile
932 static union file_directory_info *append_entry( void *info_ptr, IO_STATUS_BLOCK *io, ULONG max_length,
933 const char *long_name, const char *short_name,
934 const UNICODE_STRING *mask )
936 union file_directory_info *info;
937 int i, long_len, short_len, total_len;
939 WCHAR long_nameW[MAX_DIR_ENTRY_LEN];
940 WCHAR short_nameW[12];
942 ULONG attributes = 0;
944 io->u.Status = STATUS_SUCCESS;
945 long_len = ntdll_umbstowcs( 0, long_name, strlen(long_name), long_nameW, MAX_DIR_ENTRY_LEN );
946 if (long_len == -1) return NULL;
948 str.Buffer = long_nameW;
949 str.Length = long_len * sizeof(WCHAR);
950 str.MaximumLength = sizeof(long_nameW);
954 short_len = ntdll_umbstowcs( 0, short_name, strlen(short_name),
955 short_nameW, sizeof(short_nameW) / sizeof(WCHAR) );
956 if (short_len == -1) short_len = sizeof(short_nameW) / sizeof(WCHAR);
958 else /* generate a short name if necessary */
963 if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
964 short_len = hash_short_file_name( &str, short_nameW );
967 TRACE( "long %s short %s mask %s\n",
968 debugstr_us(&str), debugstr_wn(short_nameW, short_len), debugstr_us(mask) );
970 if (mask && !match_filename( &str, mask ))
972 if (!short_len) return NULL; /* no short name to match */
973 str.Buffer = short_nameW;
974 str.Length = short_len * sizeof(WCHAR);
975 str.MaximumLength = sizeof(short_nameW);
976 if (!match_filename( &str, mask )) return NULL;
979 if (lstat( long_name, &st ) == -1) return NULL;
980 if (S_ISLNK( st.st_mode ))
982 if (stat( long_name, &st ) == -1) return NULL;
983 if (S_ISDIR( st.st_mode )) attributes |= FILE_ATTRIBUTE_REPARSE_POINT;
985 if (is_ignored_file( &st ))
987 TRACE( "ignoring file %s\n", long_name );
990 if (!show_dot_files && long_name[0] == '.' && long_name[1] && (long_name[1] != '.' || long_name[2]))
991 attributes |= FILE_ATTRIBUTE_HIDDEN;
993 total_len = (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[long_len]) + 3) & ~3;
994 if (io->Information + total_len > max_length)
996 total_len = max_length - io->Information;
997 io->u.Status = STATUS_BUFFER_OVERFLOW;
1000 info = (union file_directory_info *)((char *)info_ptr + io->Information);
1001 fill_stat_info( &st, &info->both, FileBothDirectoryInformation );
1002 info->both.NextEntryOffset = total_len;
1003 info->both.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
1004 info->both.FileAttributes |= attributes;
1005 info->both.EaSize = 0; /* FIXME */
1006 info->both.ShortNameLength = short_len * sizeof(WCHAR);
1007 for (i = 0; i < short_len; i++) info->both.ShortName[i] = toupperW(short_nameW[i]);
1008 info->both.FileNameLength = long_len * sizeof(WCHAR);
1009 memcpy( info->both.FileName, long_nameW,
1010 min( info->both.FileNameLength,
1011 total_len - FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName )));
1013 io->Information += total_len;
1018 #ifdef VFAT_IOCTL_READDIR_BOTH
1020 /***********************************************************************
1023 * Wrapper for the VFAT ioctl to work around various kernel bugs.
1024 * dir_section must be held by caller.
1026 static KERNEL_DIRENT *start_vfat_ioctl( int fd )
1028 static KERNEL_DIRENT *de;
1033 const size_t page_size = getpagesize();
1034 SIZE_T size = 2 * sizeof(*de) + page_size;
1037 if (NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_RESERVE, PAGE_READWRITE ))
1039 /* commit only the size needed for the dir entries */
1040 /* this leaves an extra unaccessible page, which should make the kernel */
1041 /* fail with -EFAULT before it stomps all over our memory */
1043 size = 2 * sizeof(*de);
1044 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_COMMIT, PAGE_READWRITE );
1047 /* set d_reclen to 65535 to work around an AFS kernel bug */
1048 de[0].d_reclen = 65535;
1049 res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
1052 if (errno != ENOENT) return NULL; /* VFAT ioctl probably not supported */
1053 de[0].d_reclen = 0; /* eof */
1055 else if (!res && de[0].d_reclen == 65535) return NULL; /* AFS bug */
1061 /***********************************************************************
1062 * read_directory_vfat
1064 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
1066 static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1067 BOOLEAN single_entry, const UNICODE_STRING *mask,
1068 BOOLEAN restart_scan )
1073 union file_directory_info *info, *last_info = NULL;
1075 io->u.Status = STATUS_SUCCESS;
1077 if (restart_scan) lseek( fd, 0, SEEK_SET );
1079 if (length < max_dir_info_size) /* we may have to return a partial entry here */
1081 off_t old_pos = lseek( fd, 0, SEEK_CUR );
1083 if (!(de = start_vfat_ioctl( fd ))) return -1; /* not supported */
1085 while (de[0].d_reclen)
1087 /* make sure names are null-terminated to work around an x86-64 kernel bug */
1088 len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1089 de[0].d_name[len] = 0;
1090 len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1091 de[1].d_name[len] = 0;
1093 if (de[1].d_name[0])
1094 info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask );
1096 info = append_entry( buffer, io, length, de[0].d_name, NULL, mask );
1100 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1101 lseek( fd, old_pos, SEEK_SET ); /* restore pos to previous entry */
1104 old_pos = lseek( fd, 0, SEEK_CUR );
1105 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
1108 else /* we'll only return full entries, no need to worry about overflow */
1110 if (!(de = start_vfat_ioctl( fd ))) return -1; /* not supported */
1112 while (de[0].d_reclen)
1114 /* make sure names are null-terminated to work around an x86-64 kernel bug */
1115 len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1116 de[0].d_name[len] = 0;
1117 len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1118 de[1].d_name[len] = 0;
1120 if (de[1].d_name[0])
1121 info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask );
1123 info = append_entry( buffer, io, length, de[0].d_name, NULL, mask );
1127 if (single_entry) break;
1128 /* check if we still have enough space for the largest possible entry */
1129 if (io->Information + max_dir_info_size > length) break;
1131 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
1135 if (last_info) last_info->next = 0;
1136 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1139 #endif /* VFAT_IOCTL_READDIR_BOTH */
1142 /***********************************************************************
1143 * read_directory_getdents
1145 * Read a directory using the Linux getdents64 system call; helper for NtQueryDirectoryFile.
1148 static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1149 BOOLEAN single_entry, const UNICODE_STRING *mask,
1150 BOOLEAN restart_scan )
1153 size_t size = length;
1154 int res, fake_dot_dot = 1;
1155 char *data, local_buffer[8192];
1156 KERNEL_DIRENT64 *de;
1157 union file_directory_info *info, *last_info = NULL;
1159 if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1161 size = sizeof(local_buffer);
1162 data = local_buffer;
1165 if (restart_scan) lseek( fd, 0, SEEK_SET );
1166 else if (length < max_dir_info_size) /* we may have to return a partial entry here */
1168 old_pos = lseek( fd, 0, SEEK_CUR );
1169 if (old_pos == -1 && errno == ENOENT)
1171 io->u.Status = STATUS_NO_MORE_FILES;
1177 io->u.Status = STATUS_SUCCESS;
1179 res = getdents64( fd, data, size );
1182 if (errno != ENOSYS)
1184 io->u.Status = FILE_GetNtStatus();
1190 de = (KERNEL_DIRENT64 *)data;
1194 /* check if we got . and .. from getdents */
1197 if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
1199 KERNEL_DIRENT64 *next_de = (KERNEL_DIRENT64 *)(data + de->d_reclen);
1200 if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
1203 /* make sure we have enough room for both entries */
1206 static const ULONG min_info_size = (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[1]) +
1207 FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[2]) + 3) & ~3;
1208 if (length < min_info_size || single_entry)
1210 FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
1217 if ((info = append_entry( buffer, io, length, ".", NULL, mask )))
1219 if ((info = append_entry( buffer, io, length, "..", NULL, mask )))
1222 /* check if we still have enough space for the largest possible entry */
1223 if (last_info && io->Information + max_dir_info_size > length)
1225 lseek( fd, 0, SEEK_SET ); /* reset pos to first entry */
1233 res -= de->d_reclen;
1235 !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
1236 (info = append_entry( buffer, io, length, de->d_name, NULL, mask )))
1239 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1241 lseek( fd, old_pos, SEEK_SET ); /* restore pos to previous entry */
1244 /* check if we still have enough space for the largest possible entry */
1245 if (single_entry || io->Information + max_dir_info_size > length)
1247 if (res > 0) lseek( fd, de->d_off, SEEK_SET ); /* set pos to next entry */
1251 old_pos = de->d_off;
1252 /* move on to the next entry */
1253 if (res > 0) de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen);
1256 res = getdents64( fd, data, size );
1257 de = (KERNEL_DIRENT64 *)data;
1261 if (last_info) last_info->next = 0;
1262 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1265 if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1269 #elif defined HAVE_GETDIRENTRIES
1271 #if _DARWIN_FEATURE_64_BIT_INODE
1273 /* Darwin doesn't provide a version of getdirentries with support for 64-bit
1274 * inodes. When 64-bit inodes are enabled, the getdirentries symbol is mapped
1275 * to _getdirentries_is_not_available_when_64_bit_inodes_are_in_effect so that
1276 * we get link errors if we try to use it. We still need getdirentries, but we
1277 * don't need it to support 64-bit inodes. So, we use the legacy getdirentries
1278 * with 32-bit inodes. We have to be careful to use a corresponding dirent
1281 int darwin_legacy_getdirentries(int, char *, int, long *) __asm("_getdirentries");
1282 #define getdirentries darwin_legacy_getdirentries
1284 struct darwin_legacy_dirent {
1286 __uint16_t d_reclen;
1289 char d_name[__DARWIN_MAXNAMLEN + 1];
1291 #define dirent darwin_legacy_dirent
1295 /***********************************************************************
1296 * wine_getdirentries
1298 * Wrapper for the BSD getdirentries system call to fix a bug in the
1299 * Mac OS X version. For some file systems (at least Apple Filing
1300 * Protocol a.k.a. AFP), getdirentries resets the file position to 0
1301 * when it's about to return 0 (no more entries). So, a subsequent
1302 * getdirentries call starts over at the beginning again, causing an
1305 static inline int wine_getdirentries(int fd, char *buf, int nbytes, long *basep)
1307 int res = getdirentries(fd, buf, nbytes, basep);
1310 lseek(fd, *basep, SEEK_SET);
1315 /***********************************************************************
1316 * read_directory_getdirentries
1318 * Read a directory using the BSD getdirentries system call; helper for NtQueryDirectoryFile.
1320 static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1321 BOOLEAN single_entry, const UNICODE_STRING *mask,
1322 BOOLEAN restart_scan )
1325 ULONG_PTR restart_info_pos = 0;
1326 size_t size, initial_size = length;
1327 int res, fake_dot_dot = 1;
1328 char *data, local_buffer[8192];
1330 union file_directory_info *info, *last_info = NULL, *restart_last_info = NULL;
1332 size = initial_size;
1333 data = local_buffer;
1334 if (size > sizeof(local_buffer) && !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
1336 io->u.Status = STATUS_NO_MEMORY;
1337 return io->u.Status;
1340 if (restart_scan) lseek( fd, 0, SEEK_SET );
1342 io->u.Status = STATUS_SUCCESS;
1344 /* FIXME: should make sure size is larger than filesystem block size */
1345 res = wine_getdirentries( fd, data, size, &restart_pos );
1348 io->u.Status = FILE_GetNtStatus();
1353 de = (struct dirent *)data;
1357 /* check if we got . and .. from getdirentries */
1360 if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
1362 struct dirent *next_de = (struct dirent *)(data + de->d_reclen);
1363 if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
1366 /* make sure we have enough room for both entries */
1369 static const ULONG min_info_size = (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[1]) +
1370 FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[2]) + 3) & ~3;
1371 if (length < min_info_size || single_entry)
1373 FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
1380 if ((info = append_entry( buffer, io, length, ".", NULL, mask )))
1382 if ((info = append_entry( buffer, io, length, "..", NULL, mask )))
1385 restart_last_info = last_info;
1386 restart_info_pos = io->Information;
1388 /* check if we still have enough space for the largest possible entry */
1389 if (last_info && io->Information + max_dir_info_size > length)
1391 lseek( fd, 0, SEEK_SET ); /* reset pos to first entry */
1399 res -= de->d_reclen;
1401 !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
1402 ((info = append_entry( buffer, io, length, de->d_name, NULL, mask ))))
1405 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1407 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1408 if (restart_info_pos) /* if we have a complete read already, return it */
1410 io->u.Status = STATUS_SUCCESS;
1411 io->Information = restart_info_pos;
1412 last_info = restart_last_info;
1415 /* otherwise restart from the start with a smaller size */
1416 size = (char *)de - data;
1418 io->Information = 0;
1422 /* if we have to return but the buffer contains more data, restart with a smaller size */
1423 if (res > 0 && (single_entry || io->Information + max_dir_info_size > length))
1425 lseek( fd, (unsigned long)restart_pos, SEEK_SET );
1426 size = (char *)de - data;
1427 io->Information = restart_info_pos;
1428 last_info = restart_last_info;
1432 /* move on to the next entry */
1435 de = (struct dirent *)((char *)de + de->d_reclen);
1438 if (size < initial_size) break; /* already restarted once, give up now */
1439 size = min( size, length - io->Information );
1440 /* if size is too small don't bother to continue */
1441 if (size < max_dir_info_size && last_info) break;
1442 restart_last_info = last_info;
1443 restart_info_pos = io->Information;
1445 res = wine_getdirentries( fd, data, size, &restart_pos );
1446 de = (struct dirent *)data;
1449 if (last_info) last_info->next = 0;
1450 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1453 if (data != local_buffer) RtlFreeHeap( GetProcessHeap(), 0, data );
1457 #if _DARWIN_FEATURE_64_BIT_INODE
1458 #undef getdirentries
1462 #endif /* HAVE_GETDIRENTRIES */
1465 /***********************************************************************
1466 * read_directory_readdir
1468 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
1470 static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1471 BOOLEAN single_entry, const UNICODE_STRING *mask,
1472 BOOLEAN restart_scan )
1475 off_t i, old_pos = 0;
1477 union file_directory_info *info, *last_info = NULL;
1479 if (!(dir = opendir( "." )))
1481 io->u.Status = FILE_GetNtStatus();
1487 old_pos = lseek( fd, 0, SEEK_CUR );
1488 /* skip the right number of entries */
1489 for (i = 0; i < old_pos - 2; i++)
1491 if (!readdir( dir ))
1494 io->u.Status = STATUS_NO_MORE_FILES;
1499 io->u.Status = STATUS_SUCCESS;
1504 info = append_entry( buffer, io, length, ".", NULL, mask );
1505 else if (old_pos == 1)
1506 info = append_entry( buffer, io, length, "..", NULL, mask );
1507 else if ((de = readdir( dir )))
1509 if (strcmp( de->d_name, "." ) && strcmp( de->d_name, ".." ))
1510 info = append_entry( buffer, io, length, de->d_name, NULL, mask );
1520 if (io->u.Status == STATUS_BUFFER_OVERFLOW)
1522 old_pos--; /* restore pos to previous entry */
1525 if (single_entry) break;
1526 /* check if we still have enough space for the largest possible entry */
1527 if (io->Information + max_dir_info_size > length) break;
1531 lseek( fd, old_pos, SEEK_SET ); /* store dir offset as filepos for fd */
1534 if (last_info) last_info->next = 0;
1535 else io->u.Status = restart_scan ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES;
1538 /***********************************************************************
1539 * read_directory_stat
1541 * Read a single file from a directory by determining whether the file
1542 * identified by mask exists using stat.
1544 static int read_directory_stat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
1545 BOOLEAN single_entry, const UNICODE_STRING *mask,
1546 BOOLEAN restart_scan )
1548 int unix_len, ret, used_default;
1552 TRACE("trying optimisation for file %s\n", debugstr_us( mask ));
1554 unix_len = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), NULL, 0, NULL, NULL );
1555 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len + 1)))
1557 io->u.Status = STATUS_NO_MEMORY;
1560 ret = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR), unix_name, unix_len,
1561 NULL, &used_default );
1562 if (ret > 0 && !used_default)
1567 lseek( fd, 0, SEEK_SET );
1569 else if (lseek( fd, 0, SEEK_CUR ) != 0)
1571 io->u.Status = STATUS_NO_MORE_FILES;
1576 ret = stat( unix_name, &st );
1579 union file_directory_info *info = append_entry( buffer, io, length, unix_name, NULL, NULL );
1583 if (io->u.Status != STATUS_BUFFER_OVERFLOW) lseek( fd, 1, SEEK_CUR );
1585 else io->u.Status = STATUS_NO_MORE_FILES;
1591 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1593 TRACE("returning %d\n", ret);
1599 static inline WCHAR *mempbrkW( const WCHAR *ptr, const WCHAR *accept, size_t n )
1602 for (end = ptr + n; ptr < end; ptr++) if (strchrW( accept, *ptr )) return (WCHAR *)ptr;
1606 /******************************************************************************
1607 * NtQueryDirectoryFile [NTDLL.@]
1608 * ZwQueryDirectoryFile [NTDLL.@]
1610 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
1611 PIO_APC_ROUTINE apc_routine, PVOID apc_context,
1612 PIO_STATUS_BLOCK io,
1613 PVOID buffer, ULONG length,
1614 FILE_INFORMATION_CLASS info_class,
1615 BOOLEAN single_entry,
1616 PUNICODE_STRING mask,
1617 BOOLEAN restart_scan )
1619 int cwd, fd, needs_close;
1620 static const WCHAR wszWildcards[] = { '*','?',0 };
1622 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
1623 handle, event, apc_routine, apc_context, io, buffer,
1624 length, info_class, single_entry, debugstr_us(mask),
1627 if (length < sizeof(FILE_BOTH_DIR_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
1629 if (event || apc_routine)
1631 FIXME( "Unsupported yet option\n" );
1632 return io->u.Status = STATUS_NOT_IMPLEMENTED;
1634 if (info_class != FileBothDirectoryInformation)
1636 FIXME( "Unsupported file info class %d\n", info_class );
1637 return io->u.Status = STATUS_NOT_IMPLEMENTED;
1640 if ((io->u.Status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
1641 return io->u.Status;
1643 io->Information = 0;
1645 RtlEnterCriticalSection( &dir_section );
1647 if (show_dot_files == -1) init_options();
1649 cwd = open( ".", O_RDONLY );
1650 if (fchdir( fd ) != -1)
1652 #ifdef VFAT_IOCTL_READDIR_BOTH
1653 if ((read_directory_vfat( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1656 if (mask && !mempbrkW( mask->Buffer, wszWildcards, mask->Length / sizeof(WCHAR) ) &&
1657 read_directory_stat( fd, io, buffer, length, single_entry, mask, restart_scan ) != -1)
1660 if ((read_directory_getdents( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1662 #elif defined HAVE_GETDIRENTRIES
1663 if ((read_directory_getdirentries( fd, io, buffer, length, single_entry, mask, restart_scan )) != -1)
1666 read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan );
1669 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
1671 else io->u.Status = FILE_GetNtStatus();
1673 RtlLeaveCriticalSection( &dir_section );
1675 if (needs_close) close( fd );
1676 if (cwd != -1) close( cwd );
1677 TRACE( "=> %x (%ld)\n", io->u.Status, io->Information );
1678 return io->u.Status;
1682 /***********************************************************************
1685 * Find a file in a directory the hard way, by doing a case-insensitive search.
1686 * The file found is appended to unix_name at pos.
1687 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
1689 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
1690 int check_case, int *is_win_dir )
1692 WCHAR buffer[MAX_DIR_ENTRY_LEN];
1698 int ret, used_default, is_name_8_dot_3;
1700 /* try a shortcut for this directory */
1702 unix_name[pos++] = '/';
1703 ret = ntdll_wcstoumbs( 0, name, length, unix_name + pos, MAX_DIR_ENTRY_LEN,
1704 NULL, &used_default );
1705 /* if we used the default char, the Unix name won't round trip properly back to Unicode */
1706 /* so it cannot match the file we are looking for */
1707 if (ret >= 0 && !used_default)
1709 unix_name[pos + ret] = 0;
1710 if (!stat( unix_name, &st ))
1712 if (is_win_dir) *is_win_dir = is_same_file( &windir, &st );
1713 return STATUS_SUCCESS;
1716 if (check_case) goto not_found; /* we want an exact match */
1718 if (pos > 1) unix_name[pos - 1] = 0;
1719 else unix_name[1] = 0; /* keep the initial slash */
1721 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
1723 str.Buffer = (WCHAR *)name;
1724 str.Length = length * sizeof(WCHAR);
1725 str.MaximumLength = str.Length;
1726 is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
1728 /* now look for it through the directory */
1730 #ifdef VFAT_IOCTL_READDIR_BOTH
1731 if (is_name_8_dot_3)
1733 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
1738 RtlEnterCriticalSection( &dir_section );
1739 if ((de = start_vfat_ioctl( fd )))
1741 unix_name[pos - 1] = '/';
1742 while (de[0].d_reclen)
1744 /* make sure names are null-terminated to work around an x86-64 kernel bug */
1745 size_t len = min(de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1746 de[0].d_name[len] = 0;
1747 len = min(de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1748 de[1].d_name[len] = 0;
1750 if (de[1].d_name[0])
1752 ret = ntdll_umbstowcs( 0, de[1].d_name, strlen(de[1].d_name),
1753 buffer, MAX_DIR_ENTRY_LEN );
1754 if (ret == length && !memicmpW( buffer, name, length))
1756 strcpy( unix_name + pos, de[1].d_name );
1757 RtlLeaveCriticalSection( &dir_section );
1762 ret = ntdll_umbstowcs( 0, de[0].d_name, strlen(de[0].d_name),
1763 buffer, MAX_DIR_ENTRY_LEN );
1764 if (ret == length && !memicmpW( buffer, name, length))
1766 strcpy( unix_name + pos,
1767 de[1].d_name[0] ? de[1].d_name : de[0].d_name );
1768 RtlLeaveCriticalSection( &dir_section );
1772 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
1774 RtlLeaveCriticalSection( &dir_section );
1780 RtlLeaveCriticalSection( &dir_section );
1783 /* fall through to normal handling */
1785 #endif /* VFAT_IOCTL_READDIR_BOTH */
1787 if (!(dir = opendir( unix_name )))
1789 if (errno == ENOENT) return STATUS_OBJECT_PATH_NOT_FOUND;
1790 else return FILE_GetNtStatus();
1792 unix_name[pos - 1] = '/';
1793 str.Buffer = buffer;
1794 str.MaximumLength = sizeof(buffer);
1795 while ((de = readdir( dir )))
1797 ret = ntdll_umbstowcs( 0, de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
1798 if (ret == length && !memicmpW( buffer, name, length ))
1800 strcpy( unix_name + pos, de->d_name );
1805 if (!is_name_8_dot_3) continue;
1807 str.Length = ret * sizeof(WCHAR);
1808 if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
1810 WCHAR short_nameW[12];
1811 ret = hash_short_file_name( &str, short_nameW );
1812 if (ret == length && !memicmpW( short_nameW, name, length ))
1814 strcpy( unix_name + pos, de->d_name );
1821 goto not_found; /* avoid warning */
1824 unix_name[pos - 1] = 0;
1825 return STATUS_OBJECT_PATH_NOT_FOUND;
1828 if (is_win_dir && !stat( unix_name, &st )) *is_win_dir = is_same_file( &windir, &st );
1829 return STATUS_SUCCESS;
1835 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
1836 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
1837 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};
1838 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
1839 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
1840 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
1841 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
1842 static const WCHAR syswow64W[] = {'s','y','s','w','o','w','6','4',0};
1843 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
1844 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
1845 static const WCHAR wow_regeditW[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
1849 const WCHAR *source;
1850 const WCHAR *dos_target;
1851 const char *unix_target;
1854 { catrootW, NULL, NULL },
1855 { catroot2W, NULL, NULL },
1856 { driversstoreW, NULL, NULL },
1857 { driversetcW, NULL, NULL },
1858 { logfilesW, NULL, NULL },
1859 { spoolW, NULL, NULL },
1860 { system32W, syswow64W, NULL },
1861 { sysnativeW, system32W, NULL },
1862 { regeditW, wow_regeditW, NULL }
1865 static unsigned int nb_redirects;
1868 /***********************************************************************
1869 * get_redirect_target
1871 * Find the target unix name for a redirected dir.
1873 static const char *get_redirect_target( const char *windows_dir, const WCHAR *name )
1875 int used_default, len, pos, win_len = strlen( windows_dir );
1876 char *unix_name, *unix_target = NULL;
1879 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, win_len + MAX_DIR_ENTRY_LEN + 2 )))
1881 memcpy( unix_name, windows_dir, win_len );
1886 const WCHAR *end, *next;
1888 for (end = name; *end; end++) if (IS_SEPARATOR(*end)) break;
1889 for (next = end; *next; next++) if (!IS_SEPARATOR(*next)) break;
1891 status = find_file_in_dir( unix_name, pos, name, end - name, FALSE, NULL );
1892 if (status == STATUS_OBJECT_PATH_NOT_FOUND && !*next) /* not finding last element is ok */
1894 len = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
1895 MAX_DIR_ENTRY_LEN - (pos - win_len), NULL, &used_default );
1896 if (len > 0 && !used_default)
1898 unix_name[pos] = '/';
1904 if (status) goto done;
1905 pos += strlen( unix_name + pos );
1909 if ((unix_target = RtlAllocateHeap( GetProcessHeap(), 0, pos - win_len )))
1910 memcpy( unix_target, unix_name + win_len + 1, pos - win_len );
1913 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
1918 /***********************************************************************
1921 static void init_redirects(void)
1923 UNICODE_STRING nt_name;
1924 ANSI_STRING unix_name;
1929 if (!RtlDosPathNameToNtPathName_U( windows_dir.Buffer, &nt_name, NULL, NULL ))
1931 ERR( "can't convert %s\n", debugstr_us(&windows_dir) );
1934 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN_IF, FALSE );
1935 RtlFreeUnicodeString( &nt_name );
1938 ERR( "cannot open %s (%x)\n", debugstr_us(&windows_dir), status );
1941 if (!stat( unix_name.Buffer, &st ))
1943 windir.dev = st.st_dev;
1944 windir.ino = st.st_ino;
1945 nb_redirects = sizeof(redirects) / sizeof(redirects[0]);
1946 for (i = 0; i < nb_redirects; i++)
1948 if (!redirects[i].dos_target) continue;
1949 redirects[i].unix_target = get_redirect_target( unix_name.Buffer, redirects[i].dos_target );
1950 TRACE( "%s -> %s\n", debugstr_w(redirects[i].source), redirects[i].unix_target );
1953 RtlFreeAnsiString( &unix_name );
1958 /***********************************************************************
1961 * Check if path matches a redirect name. If yes, return matched length.
1963 static int match_redirect( const WCHAR *path, int len, const WCHAR *redir, int check_case )
1967 while (i < len && *redir)
1969 if (IS_SEPARATOR(path[i]))
1971 if (*redir++ != '\\') return 0;
1972 while (i < len && IS_SEPARATOR(path[i])) i++;
1973 continue; /* move on to next path component */
1975 else if (check_case)
1977 if (path[i] != *redir) return 0;
1981 if (tolowerW(path[i]) != tolowerW(*redir)) return 0;
1986 if (*redir) return 0;
1987 if (i < len && !IS_SEPARATOR(path[i])) return 0;
1988 while (i < len && IS_SEPARATOR(path[i])) i++;
1993 /***********************************************************************
1996 * Retrieve the Unix path corresponding to a redirected path if any.
1998 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, int check_case )
2003 for (i = 0; i < nb_redirects; i++)
2005 if ((len = match_redirect( name, length, redirects[i].source, check_case )))
2007 if (!redirects[i].unix_target) break;
2008 unix_name[pos++] = '/';
2009 strcpy( unix_name + pos, redirects[i].unix_target );
2018 /* there are no redirects on 64-bit */
2020 static const unsigned int nb_redirects = 0;
2022 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, int check_case )
2029 /***********************************************************************
2030 * DIR_init_windows_dir
2032 void DIR_init_windows_dir( const WCHAR *win, const WCHAR *sys )
2034 /* FIXME: should probably store paths as NT file names */
2036 RtlCreateUnicodeString( &windows_dir, win );
2037 RtlCreateUnicodeString( &system_dir, sys );
2040 if (is_wow64) init_redirects();
2045 /******************************************************************************
2048 * Get the Unix path of a DOS device.
2050 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *unix_name_ret )
2052 const char *config_dir = wine_get_config_dir();
2054 char *unix_name, *new_name, *dev;
2058 /* make sure the device name is ASCII */
2059 for (i = 0; i < name_len; i++)
2060 if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
2062 unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
2064 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2065 return STATUS_NO_MEMORY;
2067 strcpy( unix_name, config_dir );
2068 strcat( unix_name, "/dosdevices/" );
2069 dev = unix_name + strlen(unix_name);
2071 for (i = 0; i < name_len; i++) dev[i] = (char)tolowerW(name[i]);
2074 /* special case for drive devices */
2075 if (name_len == 2 && dev[1] == ':')
2083 if (!stat( unix_name, &st ))
2085 TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
2086 unix_name_ret->Buffer = unix_name;
2087 unix_name_ret->Length = strlen(unix_name);
2088 unix_name_ret->MaximumLength = unix_len;
2089 return STATUS_SUCCESS;
2093 /* now try some defaults for it */
2094 if (!strcmp( dev, "aux" ))
2096 strcpy( dev, "com1" );
2099 if (!strcmp( dev, "prn" ))
2101 strcpy( dev, "lpt1" );
2104 if (!strcmp( dev, "nul" ))
2106 strcpy( unix_name, "/dev/null" );
2107 dev = NULL; /* last try */
2112 if (dev[1] == ':' && dev[2] == ':') /* drive device */
2114 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
2115 new_name = get_default_drive_device( unix_name );
2117 else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( atoi(dev + 3 ));
2118 else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( atoi(dev + 3 ));
2120 if (!new_name) break;
2122 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2123 unix_name = new_name;
2124 unix_len = strlen(unix_name) + 1;
2125 dev = NULL; /* last try */
2127 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2128 return STATUS_BAD_DEVICE_TYPE;
2132 /* return the length of the DOS namespace prefix if any */
2133 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
2135 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
2136 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2138 if (name->Length > sizeof(nt_prefixW) &&
2139 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
2140 return sizeof(nt_prefixW) / sizeof(WCHAR);
2142 if (name->Length > sizeof(dosdev_prefixW) &&
2143 !memicmpW( name->Buffer, dosdev_prefixW, sizeof(dosdev_prefixW)/sizeof(WCHAR) ))
2144 return sizeof(dosdev_prefixW) / sizeof(WCHAR);
2150 /******************************************************************************
2151 * wine_nt_to_unix_file_name (NTDLL.@) Not a Windows API
2153 * Convert a file name from NT namespace to Unix namespace.
2155 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
2156 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
2157 * returned, but the unix name is still filled in properly.
2159 NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
2160 UINT disposition, BOOLEAN check_case )
2162 static const WCHAR unixW[] = {'u','n','i','x'};
2163 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
2165 NTSTATUS status = STATUS_SUCCESS;
2166 const char *config_dir = wine_get_config_dir();
2167 const WCHAR *name, *p;
2170 int pos, ret, name_len, unix_len, prefix_len, used_default;
2171 WCHAR prefix[MAX_DIR_ENTRY_LEN];
2172 BOOLEAN is_unix = FALSE;
2173 const BOOL redirect = nb_redirects && ntdll_get_thread_data()->wow64_redir;
2175 name = nameW->Buffer;
2176 name_len = nameW->Length / sizeof(WCHAR);
2178 if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
2180 if (!(pos = get_dos_prefix_len( nameW )))
2181 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
2186 /* check for sub-directory */
2187 for (pos = 0; pos < name_len; pos++)
2189 if (IS_SEPARATOR(name[pos])) break;
2190 if (name[pos] < 32 || strchrW( invalid_charsW, name[pos] ))
2191 return STATUS_OBJECT_NAME_INVALID;
2193 if (pos > MAX_DIR_ENTRY_LEN)
2194 return STATUS_OBJECT_NAME_INVALID;
2196 if (pos == name_len) /* no subdir, plain DOS device */
2197 return get_dos_device( name, name_len, unix_name_ret );
2199 for (prefix_len = 0; prefix_len < pos; prefix_len++)
2200 prefix[prefix_len] = tolowerW(name[prefix_len]);
2203 name_len -= prefix_len;
2205 /* check for invalid characters (all chars except 0 are valid for unix) */
2206 is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
2209 for (p = name; p < name + name_len; p++)
2210 if (!*p) return STATUS_OBJECT_NAME_INVALID;
2215 for (p = name; p < name + name_len; p++)
2216 if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
2219 unix_len = ntdll_wcstoumbs( 0, prefix, prefix_len, NULL, 0, NULL, NULL );
2220 unix_len += ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
2221 unix_len += MAX_DIR_ENTRY_LEN + 3;
2222 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
2223 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2224 return STATUS_NO_MEMORY;
2225 strcpy( unix_name, config_dir );
2226 strcat( unix_name, "/dosdevices/" );
2227 pos = strlen(unix_name);
2229 ret = ntdll_wcstoumbs( 0, prefix, prefix_len, unix_name + pos, unix_len - pos - 1,
2230 NULL, &used_default );
2231 if (!ret || used_default)
2233 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2234 return STATUS_OBJECT_NAME_INVALID;
2238 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
2240 if (prefix_len != 2 || prefix[1] != ':')
2243 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
2247 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2248 return STATUS_BAD_DEVICE_TYPE;
2250 pos = 0; /* fall back to unix root */
2254 /* try a shortcut first */
2256 ret = ntdll_wcstoumbs( 0, name, name_len, unix_name + pos, unix_len - pos - 1,
2257 NULL, &used_default );
2259 while (name_len && IS_SEPARATOR(*name))
2265 if (ret > 0 && !used_default) /* if we used the default char the name didn't convert properly */
2268 unix_name[pos + ret] = 0;
2269 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
2270 if ((!redirect || !strstr( unix_name, "/windows/")) && !stat( unix_name, &st ))
2272 /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */
2273 if (disposition == FILE_CREATE)
2275 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2276 return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED;
2282 if (!name_len) /* empty name -> drive root doesn't exist */
2284 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2285 return STATUS_OBJECT_PATH_NOT_FOUND;
2287 if (check_case && !redirect && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
2289 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2290 return STATUS_OBJECT_NAME_NOT_FOUND;
2293 /* now do it component by component */
2297 const WCHAR *end, *next;
2301 while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
2303 while (next < name + name_len && IS_SEPARATOR(*next)) next++;
2304 name_len -= next - name;
2306 /* grow the buffer if needed */
2308 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
2311 unix_len += 2 * MAX_DIR_ENTRY_LEN;
2312 if (!(new_name = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name, unix_len )))
2314 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2315 return STATUS_NO_MEMORY;
2317 unix_name = new_name;
2320 status = find_file_in_dir( unix_name, pos, name, end - name,
2321 check_case, redirect ? &is_win_dir : NULL );
2323 /* if this is the last element, not finding it is not necessarily fatal */
2326 if (status == STATUS_OBJECT_PATH_NOT_FOUND)
2328 status = STATUS_OBJECT_NAME_NOT_FOUND;
2329 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
2331 ret = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2332 MAX_DIR_ENTRY_LEN, NULL, &used_default );
2333 if (ret > 0 && !used_default)
2335 unix_name[pos] = '/';
2336 unix_name[pos + 1 + ret] = 0;
2337 status = STATUS_NO_SUCH_FILE;
2342 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
2344 status = STATUS_OBJECT_NAME_COLLISION;
2348 if (status != STATUS_SUCCESS)
2350 /* couldn't find it at all, fail */
2351 WARN( "%s not found in %s\n", debugstr_w(name), unix_name );
2352 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2356 pos += strlen( unix_name + pos );
2359 if (is_win_dir && (prefix_len = get_redirect_path( unix_name, pos, name, name_len, check_case )))
2362 name_len -= prefix_len;
2363 pos += strlen( unix_name + pos );
2364 TRACE( "redirecting %s -> %s + %s\n",
2365 debugstr_us(nameW), debugstr_a(unix_name), debugstr_w(name) );
2370 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
2371 unix_name_ret->Buffer = unix_name;
2372 unix_name_ret->Length = strlen(unix_name);
2373 unix_name_ret->MaximumLength = unix_len;
2378 /******************************************************************
2379 * RtlWow64EnableFsRedirection (NTDLL.@)
2381 NTSTATUS WINAPI RtlWow64EnableFsRedirection( BOOLEAN enable )
2383 if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
2384 ntdll_get_thread_data()->wow64_redir = enable;
2385 return STATUS_SUCCESS;
2389 /******************************************************************
2390 * RtlWow64EnableFsRedirectionEx (NTDLL.@)
2392 NTSTATUS WINAPI RtlWow64EnableFsRedirectionEx( ULONG disable, ULONG *old_value )
2394 if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
2395 *old_value = !ntdll_get_thread_data()->wow64_redir;
2396 ntdll_get_thread_data()->wow64_redir = !disable;
2397 return STATUS_SUCCESS;
2401 /******************************************************************
2402 * RtlDoesFileExists_U (NTDLL.@)
2404 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
2406 UNICODE_STRING nt_name;
2407 FILE_BASIC_INFORMATION basic_info;
2408 OBJECT_ATTRIBUTES attr;
2411 if (!RtlDosPathNameToNtPathName_U( file_name, &nt_name, NULL, NULL )) return FALSE;
2413 attr.Length = sizeof(attr);
2414 attr.RootDirectory = 0;
2415 attr.ObjectName = &nt_name;
2416 attr.Attributes = OBJ_CASE_INSENSITIVE;
2417 attr.SecurityDescriptor = NULL;
2418 attr.SecurityQualityOfService = NULL;
2420 ret = NtQueryAttributesFile(&attr, &basic_info) == STATUS_SUCCESS;
2422 RtlFreeUnicodeString( &nt_name );
2427 /***********************************************************************
2428 * DIR_unmount_device
2430 * Unmount the specified device.
2432 NTSTATUS DIR_unmount_device( HANDLE handle )
2435 int unix_fd, needs_close;
2437 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
2440 char *mount_point = NULL;
2442 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
2443 status = STATUS_INVALID_PARAMETER;
2446 if ((mount_point = get_device_mount_point( st.st_rdev )))
2449 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
2451 static const char umount[] = "umount >/dev/null 2>&1 ";
2453 char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount));
2456 strcpy( cmd, umount );
2457 strcat( cmd, mount_point );
2459 RtlFreeHeap( GetProcessHeap(), 0, cmd );
2461 /* umount will fail to release the loop device since we still have
2462 a handle to it, so we release it here */
2463 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
2466 RtlFreeHeap( GetProcessHeap(), 0, mount_point );
2469 if (needs_close) close( unix_fd );
2475 /******************************************************************************
2478 * Retrieve the Unix name of the current directory; helper for wine_unix_to_nt_file_name.
2479 * Returned value must be freed by caller.
2481 NTSTATUS DIR_get_unix_cwd( char **cwd )
2483 int old_cwd, unix_fd, needs_close;
2488 RtlAcquirePebLock();
2490 if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */
2491 curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
2493 curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
2495 if (!(handle = curdir->Handle))
2497 UNICODE_STRING dirW;
2498 OBJECT_ATTRIBUTES attr;
2501 if (!RtlDosPathNameToNtPathName_U( curdir->DosPath.Buffer, &dirW, NULL, NULL ))
2503 status = STATUS_OBJECT_NAME_INVALID;
2506 attr.Length = sizeof(attr);
2507 attr.RootDirectory = 0;
2508 attr.Attributes = OBJ_CASE_INSENSITIVE;
2509 attr.ObjectName = &dirW;
2510 attr.SecurityDescriptor = NULL;
2511 attr.SecurityQualityOfService = NULL;
2513 status = NtOpenFile( &handle, 0, &attr, &io, 0,
2514 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
2515 RtlFreeUnicodeString( &dirW );
2516 if (status != STATUS_SUCCESS) goto done;
2519 if ((status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )) == STATUS_SUCCESS)
2521 RtlEnterCriticalSection( &dir_section );
2523 if ((old_cwd = open(".", O_RDONLY)) != -1 && fchdir( unix_fd ) != -1)
2525 unsigned int size = 512;
2529 if (!(*cwd = RtlAllocateHeap( GetProcessHeap(), 0, size )))
2531 status = STATUS_NO_MEMORY;
2534 if (getcwd( *cwd, size )) break;
2535 RtlFreeHeap( GetProcessHeap(), 0, *cwd );
2536 if (errno != ERANGE)
2538 status = STATUS_OBJECT_PATH_INVALID;
2543 if (fchdir( old_cwd ) == -1) chdir( "/" );
2545 else status = FILE_GetNtStatus();
2547 RtlLeaveCriticalSection( &dir_section );
2548 if (old_cwd != -1) close( old_cwd );
2549 if (needs_close) close( unix_fd );
2551 if (!curdir->Handle) NtClose( handle );
2554 RtlReleasePebLock();
2558 struct read_changes_info
2563 PIO_APC_ROUTINE apc;
2567 /* callback for ioctl user APC */
2568 static void WINAPI read_changes_user_apc( void *arg, IO_STATUS_BLOCK *io, ULONG reserved )
2570 struct read_changes_info *info = arg;
2571 if (info->apc) info->apc( info->apc_arg, io, reserved );
2572 RtlFreeHeap( GetProcessHeap(), 0, info );
2575 static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, void **apc )
2577 struct read_changes_info *info = user;
2578 char path[PATH_MAX];
2579 NTSTATUS ret = STATUS_SUCCESS;
2582 SERVER_START_REQ( read_change )
2584 req->handle = wine_server_obj_handle( info->FileHandle );
2585 wine_server_set_reply( req, path, PATH_MAX );
2586 ret = wine_server_call( req );
2587 action = reply->action;
2588 len = wine_server_reply_size( reply );
2592 if (ret == STATUS_SUCCESS && info->Buffer &&
2593 (info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
2595 PFILE_NOTIFY_INFORMATION pfni;
2597 pfni = info->Buffer;
2599 /* convert to an NT style path */
2600 for (i=0; i<len; i++)
2604 len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
2605 info->BufferSize - sizeof (*pfni) );
2607 pfni->NextEntryOffset = 0;
2608 pfni->Action = action;
2609 pfni->FileNameLength = len * sizeof (WCHAR);
2610 pfni->FileName[len] = 0;
2611 len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
2615 ret = STATUS_NOTIFY_ENUM_DIR;
2619 iosb->u.Status = ret;
2620 iosb->Information = len;
2621 *apc = read_changes_user_apc;
2625 #define FILE_NOTIFY_ALL ( \
2626 FILE_NOTIFY_CHANGE_FILE_NAME | \
2627 FILE_NOTIFY_CHANGE_DIR_NAME | \
2628 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
2629 FILE_NOTIFY_CHANGE_SIZE | \
2630 FILE_NOTIFY_CHANGE_LAST_WRITE | \
2631 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
2632 FILE_NOTIFY_CHANGE_CREATION | \
2633 FILE_NOTIFY_CHANGE_SECURITY )
2635 /******************************************************************************
2636 * NtNotifyChangeDirectoryFile [NTDLL.@]
2639 NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
2640 PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
2641 PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
2642 ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
2644 struct read_changes_info *info;
2646 ULONG_PTR cvalue = ApcRoutine ? 0 : (ULONG_PTR)ApcContext;
2648 TRACE("%p %p %p %p %p %p %u %u %d\n",
2649 FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
2650 Buffer, BufferSize, CompletionFilter, WatchTree );
2653 return STATUS_ACCESS_VIOLATION;
2655 if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
2656 return STATUS_INVALID_PARAMETER;
2658 info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof *info );
2660 return STATUS_NO_MEMORY;
2662 info->FileHandle = FileHandle;
2663 info->Buffer = Buffer;
2664 info->BufferSize = BufferSize;
2665 info->apc = ApcRoutine;
2666 info->apc_arg = ApcContext;
2668 SERVER_START_REQ( read_directory_changes )
2670 req->filter = CompletionFilter;
2671 req->want_data = (Buffer != NULL);
2672 req->subtree = WatchTree;
2673 req->async.handle = wine_server_obj_handle( FileHandle );
2674 req->async.callback = wine_server_client_ptr( read_changes_apc );
2675 req->async.iosb = wine_server_client_ptr( IoStatusBlock );
2676 req->async.arg = wine_server_client_ptr( info );
2677 req->async.event = wine_server_obj_handle( Event );
2678 req->async.cvalue = cvalue;
2679 status = wine_server_call( req );
2683 if (status != STATUS_PENDING)
2684 RtlFreeHeap( GetProcessHeap(), 0, info );