ntdll: Cache the entire directory contents and sort the names before returning files.
[wine.git] / dlls / ntdll / directory.c
blob223b842ed5943786dc476ef2445288004159215d
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_ATTR_H
48 #include <sys/attr.h>
49 #endif
50 #ifdef MAJOR_IN_MKDEV
51 # include <sys/mkdev.h>
52 #elif defined(MAJOR_IN_SYSMACROS)
53 # include <sys/sysmacros.h>
54 #endif
55 #ifdef HAVE_SYS_VNODE_H
56 /* Work around a conflict with Solaris' system list defined in sys/list.h. */
57 #define list SYSLIST
58 #define list_next SYSLIST_NEXT
59 #define list_prev SYSLIST_PREV
60 #define list_head SYSLIST_HEAD
61 #define list_tail SYSLIST_TAIL
62 #define list_move_tail SYSLIST_MOVE_TAIL
63 #define list_remove SYSLIST_REMOVE
64 #include <sys/vnode.h>
65 #undef list
66 #undef list_next
67 #undef list_prev
68 #undef list_head
69 #undef list_tail
70 #undef list_move_tail
71 #undef list_remove
72 #endif
73 #ifdef HAVE_SYS_IOCTL_H
74 #include <sys/ioctl.h>
75 #endif
76 #ifdef HAVE_LINUX_IOCTL_H
77 #include <linux/ioctl.h>
78 #endif
79 #ifdef HAVE_LINUX_MAJOR_H
80 # include <linux/major.h>
81 #endif
82 #ifdef HAVE_SYS_PARAM_H
83 #include <sys/param.h>
84 #endif
85 #ifdef HAVE_SYS_MOUNT_H
86 #include <sys/mount.h>
87 #endif
88 #ifdef HAVE_SYS_STATFS_H
89 #include <sys/statfs.h>
90 #endif
91 #include <time.h>
92 #ifdef HAVE_UNISTD_H
93 # include <unistd.h>
94 #endif
96 #include "ntstatus.h"
97 #define WIN32_NO_STATUS
98 #define NONAMELESSUNION
99 #include "windef.h"
100 #include "winnt.h"
101 #include "winternl.h"
102 #include "ddk/wdm.h"
103 #include "ntdll_misc.h"
104 #include "wine/unicode.h"
105 #include "wine/server.h"
106 #include "wine/list.h"
107 #include "wine/library.h"
108 #include "wine/debug.h"
110 WINE_DEFAULT_DEBUG_CHANNEL(file);
112 /* just in case... */
113 #undef VFAT_IOCTL_READDIR_BOTH
115 #ifdef linux
117 /* We want the real kernel dirent structure, not the libc one */
118 typedef struct
120 long d_ino;
121 long d_off;
122 unsigned short d_reclen;
123 char d_name[256];
124 } KERNEL_DIRENT;
126 /* Define the VFAT ioctl to get both short and long file names */
127 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
129 #ifndef O_DIRECTORY
130 # define O_DIRECTORY 0200000 /* must be directory */
131 #endif
133 #endif /* linux */
135 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
136 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
138 #define INVALID_NT_CHARS '*','?','<','>','|','"'
139 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
141 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
143 #define MAX_IGNORED_FILES 4
145 struct file_identity
147 dev_t dev;
148 ino_t ino;
151 static struct file_identity ignored_files[MAX_IGNORED_FILES];
152 static unsigned int ignored_files_count;
154 union file_directory_info
156 ULONG next;
157 FILE_DIRECTORY_INFORMATION dir;
158 FILE_BOTH_DIRECTORY_INFORMATION both;
159 FILE_FULL_DIRECTORY_INFORMATION full;
160 FILE_ID_BOTH_DIRECTORY_INFORMATION id_both;
161 FILE_ID_FULL_DIRECTORY_INFORMATION id_full;
164 struct dir_data_buffer
166 struct dir_data_buffer *next; /* next buffer in the list */
167 unsigned int size; /* total size of the buffer */
168 unsigned int pos; /* current position in the buffer */
169 char data[1];
172 struct dir_data_names
174 const WCHAR *long_name; /* long file name in Unicode */
175 const WCHAR *short_name; /* short file name in Unicode */
176 const char *unix_name; /* Unix file name in host encoding */
179 struct dir_data
181 unsigned int size; /* size of the names array */
182 unsigned int count; /* count of used entries in the names array */
183 unsigned int pos; /* current reading position in the names array */
184 struct file_identity id; /* directory file identity */
185 struct dir_data_names *names; /* directory file names */
186 struct dir_data_buffer *buffer; /* head of data buffers list */
189 static const unsigned int dir_data_buffer_initial_size = 4096;
190 static const unsigned int dir_data_cache_initial_size = 256;
191 static const unsigned int dir_data_names_initial_size = 64;
193 static struct dir_data **dir_data_cache;
194 static unsigned int dir_data_cache_size;
196 static BOOL show_dot_files;
197 static RTL_RUN_ONCE init_once = RTL_RUN_ONCE_INIT;
199 /* at some point we may want to allow Winelib apps to set this */
200 static const BOOL is_case_sensitive = FALSE;
202 UNICODE_STRING system_dir = { 0, 0, NULL }; /* system directory */
204 static struct file_identity windir;
206 static RTL_CRITICAL_SECTION dir_section;
207 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
209 0, 0, &dir_section,
210 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
211 0, 0, { (DWORD_PTR)(__FILE__ ": dir_section") }
213 static RTL_CRITICAL_SECTION dir_section = { &critsect_debug, -1, 0, 0, 0, 0 };
216 /* check if a given Unicode char is OK in a DOS short name */
217 static inline BOOL is_invalid_dos_char( WCHAR ch )
219 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
220 if (ch > 0x7f) return TRUE;
221 return strchrW( invalid_chars, ch ) != NULL;
224 /* check if the device can be a mounted volume */
225 static inline BOOL is_valid_mounted_device( const struct stat *st )
227 #if defined(linux) || defined(__sun__)
228 return S_ISBLK( st->st_mode );
229 #else
230 /* disks are char devices on *BSD */
231 return S_ISCHR( st->st_mode );
232 #endif
235 static inline void ignore_file( const char *name )
237 struct stat st;
238 assert( ignored_files_count < MAX_IGNORED_FILES );
239 if (!stat( name, &st ))
241 ignored_files[ignored_files_count].dev = st.st_dev;
242 ignored_files[ignored_files_count].ino = st.st_ino;
243 ignored_files_count++;
247 static inline BOOL is_same_file( const struct file_identity *file, const struct stat *st )
249 return st->st_dev == file->dev && st->st_ino == file->ino;
252 static inline BOOL is_ignored_file( const struct stat *st )
254 unsigned int i;
256 for (i = 0; i < ignored_files_count; i++)
257 if (is_same_file( &ignored_files[i], st )) return TRUE;
258 return FALSE;
261 static inline unsigned int dir_info_align( unsigned int len )
263 return (len + 7) & ~7;
266 static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned int len )
268 switch (class)
270 case FileDirectoryInformation:
271 return offsetof( FILE_DIRECTORY_INFORMATION, FileName[len] );
272 case FileBothDirectoryInformation:
273 return offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[len] );
274 case FileFullDirectoryInformation:
275 return offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[len] );
276 case FileIdBothDirectoryInformation:
277 return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[len] );
278 case FileIdFullDirectoryInformation:
279 return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[len] );
280 default:
281 assert(0);
282 return 0;
286 static inline BOOL has_wildcard( const UNICODE_STRING *mask )
288 return (!mask ||
289 memchrW( mask->Buffer, '*', mask->Length / sizeof(WCHAR) ) ||
290 memchrW( mask->Buffer, '?', mask->Length / sizeof(WCHAR) ));
293 /* get space from the current directory data buffer, allocating a new one if necessary */
294 static void *get_dir_data_space( struct dir_data *data, unsigned int size )
296 struct dir_data_buffer *buffer = data->buffer;
297 void *ret;
299 if (!buffer || size >= buffer->size - buffer->pos)
301 unsigned int new_size = buffer ? buffer->size * 2 : dir_data_buffer_initial_size;
302 if (new_size < size) new_size = size;
303 if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0,
304 offsetof( struct dir_data_buffer, data[new_size] ) ))) return NULL;
305 buffer->pos = 0;
306 buffer->size = new_size;
307 buffer->next = data->buffer;
308 data->buffer = buffer;
310 ret = buffer->data + buffer->pos;
311 buffer->pos += size;
312 return ret;
315 /* add a string to the directory data buffer */
316 static const char *add_dir_data_nameA( struct dir_data *data, const char *name )
318 /* keep buffer data WCHAR-aligned */
319 char *ptr = get_dir_data_space( data, (strlen( name ) + sizeof(WCHAR)) & ~(sizeof(WCHAR) - 1) );
320 if (ptr) strcpy( ptr, name );
321 return ptr;
324 /* add a Unicode string to the directory data buffer */
325 static const WCHAR *add_dir_data_nameW( struct dir_data *data, const WCHAR *name )
327 WCHAR *ptr = get_dir_data_space( data, (strlenW( name ) + 1) * sizeof(WCHAR) );
328 if (ptr) strcpyW( ptr, name );
329 return ptr;
332 /* add an entry to the directory names array */
333 static BOOL add_dir_data_names( struct dir_data *data, const WCHAR *long_name,
334 const WCHAR *short_name, const char *unix_name )
336 static const WCHAR empty[1];
337 struct dir_data_names *names = data->names;
339 if (data->count >= data->size)
341 unsigned int new_size = max( data->size * 2, dir_data_names_initial_size );
343 if (names) names = RtlReAllocateHeap( GetProcessHeap(), 0, names, new_size * sizeof(*names) );
344 else names = RtlAllocateHeap( GetProcessHeap(), 0, new_size * sizeof(*names) );
345 if (!names) return FALSE;
346 data->size = new_size;
347 data->names = names;
350 if (short_name[0])
352 if (!(names[data->count].short_name = add_dir_data_nameW( data, short_name ))) return FALSE;
354 else names[data->count].short_name = empty;
356 if (!(names[data->count].long_name = add_dir_data_nameW( data, long_name ))) return FALSE;
357 if (!(names[data->count].unix_name = add_dir_data_nameA( data, unix_name ))) return FALSE;
358 data->count++;
359 return TRUE;
362 /* free the complete directory data structure */
363 static void free_dir_data( struct dir_data *data )
365 struct dir_data_buffer *buffer, *next;
367 if (!data) return;
369 for (buffer = data->buffer; buffer; buffer = next)
371 next = buffer->next;
372 RtlFreeHeap( GetProcessHeap(), 0, buffer );
374 RtlFreeHeap( GetProcessHeap(), 0, data->names );
375 RtlFreeHeap( GetProcessHeap(), 0, data );
379 /* support for a directory queue for filesystem searches */
381 struct dir_name
383 struct list entry;
384 char name[1];
387 static struct list dir_queue = LIST_INIT( dir_queue );
389 static NTSTATUS add_dir_to_queue( const char *name )
391 int len = strlen( name ) + 1;
392 struct dir_name *dir = RtlAllocateHeap( GetProcessHeap(), 0,
393 FIELD_OFFSET( struct dir_name, name[len] ));
394 if (!dir) return STATUS_NO_MEMORY;
395 strcpy( dir->name, name );
396 list_add_tail( &dir_queue, &dir->entry );
397 return STATUS_SUCCESS;
400 static NTSTATUS next_dir_in_queue( char *name )
402 struct list *head = list_head( &dir_queue );
403 if (head)
405 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
406 strcpy( name, dir->name );
407 list_remove( &dir->entry );
408 RtlFreeHeap( GetProcessHeap(), 0, dir );
409 return STATUS_SUCCESS;
411 return STATUS_OBJECT_NAME_NOT_FOUND;
414 static void flush_dir_queue(void)
416 struct list *head;
418 while ((head = list_head( &dir_queue )))
420 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
421 list_remove( &dir->entry );
422 RtlFreeHeap( GetProcessHeap(), 0, dir );
427 /***********************************************************************
428 * get_default_com_device
430 * Return the default device to use for serial ports.
432 static char *get_default_com_device( int num )
434 char *ret = NULL;
436 if (num < 1 || num > 256) return NULL;
437 #ifdef linux
438 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/ttyS256") );
439 if (!ret) return NULL;
440 sprintf( ret, "/dev/ttyS%d", num - 1 );
441 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
442 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/cuau256") );
443 if (!ret) return NULL;
444 sprintf( ret, "/dev/cuau%d", num - 1 );
445 #elif defined(__DragonFly__)
446 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/cuaa256") );
447 if (!ret) return NULL;
448 sprintf( ret, "/dev/cuaa%d", num - 1 );
449 #else
450 FIXME( "no known default for device com%d\n", num );
451 #endif
452 return ret;
456 /***********************************************************************
457 * get_default_lpt_device
459 * Return the default device to use for parallel ports.
461 static char *get_default_lpt_device( int num )
463 char *ret = NULL;
465 if (num < 1 || num > 256) return NULL;
466 #ifdef linux
467 ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/lp256") );
468 if (!ret) return NULL;
469 sprintf( ret, "/dev/lp%d", num - 1 );
470 #else
471 FIXME( "no known default for device lpt%d\n", num );
472 #endif
473 return ret;
476 #ifdef __ANDROID__
478 static char *unescape_field( char *str )
480 char *in, *out;
482 for (in = out = str; *in; in++, out++)
484 *out = *in;
485 if (in[0] == '\\')
487 if (in[1] == '\\')
489 out[0] = '\\';
490 in++;
492 else if (in[1] == '0' && in[2] == '4' && in[3] == '0')
494 out[0] = ' ';
495 in += 3;
497 else if (in[1] == '0' && in[2] == '1' && in[3] == '1')
499 out[0] = '\t';
500 in += 3;
502 else if (in[1] == '0' && in[2] == '1' && in[3] == '2')
504 out[0] = '\n';
505 in += 3;
507 else if (in[1] == '1' && in[2] == '3' && in[3] == '4')
509 out[0] = '\\';
510 in += 3;
514 *out = '\0';
516 return str;
519 static inline char *get_field( char **str )
521 char *ret;
523 ret = strsep( str, " \t" );
524 if (*str) *str += strspn( *str, " \t" );
526 return ret;
528 /************************************************************************
529 * getmntent_replacement
531 * getmntent replacement for Android.
533 * NB returned static buffer is not thread safe; protect with dir_section.
535 static struct mntent *getmntent_replacement( FILE *f )
537 static struct mntent entry;
538 static char buf[4096];
539 char *p, *start;
543 if (!fgets( buf, sizeof(buf), f )) return NULL;
544 p = strchr( buf, '\n' );
545 if (p) *p = '\0';
546 else /* Partially unread line, move file ptr to end */
548 char tmp[1024];
549 while (fgets( tmp, sizeof(tmp), f ))
550 if (strchr( tmp, '\n' )) break;
552 start = buf + strspn( buf, " \t" );
553 } while (start[0] == '\0' || start[0] == '#');
555 p = get_field( &start );
556 entry.mnt_fsname = p ? unescape_field( p ) : (char *)"";
558 p = get_field( &start );
559 entry.mnt_dir = p ? unescape_field( p ) : (char *)"";
561 p = get_field( &start );
562 entry.mnt_type = p ? unescape_field( p ) : (char *)"";
564 p = get_field( &start );
565 entry.mnt_opts = p ? unescape_field( p ) : (char *)"";
567 p = get_field( &start );
568 entry.mnt_freq = p ? atoi(p) : 0;
570 p = get_field( &start );
571 entry.mnt_passno = p ? atoi(p) : 0;
573 return &entry;
575 #define getmntent getmntent_replacement
576 #endif
578 /***********************************************************************
579 * DIR_get_drives_info
581 * Retrieve device/inode number for all the drives. Helper for find_drive_root.
583 unsigned int DIR_get_drives_info( struct drive_info info[MAX_DOS_DRIVES] )
585 static struct drive_info cache[MAX_DOS_DRIVES];
586 static time_t last_update;
587 static unsigned int nb_drives;
588 unsigned int ret;
589 time_t now = time(NULL);
591 RtlEnterCriticalSection( &dir_section );
592 if (now != last_update)
594 const char *config_dir = wine_get_config_dir();
595 char *buffer, *p;
596 struct stat st;
597 unsigned int i;
599 if ((buffer = RtlAllocateHeap( GetProcessHeap(), 0,
600 strlen(config_dir) + sizeof("/dosdevices/a:") )))
602 strcpy( buffer, config_dir );
603 strcat( buffer, "/dosdevices/a:" );
604 p = buffer + strlen(buffer) - 2;
606 for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
608 *p = 'a' + i;
609 if (!stat( buffer, &st ))
611 cache[i].dev = st.st_dev;
612 cache[i].ino = st.st_ino;
613 nb_drives++;
615 else
617 cache[i].dev = 0;
618 cache[i].ino = 0;
621 RtlFreeHeap( GetProcessHeap(), 0, buffer );
623 last_update = now;
625 memcpy( info, cache, sizeof(cache) );
626 ret = nb_drives;
627 RtlLeaveCriticalSection( &dir_section );
628 return ret;
632 /***********************************************************************
633 * parse_mount_entries
635 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
638 #ifdef sun
639 #include <sys/vfstab.h>
640 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
642 struct vfstab entry;
643 struct stat st;
644 char *device;
646 while (! getvfsent( f, &entry ))
648 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
649 if (!strcmp( entry.vfs_fstype, "nfs" ) ||
650 !strcmp( entry.vfs_fstype, "smbfs" ) ||
651 !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
653 if (stat( entry.vfs_mountp, &st ) == -1) continue;
654 if (st.st_dev != dev || st.st_ino != ino) continue;
655 if (!strcmp( entry.vfs_fstype, "fd" ))
657 if ((device = strstr( entry.vfs_mntopts, "dev=" )))
659 char *p = strchr( device + 4, ',' );
660 if (p) *p = 0;
661 return device + 4;
664 else
665 return entry.vfs_special;
667 return NULL;
669 #endif
671 #ifdef linux
672 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
674 struct mntent *entry;
675 struct stat st;
676 char *device;
678 while ((entry = getmntent( f )))
680 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
681 if (!strcmp( entry->mnt_type, "nfs" ) ||
682 !strcmp( entry->mnt_type, "smbfs" ) ||
683 !strcmp( entry->mnt_type, "ncpfs" )) continue;
685 if (stat( entry->mnt_dir, &st ) == -1) continue;
686 if (st.st_dev != dev || st.st_ino != ino) continue;
687 if (!strcmp( entry->mnt_type, "supermount" ))
689 if ((device = strstr( entry->mnt_opts, "dev=" )))
691 char *p = strchr( device + 4, ',' );
692 if (p) *p = 0;
693 return device + 4;
696 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
698 /* if device is a regular file check for a loop mount */
699 if ((device = strstr( entry->mnt_opts, "loop=" )))
701 char *p = strchr( device + 5, ',' );
702 if (p) *p = 0;
703 return device + 5;
706 else
707 return entry->mnt_fsname;
709 return NULL;
711 #endif
713 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
714 #include <fstab.h>
715 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
717 struct fstab *entry;
718 struct stat st;
720 while ((entry = getfsent()))
722 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
723 if (!strcmp( entry->fs_vfstype, "nfs" ) ||
724 !strcmp( entry->fs_vfstype, "smbfs" ) ||
725 !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
727 if (stat( entry->fs_file, &st ) == -1) continue;
728 if (st.st_dev != dev || st.st_ino != ino) continue;
729 return entry->fs_spec;
731 return NULL;
733 #endif
735 #ifdef sun
736 #include <sys/mnttab.h>
737 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
739 struct mnttab entry;
740 struct stat st;
741 char *device;
744 while (( ! getmntent( f, &entry) ))
746 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
747 if (!strcmp( entry.mnt_fstype, "nfs" ) ||
748 !strcmp( entry.mnt_fstype, "smbfs" ) ||
749 !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
751 if (stat( entry.mnt_mountp, &st ) == -1) continue;
752 if (st.st_dev != dev || st.st_ino != ino) continue;
753 if (!strcmp( entry.mnt_fstype, "fd" ))
755 if ((device = strstr( entry.mnt_mntopts, "dev=" )))
757 char *p = strchr( device + 4, ',' );
758 if (p) *p = 0;
759 return device + 4;
762 else
763 return entry.mnt_special;
765 return NULL;
767 #endif
769 /***********************************************************************
770 * get_default_drive_device
772 * Return the default device to use for a given drive mount point.
774 static char *get_default_drive_device( const char *root )
776 char *ret = NULL;
778 #ifdef linux
779 FILE *f;
780 char *device = NULL;
781 int fd, res = -1;
782 struct stat st;
784 /* try to open it first to force it to get mounted */
785 if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
787 res = fstat( fd, &st );
788 close( fd );
790 /* now try normal stat just in case */
791 if (res == -1) res = stat( root, &st );
792 if (res == -1) return NULL;
794 RtlEnterCriticalSection( &dir_section );
796 #ifdef __ANDROID__
797 if ((f = fopen( "/proc/mounts", "r" )))
799 device = parse_mount_entries( f, st.st_dev, st.st_ino );
800 fclose( f );
802 #else
803 if ((f = fopen( "/etc/mtab", "r" )))
805 device = parse_mount_entries( f, st.st_dev, st.st_ino );
806 fclose( f );
808 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
809 if (!device && (f = fopen( "/etc/fstab", "r" )))
811 device = parse_mount_entries( f, st.st_dev, st.st_ino );
812 fclose( f );
814 #endif
815 if (device)
817 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
818 if (ret) strcpy( ret, device );
820 RtlLeaveCriticalSection( &dir_section );
822 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
823 char *device = NULL;
824 int fd, res = -1;
825 struct stat st;
827 /* try to open it first to force it to get mounted */
828 if ((fd = open( root, O_RDONLY )) != -1)
830 res = fstat( fd, &st );
831 close( fd );
833 /* now try normal stat just in case */
834 if (res == -1) res = stat( root, &st );
835 if (res == -1) return NULL;
837 RtlEnterCriticalSection( &dir_section );
839 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
840 * pass NULL. Leave the argument in for symmetry.
842 device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
843 if (device)
845 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
846 if (ret) strcpy( ret, device );
848 RtlLeaveCriticalSection( &dir_section );
850 #elif defined( sun )
851 FILE *f;
852 char *device = NULL;
853 int fd, res = -1;
854 struct stat st;
856 /* try to open it first to force it to get mounted */
857 if ((fd = open( root, O_RDONLY )) != -1)
859 res = fstat( fd, &st );
860 close( fd );
862 /* now try normal stat just in case */
863 if (res == -1) res = stat( root, &st );
864 if (res == -1) return NULL;
866 RtlEnterCriticalSection( &dir_section );
868 if ((f = fopen( "/etc/mnttab", "r" )))
870 device = parse_mount_entries( f, st.st_dev, st.st_ino);
871 fclose( f );
873 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
874 if (!device && (f = fopen( "/etc/vfstab", "r" )))
876 device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
877 fclose( f );
879 if (device)
881 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(device) + 1 );
882 if (ret) strcpy( ret, device );
884 RtlLeaveCriticalSection( &dir_section );
886 #elif defined(__APPLE__)
887 struct statfs *mntStat;
888 struct stat st;
889 int i;
890 int mntSize;
891 dev_t dev;
892 ino_t ino;
893 static const char path_bsd_device[] = "/dev/disk";
894 int res;
896 res = stat( root, &st );
897 if (res == -1) return NULL;
899 dev = st.st_dev;
900 ino = st.st_ino;
902 RtlEnterCriticalSection( &dir_section );
904 mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
906 for (i = 0; i < mntSize && !ret; i++)
908 if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
909 if (st.st_dev != dev || st.st_ino != ino) continue;
911 /* FIXME add support for mounted network drive */
912 if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
914 /* set return value to the corresponding raw BSD node */
915 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
916 if (ret)
918 strcpy(ret, "/dev/r");
919 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
923 RtlLeaveCriticalSection( &dir_section );
924 #else
925 static int warned;
926 if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
927 #endif
928 return ret;
932 /***********************************************************************
933 * get_device_mount_point
935 * Return the current mount point for a device.
937 static char *get_device_mount_point( dev_t dev )
939 char *ret = NULL;
941 #ifdef linux
942 FILE *f;
944 RtlEnterCriticalSection( &dir_section );
946 #ifdef __ANDROID__
947 if ((f = fopen( "/proc/mounts", "r" )))
948 #else
949 if ((f = fopen( "/etc/mtab", "r" )))
950 #endif
952 struct mntent *entry;
953 struct stat st;
954 char *p, *device;
956 while ((entry = getmntent( f )))
958 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
959 if (!strcmp( entry->mnt_type, "nfs" ) ||
960 !strcmp( entry->mnt_type, "smbfs" ) ||
961 !strcmp( entry->mnt_type, "ncpfs" )) continue;
963 if (!strcmp( entry->mnt_type, "supermount" ))
965 if ((device = strstr( entry->mnt_opts, "dev=" )))
967 device += 4;
968 if ((p = strchr( device, ',' ))) *p = 0;
971 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
973 /* if device is a regular file check for a loop mount */
974 if ((device = strstr( entry->mnt_opts, "loop=" )))
976 device += 5;
977 if ((p = strchr( device, ',' ))) *p = 0;
980 else device = entry->mnt_fsname;
982 if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
984 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry->mnt_dir) + 1 );
985 if (ret) strcpy( ret, entry->mnt_dir );
986 break;
989 fclose( f );
991 RtlLeaveCriticalSection( &dir_section );
992 #elif defined(__APPLE__)
993 struct statfs *entry;
994 struct stat st;
995 int i, size;
997 RtlEnterCriticalSection( &dir_section );
999 size = getmntinfo( &entry, MNT_NOWAIT );
1000 for (i = 0; i < size; i++)
1002 if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
1003 if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
1005 ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry[i].f_mntonname) + 1 );
1006 if (ret) strcpy( ret, entry[i].f_mntonname );
1007 break;
1010 RtlLeaveCriticalSection( &dir_section );
1011 #else
1012 static int warned;
1013 if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
1014 #endif
1015 return ret;
1019 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1020 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1022 struct get_fsid
1024 ULONG size;
1025 dev_t dev;
1026 fsid_t fsid;
1029 struct fs_cache
1031 dev_t dev;
1032 fsid_t fsid;
1033 BOOLEAN case_sensitive;
1034 } fs_cache[64];
1036 struct vol_caps
1038 ULONG size;
1039 vol_capabilities_attr_t caps;
1042 /***********************************************************************
1043 * look_up_fs_cache
1045 * Checks if the specified file system is in the cache.
1047 static struct fs_cache *look_up_fs_cache( dev_t dev )
1049 int i;
1050 for (i = 0; i < sizeof(fs_cache)/sizeof(fs_cache[0]); i++)
1051 if (fs_cache[i].dev == dev)
1052 return fs_cache+i;
1053 return NULL;
1056 /***********************************************************************
1057 * add_fs_cache
1059 * Adds the specified file system to the cache.
1061 static void add_fs_cache( dev_t dev, fsid_t fsid, BOOLEAN case_sensitive )
1063 int i;
1064 struct fs_cache *entry = look_up_fs_cache( dev );
1065 static int once = 0;
1066 if (entry)
1068 /* Update the cache */
1069 entry->fsid = fsid;
1070 entry->case_sensitive = case_sensitive;
1071 return;
1074 /* Add a new entry */
1075 for (i = 0; i < sizeof(fs_cache)/sizeof(fs_cache[0]); i++)
1076 if (fs_cache[i].dev == 0)
1078 /* This entry is empty, use it */
1079 fs_cache[i].dev = dev;
1080 fs_cache[i].fsid = fsid;
1081 fs_cache[i].case_sensitive = case_sensitive;
1082 return;
1085 /* Cache is out of space, warn */
1086 if (!once++)
1087 WARN( "FS cache is out of space, expect performance problems\n" );
1090 /***********************************************************************
1091 * get_dir_case_sensitivity_attr
1093 * Checks if the volume containing the specified directory is case
1094 * sensitive or not. Uses getattrlist(2).
1096 static int get_dir_case_sensitivity_attr( const char *dir )
1098 char *mntpoint;
1099 struct attrlist attr;
1100 struct vol_caps caps;
1101 struct get_fsid get_fsid;
1102 struct fs_cache *entry;
1104 /* First get the FS ID of the volume */
1105 attr.bitmapcount = ATTR_BIT_MAP_COUNT;
1106 attr.reserved = 0;
1107 attr.commonattr = ATTR_CMN_DEVID|ATTR_CMN_FSID;
1108 attr.volattr = attr.dirattr = attr.fileattr = attr.forkattr = 0;
1109 get_fsid.size = 0;
1110 if (getattrlist( dir, &attr, &get_fsid, sizeof(get_fsid), 0 ) != 0 ||
1111 get_fsid.size != sizeof(get_fsid))
1112 return -1;
1113 /* Try to look it up in the cache */
1114 entry = look_up_fs_cache( get_fsid.dev );
1115 if (entry && !memcmp( &entry->fsid, &get_fsid.fsid, sizeof(fsid_t) ))
1116 /* Cache lookup succeeded */
1117 return entry->case_sensitive;
1118 /* Cache is stale at this point, we have to update it */
1120 mntpoint = get_device_mount_point( get_fsid.dev );
1121 /* Now look up the case-sensitivity */
1122 attr.commonattr = 0;
1123 attr.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES;
1124 if (getattrlist( mntpoint, &attr, &caps, sizeof(caps), 0 ) < 0)
1126 RtlFreeHeap( GetProcessHeap(), 0, mntpoint );
1127 add_fs_cache( get_fsid.dev, get_fsid.fsid, TRUE );
1128 return TRUE;
1130 RtlFreeHeap( GetProcessHeap(), 0, mntpoint );
1131 if (caps.size == sizeof(caps) &&
1132 (caps.caps.valid[VOL_CAPABILITIES_FORMAT] &
1133 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING)) ==
1134 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING))
1136 BOOLEAN ret;
1138 if ((caps.caps.capabilities[VOL_CAPABILITIES_FORMAT] &
1139 VOL_CAP_FMT_CASE_SENSITIVE) != VOL_CAP_FMT_CASE_SENSITIVE)
1140 ret = FALSE;
1141 else
1142 ret = TRUE;
1143 /* Update the cache */
1144 add_fs_cache( get_fsid.dev, get_fsid.fsid, ret );
1145 return ret;
1147 return FALSE;
1149 #endif
1151 /***********************************************************************
1152 * get_dir_case_sensitivity_stat
1154 * Checks if the volume containing the specified directory is case
1155 * sensitive or not. Uses statfs(2) or statvfs(2).
1157 static BOOLEAN get_dir_case_sensitivity_stat( const char *dir )
1159 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1160 struct statfs stfs;
1162 if (statfs( dir, &stfs ) == -1) return FALSE;
1163 /* Assume these file systems are always case insensitive on Mac OS.
1164 * For FreeBSD, only assume CIOPFS is case insensitive (AFAIK, Mac OS
1165 * is the only UNIX that supports case-insensitive lookup).
1167 if (!strcmp( stfs.f_fstypename, "fusefs" ) &&
1168 !strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1169 return FALSE;
1170 #ifdef __APPLE__
1171 if (!strcmp( stfs.f_fstypename, "msdos" ) ||
1172 !strcmp( stfs.f_fstypename, "cd9660" ) ||
1173 !strcmp( stfs.f_fstypename, "udf" ) ||
1174 !strcmp( stfs.f_fstypename, "ntfs" ) ||
1175 !strcmp( stfs.f_fstypename, "smbfs" ))
1176 return FALSE;
1177 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1178 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_fssubtype == 0 ||
1179 stfs.f_fssubtype == 1 ||
1180 stfs.f_fssubtype == 128))
1181 return FALSE;
1182 #else
1183 /* The field says "reserved", but a quick look at the kernel source
1184 * tells us that this "reserved" field is really the same as the
1185 * "fssubtype" field from the inode64 structure (see munge_statfs()
1186 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1188 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_reserved1 == 0 ||
1189 stfs.f_reserved1 == 1 ||
1190 stfs.f_reserved1 == 128))
1191 return FALSE;
1192 #endif
1193 #endif
1194 return TRUE;
1196 #elif defined(__NetBSD__)
1197 struct statvfs stfs;
1199 if (statvfs( dir, &stfs ) == -1) return FALSE;
1200 /* Only assume CIOPFS is case insensitive. */
1201 if (strcmp( stfs.f_fstypename, "fusefs" ) ||
1202 strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1203 return TRUE;
1204 return FALSE;
1206 #elif defined(__linux__)
1207 struct statfs stfs;
1208 struct stat st;
1209 char *cifile;
1211 /* Only assume CIOPFS is case insensitive. */
1212 if (statfs( dir, &stfs ) == -1) return FALSE;
1213 if (stfs.f_type != 0x65735546 /* FUSE_SUPER_MAGIC */)
1214 return TRUE;
1215 /* Normally, we'd have to parse the mtab to find out exactly what
1216 * kind of FUSE FS this is. But, someone on wine-devel suggested
1217 * a shortcut. We'll stat a special file in the directory. If it's
1218 * there, we'll assume it's a CIOPFS, else not.
1219 * This will break if somebody puts a file named ".ciopfs" in a non-
1220 * CIOPFS directory.
1222 cifile = RtlAllocateHeap( GetProcessHeap(), 0, strlen( dir )+sizeof("/.ciopfs") );
1223 if (!cifile) return TRUE;
1224 strcpy( cifile, dir );
1225 strcat( cifile, "/.ciopfs" );
1226 if (stat( cifile, &st ) == 0)
1228 RtlFreeHeap( GetProcessHeap(), 0, cifile );
1229 return FALSE;
1231 RtlFreeHeap( GetProcessHeap(), 0, cifile );
1232 return TRUE;
1233 #else
1234 return TRUE;
1235 #endif
1239 /***********************************************************************
1240 * get_dir_case_sensitivity
1242 * Checks if the volume containing the specified directory is case
1243 * sensitive or not. Uses statfs(2) or statvfs(2).
1245 static BOOLEAN get_dir_case_sensitivity( const char *dir )
1247 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1248 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1249 int case_sensitive = get_dir_case_sensitivity_attr( dir );
1250 if (case_sensitive != -1) return case_sensitive;
1251 #endif
1252 return get_dir_case_sensitivity_stat( dir );
1256 /***********************************************************************
1257 * init_options
1259 * Initialize the show_dot_files options.
1261 static DWORD WINAPI init_options( RTL_RUN_ONCE *once, void *param, void **context )
1263 static const WCHAR WineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e',0};
1264 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
1265 char tmp[80];
1266 HANDLE root, hkey;
1267 DWORD dummy;
1268 OBJECT_ATTRIBUTES attr;
1269 UNICODE_STRING nameW;
1271 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
1272 attr.Length = sizeof(attr);
1273 attr.RootDirectory = root;
1274 attr.ObjectName = &nameW;
1275 attr.Attributes = 0;
1276 attr.SecurityDescriptor = NULL;
1277 attr.SecurityQualityOfService = NULL;
1278 RtlInitUnicodeString( &nameW, WineW );
1280 /* @@ Wine registry key: HKCU\Software\Wine */
1281 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
1283 RtlInitUnicodeString( &nameW, ShowDotFilesW );
1284 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
1286 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1287 show_dot_files = IS_OPTION_TRUE( str[0] );
1289 NtClose( hkey );
1291 NtClose( root );
1293 /* a couple of directories that we don't want to return in directory searches */
1294 ignore_file( wine_get_config_dir() );
1295 ignore_file( "/dev" );
1296 ignore_file( "/proc" );
1297 #ifdef linux
1298 ignore_file( "/sys" );
1299 #endif
1300 return TRUE;
1304 /***********************************************************************
1305 * DIR_is_hidden_file
1307 * Check if the specified file should be hidden based on its name and the show dot files option.
1309 BOOL DIR_is_hidden_file( const UNICODE_STRING *name )
1311 WCHAR *p, *end;
1313 RtlRunOnceExecuteOnce( &init_once, init_options, NULL, NULL );
1315 if (show_dot_files) return FALSE;
1317 end = p = name->Buffer + name->Length/sizeof(WCHAR);
1318 while (p > name->Buffer && IS_SEPARATOR(p[-1])) p--;
1319 while (p > name->Buffer && !IS_SEPARATOR(p[-1])) p--;
1320 if (p == end || *p != '.') return FALSE;
1321 /* make sure it isn't '.' or '..' */
1322 if (p + 1 == end) return FALSE;
1323 if (p[1] == '.' && p + 2 == end) return FALSE;
1324 return TRUE;
1328 /***********************************************************************
1329 * hash_short_file_name
1331 * Transform a Unix file name into a hashed DOS name. If the name is a valid
1332 * DOS name, it is converted to upper-case; otherwise it is replaced by a
1333 * hashed version that fits in 8.3 format.
1334 * 'buffer' must be at least 12 characters long.
1335 * Returns length of short name in bytes; short name is NOT null-terminated.
1337 static ULONG hash_short_file_name( const UNICODE_STRING *name, LPWSTR buffer )
1339 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1341 LPCWSTR p, ext, end = name->Buffer + name->Length / sizeof(WCHAR);
1342 LPWSTR dst;
1343 unsigned short hash;
1344 int i;
1346 /* Compute the hash code of the file name */
1347 /* If you know something about hash functions, feel free to */
1348 /* insert a better algorithm here... */
1349 if (!is_case_sensitive)
1351 for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
1352 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p) ^ (tolowerW(p[1]) << 8);
1353 hash = (hash<<3) ^ (hash>>5) ^ tolowerW(*p); /* Last character */
1355 else
1357 for (p = name->Buffer, hash = 0xbeef; p < end - 1; p++)
1358 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
1359 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
1362 /* Find last dot for start of the extension */
1363 for (p = name->Buffer + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
1365 /* Copy first 4 chars, replacing invalid chars with '_' */
1366 for (i = 4, p = name->Buffer, dst = buffer; i > 0; i--, p++)
1368 if (p == end || p == ext) break;
1369 *dst++ = is_invalid_dos_char(*p) ? '_' : toupperW(*p);
1371 /* Pad to 5 chars with '~' */
1372 while (i-- >= 0) *dst++ = '~';
1374 /* Insert hash code converted to 3 ASCII chars */
1375 *dst++ = hash_chars[(hash >> 10) & 0x1f];
1376 *dst++ = hash_chars[(hash >> 5) & 0x1f];
1377 *dst++ = hash_chars[hash & 0x1f];
1379 /* Copy the first 3 chars of the extension (if any) */
1380 if (ext)
1382 *dst++ = '.';
1383 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
1384 *dst++ = is_invalid_dos_char(*ext) ? '_' : toupperW(*ext);
1386 return dst - buffer;
1390 /***********************************************************************
1391 * match_filename
1393 * Check a long file name against a mask.
1395 * Tests (done in W95 DOS shell - case insensitive):
1396 * *.txt test1.test.txt *
1397 * *st1* test1.txt *
1398 * *.t??????.t* test1.ta.tornado.txt *
1399 * *tornado* test1.ta.tornado.txt *
1400 * t*t test1.ta.tornado.txt *
1401 * ?est* test1.txt *
1402 * ?est??? test1.txt -
1403 * *test1.txt* test1.txt *
1404 * h?l?o*t.dat hellothisisatest.dat *
1406 static BOOLEAN match_filename( const UNICODE_STRING *name_str, const UNICODE_STRING *mask_str )
1408 BOOL mismatch;
1409 const WCHAR *name = name_str->Buffer;
1410 const WCHAR *mask = mask_str->Buffer;
1411 const WCHAR *name_end = name + name_str->Length / sizeof(WCHAR);
1412 const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
1413 const WCHAR *lastjoker = NULL;
1414 const WCHAR *next_to_retry = NULL;
1416 while (name < name_end && mask < mask_end)
1418 switch(*mask)
1420 case '*':
1421 mask++;
1422 while (mask < mask_end && *mask == '*') mask++; /* Skip consecutive '*' */
1423 if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
1424 lastjoker = mask;
1426 /* skip to the next match after the joker(s) */
1427 if (is_case_sensitive)
1428 while (name < name_end && (*name != *mask)) name++;
1429 else
1430 while (name < name_end && (toupperW(*name) != toupperW(*mask))) name++;
1431 next_to_retry = name;
1432 break;
1433 case '?':
1434 mask++;
1435 name++;
1436 break;
1437 default:
1438 if (is_case_sensitive) mismatch = (*mask != *name);
1439 else mismatch = (toupperW(*mask) != toupperW(*name));
1441 if (!mismatch)
1443 mask++;
1444 name++;
1445 if (mask == mask_end)
1447 if (name == name_end) return TRUE;
1448 if (lastjoker) mask = lastjoker;
1451 else /* mismatch ! */
1453 if (lastjoker) /* we had an '*', so we can try unlimitedly */
1455 mask = lastjoker;
1457 /* this scan sequence was a mismatch, so restart
1458 * 1 char after the first char we checked last time */
1459 next_to_retry++;
1460 name = next_to_retry;
1462 else return FALSE; /* bad luck */
1464 break;
1467 while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
1468 mask++; /* Ignore trailing '.' or '*' in mask */
1469 return (name == name_end && mask == mask_end);
1473 /***********************************************************************
1474 * append_entry
1476 * Add a file to the directory data if it matches the mask.
1478 static BOOL append_entry( struct dir_data *data, const char *long_name,
1479 const char *short_name, const UNICODE_STRING *mask )
1481 int i, long_len, short_len;
1482 WCHAR long_nameW[MAX_DIR_ENTRY_LEN + 1];
1483 WCHAR short_nameW[13];
1484 UNICODE_STRING str;
1486 long_len = ntdll_umbstowcs( 0, long_name, strlen(long_name), long_nameW, MAX_DIR_ENTRY_LEN );
1487 if (long_len == -1) return TRUE;
1488 long_nameW[long_len] = 0;
1490 str.Buffer = long_nameW;
1491 str.Length = long_len * sizeof(WCHAR);
1492 str.MaximumLength = sizeof(long_nameW);
1494 if (short_name)
1496 short_len = ntdll_umbstowcs( 0, short_name, strlen(short_name),
1497 short_nameW, sizeof(short_nameW) / sizeof(WCHAR) - 1 );
1498 if (short_len == -1) short_len = sizeof(short_nameW) / sizeof(WCHAR) - 1;
1499 for (i = 0; i < short_len; i++) short_nameW[i] = toupperW( short_nameW[i] );
1501 else /* generate a short name if necessary */
1503 BOOLEAN spaces;
1505 short_len = 0;
1506 if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
1507 short_len = hash_short_file_name( &str, short_nameW );
1509 short_nameW[short_len] = 0;
1511 TRACE( "long %s short %s mask %s\n",
1512 debugstr_w( long_nameW ), debugstr_w( short_nameW ), debugstr_us( mask ));
1514 if (mask && !match_filename( &str, mask ))
1516 if (!short_len) return TRUE; /* no short name to match */
1517 str.Buffer = short_nameW;
1518 str.Length = short_len * sizeof(WCHAR);
1519 str.MaximumLength = sizeof(short_nameW);
1520 if (!match_filename( &str, mask )) return TRUE;
1523 return add_dir_data_names( data, long_nameW, short_nameW, long_name );
1527 /***********************************************************************
1528 * get_dir_data_entry
1530 * Return a directory entry from the cached data.
1532 static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, IO_STATUS_BLOCK *io,
1533 ULONG max_length, FILE_INFORMATION_CLASS class,
1534 union file_directory_info **last_info )
1536 const struct dir_data_names *names = &dir_data->names[dir_data->pos];
1537 union file_directory_info *info;
1538 struct stat st;
1539 ULONG name_len, start, dir_size, attributes;
1541 if (get_file_info( names->unix_name, &st, &attributes ) == -1)
1543 TRACE( "file no longer exists %s\n", names->unix_name );
1544 return STATUS_SUCCESS;
1546 if (is_ignored_file( &st ))
1548 TRACE( "ignoring file %s\n", names->unix_name );
1549 return STATUS_SUCCESS;
1551 if (!show_dot_files && names->long_name[0] == '.' && names->long_name[1] &&
1552 (names->long_name[1] != '.' || names->long_name[2]))
1553 attributes |= FILE_ATTRIBUTE_HIDDEN;
1555 if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */
1557 start = dir_info_align( io->Information );
1558 dir_size = dir_info_size( class, 0 );
1559 if (start + dir_size > max_length) return STATUS_MORE_ENTRIES;
1561 max_length -= start + dir_size;
1562 name_len = strlenW( names->long_name ) * sizeof(WCHAR);
1563 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
1564 if (*last_info && name_len > max_length) return STATUS_MORE_ENTRIES;
1566 info = (union file_directory_info *)((char *)info_ptr + start);
1568 /* all the structures start with a FileDirectoryInformation layout */
1569 fill_file_info( &st, attributes, info, class );
1570 info->dir.NextEntryOffset = 0;
1571 info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
1573 switch (class)
1575 case FileDirectoryInformation:
1576 info->dir.FileNameLength = name_len;
1577 break;
1579 case FileFullDirectoryInformation:
1580 info->full.EaSize = 0; /* FIXME */
1581 info->full.FileNameLength = name_len;
1582 break;
1584 case FileIdFullDirectoryInformation:
1585 info->id_full.EaSize = 0; /* FIXME */
1586 info->id_full.FileNameLength = name_len;
1587 break;
1589 case FileBothDirectoryInformation:
1590 info->both.EaSize = 0; /* FIXME */
1591 info->both.ShortNameLength = strlenW( names->short_name ) * sizeof(WCHAR);
1592 memcpy( info->both.ShortName, names->short_name, info->both.ShortNameLength );
1593 info->both.FileNameLength = name_len;
1594 break;
1596 case FileIdBothDirectoryInformation:
1597 info->id_both.EaSize = 0; /* FIXME */
1598 info->id_both.ShortNameLength = strlenW( names->short_name ) * sizeof(WCHAR);
1599 memcpy( info->id_both.ShortName, names->short_name, info->id_both.ShortNameLength );
1600 info->id_both.FileNameLength = name_len;
1601 break;
1603 default:
1604 assert(0);
1605 return 0;
1608 memcpy( (char *)info + dir_size, names->long_name, min( name_len, max_length ) );
1609 io->Information = start + dir_size + min( name_len, max_length );
1610 if (*last_info) (*last_info)->next = (char *)info - (char *)*last_info;
1611 *last_info = info;
1612 return name_len > max_length ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
1615 #ifdef VFAT_IOCTL_READDIR_BOTH
1617 /***********************************************************************
1618 * start_vfat_ioctl
1620 * Wrapper for the VFAT ioctl to work around various kernel bugs.
1621 * dir_section must be held by caller.
1623 static KERNEL_DIRENT *start_vfat_ioctl( int fd )
1625 static KERNEL_DIRENT *de;
1626 int res;
1628 if (!de)
1630 SIZE_T size = 2 * sizeof(*de) + page_size;
1631 void *addr = NULL;
1633 if (NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_RESERVE, PAGE_READWRITE ))
1634 return NULL;
1635 /* commit only the size needed for the dir entries */
1636 /* this leaves an extra unaccessible page, which should make the kernel */
1637 /* fail with -EFAULT before it stomps all over our memory */
1638 de = addr;
1639 size = 2 * sizeof(*de);
1640 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 1, &size, MEM_COMMIT, PAGE_READWRITE );
1643 /* set d_reclen to 65535 to work around an AFS kernel bug */
1644 de[0].d_reclen = 65535;
1645 res = ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de );
1646 if (res == -1)
1648 if (errno != ENOENT) return NULL; /* VFAT ioctl probably not supported */
1649 de[0].d_reclen = 0; /* eof */
1651 else if (!res && de[0].d_reclen == 65535) return NULL; /* AFS bug */
1653 return de;
1657 /***********************************************************************
1658 * read_directory_vfat
1660 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
1662 static NTSTATUS read_directory_data_vfat( struct dir_data *data, int fd, const UNICODE_STRING *mask )
1664 char *short_name, *long_name;
1665 size_t len;
1666 KERNEL_DIRENT *de;
1667 NTSTATUS status = STATUS_NO_MEMORY;
1668 off_t old_pos = lseek( fd, 0, SEEK_CUR );
1670 if (!(de = start_vfat_ioctl( fd ))) return STATUS_NOT_SUPPORTED;
1672 lseek( fd, 0, SEEK_SET );
1674 if (!append_entry( data, ".", NULL, mask )) goto done;
1675 if (!append_entry( data, "..", NULL, mask )) goto done;
1677 while (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) != -1)
1679 if (!de[0].d_reclen) break; /* eof */
1681 /* make sure names are null-terminated to work around an x86-64 kernel bug */
1682 len = min( de[0].d_reclen, sizeof(de[0].d_name) - 1 );
1683 de[0].d_name[len] = 0;
1684 len = min( de[1].d_reclen, sizeof(de[1].d_name) - 1 );
1685 de[1].d_name[len] = 0;
1687 if (!strcmp( de[0].d_name, "." ) || !strcmp( de[0].d_name, ".." )) continue;
1688 if (de[1].d_name[0])
1690 short_name = de[0].d_name;
1691 long_name = de[1].d_name;
1693 else
1695 long_name = de[0].d_name;
1696 short_name = NULL;
1698 if (!append_entry( data, long_name, short_name, mask )) goto done;
1700 status = STATUS_SUCCESS;
1701 done:
1702 lseek( fd, old_pos, SEEK_SET );
1703 return status;
1705 #endif /* VFAT_IOCTL_READDIR_BOTH */
1708 #ifdef HAVE_GETATTRLIST
1709 /***********************************************************************
1710 * read_directory_getattrlist
1712 * Read a single file from a directory by determining whether the file
1713 * identified by mask exists using getattrlist.
1715 static NTSTATUS read_directory_data_getattrlist( struct dir_data *data, const char *unix_name )
1717 struct attrlist attrlist;
1718 #include "pshpack4.h"
1719 struct
1721 u_int32_t length;
1722 struct attrreference name_reference;
1723 fsobj_type_t type;
1724 char name[NAME_MAX * 3 + 1];
1725 } buffer;
1726 #include "poppack.h"
1728 memset( &attrlist, 0, sizeof(attrlist) );
1729 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
1730 attrlist.commonattr = ATTR_CMN_NAME | ATTR_CMN_OBJTYPE;
1731 if (getattrlist( unix_name, &attrlist, &buffer, sizeof(buffer), FSOPT_NOFOLLOW ) == -1)
1732 return STATUS_NO_SUCH_FILE;
1733 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
1734 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
1735 if (buffer.type == VLNK)
1737 u_int32_t dummy;
1738 attrlist.commonattr = 0;
1739 if (getattrlist( unix_name, &attrlist, &dummy, sizeof(dummy), 0 ) == -1)
1740 return STATUS_NO_SUCH_FILE;
1743 TRACE( "found %s\n", buffer.name );
1745 if (!append_entry( data, buffer.name, NULL, NULL )) return STATUS_NO_MEMORY;
1747 return STATUS_SUCCESS;
1749 #endif /* HAVE_GETATTRLIST */
1752 /***********************************************************************
1753 * read_directory_stat
1755 * Read a single file from a directory by determining whether the file
1756 * identified by mask exists using stat.
1758 static NTSTATUS read_directory_data_stat( struct dir_data *data, const char *unix_name )
1760 struct stat st;
1762 /* if the file system is not case sensitive we can't find the actual name through stat() */
1763 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE;
1764 if (stat( unix_name, &st ) == -1) return STATUS_NO_SUCH_FILE;
1766 TRACE( "found %s\n", unix_name );
1768 if (!append_entry( data, unix_name, NULL, NULL )) return STATUS_NO_MEMORY;
1770 return STATUS_SUCCESS;
1774 /***********************************************************************
1775 * read_directory_readdir
1777 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
1779 static NTSTATUS read_directory_data_readdir( struct dir_data *data, const UNICODE_STRING *mask )
1781 struct dirent *de;
1782 NTSTATUS status = STATUS_NO_MEMORY;
1783 DIR *dir = opendir( "." );
1785 if (!dir) return STATUS_NO_SUCH_FILE;
1787 if (!append_entry( data, ".", NULL, mask )) goto done;
1788 if (!append_entry( data, "..", NULL, mask )) goto done;
1789 while ((de = readdir( dir )))
1791 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
1792 if (!append_entry( data, de->d_name, NULL, mask )) goto done;
1794 status = STATUS_SUCCESS;
1796 done:
1797 closedir( dir );
1798 return status;
1802 /***********************************************************************
1803 * read_directory_data
1805 * Read the full contents of a directory, using one of the above helper functions.
1807 static NTSTATUS read_directory_data( struct dir_data *data, int fd, const UNICODE_STRING *mask )
1809 NTSTATUS status;
1811 #ifdef VFAT_IOCTL_READDIR_BOTH
1812 if (!(status = read_directory_data_vfat( data, fd, mask ))) return status;
1813 #endif
1815 if (!has_wildcard( mask ))
1817 /* convert the mask to a Unix name and check for it */
1818 int ret, used_default;
1819 char unix_name[MAX_DIR_ENTRY_LEN * 3 + 1];
1821 ret = ntdll_wcstoumbs( 0, mask->Buffer, mask->Length / sizeof(WCHAR),
1822 unix_name, sizeof(unix_name) - 1, NULL, &used_default );
1823 if (ret > 0 && !used_default)
1825 unix_name[ret] = 0;
1826 #ifdef HAVE_GETATTRLIST
1827 if (!(status = read_directory_data_getattrlist( data, unix_name ))) return status;
1828 #endif
1829 if (!(status = read_directory_data_stat( data, unix_name ))) return status;
1833 return read_directory_data_readdir( data, mask );
1837 /* compare file names for directory sorting */
1838 static int name_compare( const void *a, const void *b )
1840 const struct dir_data_names *file_a = (const struct dir_data_names *)a;
1841 const struct dir_data_names *file_b = (const struct dir_data_names *)b;
1842 int ret = RtlCompareUnicodeStrings( file_a->long_name, strlenW(file_a->long_name),
1843 file_b->long_name, strlenW(file_b->long_name), TRUE );
1844 if (!ret) ret = strcmpW( file_a->long_name, file_b->long_name );
1845 return ret;
1849 /***********************************************************************
1850 * init_cached_dir_data
1852 * Initialize the cached directory contents.
1854 static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const UNICODE_STRING *mask )
1856 struct dir_data *data;
1857 struct stat st;
1858 NTSTATUS status;
1859 unsigned int i;
1861 if (!(data = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) )))
1862 return STATUS_NO_MEMORY;
1864 if ((status = read_directory_data( data, fd, mask )))
1866 free_dir_data( data );
1867 return status;
1870 /* sort filenames, but not "." and ".." */
1871 i = 0;
1872 if (i < data->count && !strcmp( data->names[i].unix_name, "." )) i++;
1873 if (i < data->count && !strcmp( data->names[i].unix_name, ".." )) i++;
1874 if (i < data->count) qsort( data->names + i, data->count - i, sizeof(*data->names), name_compare );
1876 if (data->count)
1878 /* release unused space */
1879 if (data->buffer)
1880 RtlReAllocateHeap( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, data->buffer,
1881 offsetof( struct dir_data_buffer, data[data->buffer->pos] ));
1882 if (data->count < data->size)
1883 RtlReAllocateHeap( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, data->names,
1884 data->count * sizeof(*data->names) );
1885 if (!fstat( fd, &st ))
1887 data->id.dev = st.st_dev;
1888 data->id.ino = st.st_ino;
1892 TRACE( "mask %s found %u files\n", debugstr_us( mask ), data->count );
1893 for (i = 0; i < data->count; i++)
1894 TRACE( "%s %s\n", debugstr_w(data->names[i].long_name), debugstr_w(data->names[i].short_name) );
1896 *data_ret = data;
1897 return data->count ? STATUS_SUCCESS : STATUS_NO_SUCH_FILE;
1901 /***********************************************************************
1902 * get_cached_dir_data
1904 * Retrieve the cached directory data, or initialize it if necessary.
1906 static NTSTATUS get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd,
1907 const UNICODE_STRING *mask )
1909 unsigned int i;
1910 int entry = -1, free_entries[16];
1911 NTSTATUS status;
1913 SERVER_START_REQ( get_directory_cache_entry )
1915 req->handle = wine_server_obj_handle( handle );
1916 wine_server_set_reply( req, free_entries, sizeof(free_entries) );
1917 if (!(status = wine_server_call( req ))) entry = reply->entry;
1919 for (i = 0; i < wine_server_reply_size( reply ) / sizeof(*free_entries); i++)
1921 int free_idx = free_entries[i];
1922 if (free_idx < dir_data_cache_size)
1924 free_dir_data( dir_data_cache[free_idx] );
1925 dir_data_cache[free_idx] = NULL;
1929 SERVER_END_REQ;
1931 if (status)
1933 if (status == STATUS_SHARING_VIOLATION) FIXME( "shared directory handle not supported yet\n" );
1934 return status;
1937 if (entry >= dir_data_cache_size)
1939 unsigned int size = max( dir_data_cache_initial_size, max( dir_data_cache_size * 2, entry + 1 ) );
1940 struct dir_data **new_cache;
1942 if (dir_data_cache)
1943 new_cache = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, dir_data_cache,
1944 size * sizeof(*new_cache) );
1945 else
1946 new_cache = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, size * sizeof(*new_cache) );
1947 if (!new_cache) return STATUS_NO_MEMORY;
1948 dir_data_cache = new_cache;
1949 dir_data_cache_size = size;
1952 if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask );
1954 *data_ret = dir_data_cache[entry];
1955 return status;
1959 /******************************************************************************
1960 * NtQueryDirectoryFile [NTDLL.@]
1961 * ZwQueryDirectoryFile [NTDLL.@]
1963 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
1964 PIO_APC_ROUTINE apc_routine, PVOID apc_context,
1965 PIO_STATUS_BLOCK io,
1966 PVOID buffer, ULONG length,
1967 FILE_INFORMATION_CLASS info_class,
1968 BOOLEAN single_entry,
1969 PUNICODE_STRING mask,
1970 BOOLEAN restart_scan )
1972 int cwd, fd, needs_close;
1973 struct dir_data *data;
1974 NTSTATUS status;
1976 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
1977 handle, event, apc_routine, apc_context, io, buffer,
1978 length, info_class, single_entry, debugstr_us(mask),
1979 restart_scan);
1981 if (event || apc_routine)
1983 FIXME( "Unsupported yet option\n" );
1984 return STATUS_NOT_IMPLEMENTED;
1986 switch (info_class)
1988 case FileDirectoryInformation:
1989 case FileBothDirectoryInformation:
1990 case FileFullDirectoryInformation:
1991 case FileIdBothDirectoryInformation:
1992 case FileIdFullDirectoryInformation:
1993 if (length < dir_info_align( dir_info_size( info_class, 1 ))) return STATUS_INFO_LENGTH_MISMATCH;
1994 if (!buffer) return STATUS_ACCESS_VIOLATION;
1995 break;
1996 default:
1997 FIXME( "Unsupported file info class %d\n", info_class );
1998 return STATUS_NOT_IMPLEMENTED;
2001 if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
2002 return status;
2004 io->Information = 0;
2006 RtlRunOnceExecuteOnce( &init_once, init_options, NULL, NULL );
2008 RtlEnterCriticalSection( &dir_section );
2010 cwd = open( ".", O_RDONLY );
2011 if (fchdir( fd ) != -1)
2013 if (!(status = get_cached_dir_data( handle, &data, fd, mask )))
2015 union file_directory_info *last_info = NULL;
2017 if (restart_scan) data->pos = 0;
2019 while (!status && data->pos < data->count)
2021 status = get_dir_data_entry( data, buffer, io, length, info_class, &last_info );
2022 if (!status || status == STATUS_BUFFER_OVERFLOW) data->pos++;
2023 if (single_entry) break;
2026 if (!last_info) status = STATUS_NO_MORE_FILES;
2027 else if (status == STATUS_MORE_ENTRIES) status = STATUS_SUCCESS;
2029 io->u.Status = status;
2031 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
2033 else status = FILE_GetNtStatus();
2035 RtlLeaveCriticalSection( &dir_section );
2037 if (needs_close) close( fd );
2038 if (cwd != -1) close( cwd );
2039 TRACE( "=> %x (%ld)\n", status, io->Information );
2040 return status;
2044 /***********************************************************************
2045 * find_file_in_dir
2047 * Find a file in a directory the hard way, by doing a case-insensitive search.
2048 * The file found is appended to unix_name at pos.
2049 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2051 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
2052 BOOLEAN check_case, BOOLEAN *is_win_dir )
2054 WCHAR buffer[MAX_DIR_ENTRY_LEN];
2055 UNICODE_STRING str;
2056 BOOLEAN spaces, is_name_8_dot_3;
2057 DIR *dir;
2058 struct dirent *de;
2059 struct stat st;
2060 int ret, used_default;
2062 /* try a shortcut for this directory */
2064 unix_name[pos++] = '/';
2065 ret = ntdll_wcstoumbs( 0, name, length, unix_name + pos, MAX_DIR_ENTRY_LEN,
2066 NULL, &used_default );
2067 /* if we used the default char, the Unix name won't round trip properly back to Unicode */
2068 /* so it cannot match the file we are looking for */
2069 if (ret >= 0 && !used_default)
2071 unix_name[pos + ret] = 0;
2072 if (!stat( unix_name, &st ))
2074 if (is_win_dir) *is_win_dir = is_same_file( &windir, &st );
2075 return STATUS_SUCCESS;
2078 if (check_case) goto not_found; /* we want an exact match */
2080 if (pos > 1) unix_name[pos - 1] = 0;
2081 else unix_name[1] = 0; /* keep the initial slash */
2083 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2085 str.Buffer = (WCHAR *)name;
2086 str.Length = length * sizeof(WCHAR);
2087 str.MaximumLength = str.Length;
2088 is_name_8_dot_3 = RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) && !spaces;
2089 #ifndef VFAT_IOCTL_READDIR_BOTH
2090 is_name_8_dot_3 = is_name_8_dot_3 && length >= 8 && name[4] == '~';
2091 #endif
2093 if (!is_name_8_dot_3 && !get_dir_case_sensitivity( unix_name )) goto not_found;
2095 /* now look for it through the directory */
2097 #ifdef VFAT_IOCTL_READDIR_BOTH
2098 if (is_name_8_dot_3)
2100 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
2101 if (fd != -1)
2103 KERNEL_DIRENT *kde;
2105 RtlEnterCriticalSection( &dir_section );
2106 if ((kde = start_vfat_ioctl( fd )))
2108 unix_name[pos - 1] = '/';
2109 while (kde[0].d_reclen)
2111 /* make sure names are null-terminated to work around an x86-64 kernel bug */
2112 size_t len = min(kde[0].d_reclen, sizeof(kde[0].d_name) - 1 );
2113 kde[0].d_name[len] = 0;
2114 len = min(kde[1].d_reclen, sizeof(kde[1].d_name) - 1 );
2115 kde[1].d_name[len] = 0;
2117 if (kde[1].d_name[0])
2119 ret = ntdll_umbstowcs( 0, kde[1].d_name, strlen(kde[1].d_name),
2120 buffer, MAX_DIR_ENTRY_LEN );
2121 if (ret == length && !memicmpW( buffer, name, length))
2123 strcpy( unix_name + pos, kde[1].d_name );
2124 RtlLeaveCriticalSection( &dir_section );
2125 close( fd );
2126 goto success;
2129 ret = ntdll_umbstowcs( 0, kde[0].d_name, strlen(kde[0].d_name),
2130 buffer, MAX_DIR_ENTRY_LEN );
2131 if (ret == length && !memicmpW( buffer, name, length))
2133 strcpy( unix_name + pos,
2134 kde[1].d_name[0] ? kde[1].d_name : kde[0].d_name );
2135 RtlLeaveCriticalSection( &dir_section );
2136 close( fd );
2137 goto success;
2139 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) == -1)
2141 RtlLeaveCriticalSection( &dir_section );
2142 close( fd );
2143 goto not_found;
2147 RtlLeaveCriticalSection( &dir_section );
2148 close( fd );
2150 /* fall through to normal handling */
2152 #endif /* VFAT_IOCTL_READDIR_BOTH */
2154 if (!(dir = opendir( unix_name )))
2156 if (errno == ENOENT) return STATUS_OBJECT_PATH_NOT_FOUND;
2157 else return FILE_GetNtStatus();
2159 unix_name[pos - 1] = '/';
2160 str.Buffer = buffer;
2161 str.MaximumLength = sizeof(buffer);
2162 while ((de = readdir( dir )))
2164 ret = ntdll_umbstowcs( 0, de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
2165 if (ret == length && !memicmpW( buffer, name, length ))
2167 strcpy( unix_name + pos, de->d_name );
2168 closedir( dir );
2169 goto success;
2172 if (!is_name_8_dot_3) continue;
2174 str.Length = ret * sizeof(WCHAR);
2175 if (!RtlIsNameLegalDOS8Dot3( &str, NULL, &spaces ) || spaces)
2177 WCHAR short_nameW[12];
2178 ret = hash_short_file_name( &str, short_nameW );
2179 if (ret == length && !memicmpW( short_nameW, name, length ))
2181 strcpy( unix_name + pos, de->d_name );
2182 closedir( dir );
2183 goto success;
2187 closedir( dir );
2189 not_found:
2190 unix_name[pos - 1] = 0;
2191 return STATUS_OBJECT_PATH_NOT_FOUND;
2193 success:
2194 if (is_win_dir && !stat( unix_name, &st )) *is_win_dir = is_same_file( &windir, &st );
2195 return STATUS_SUCCESS;
2199 #ifndef _WIN64
2201 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2202 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2203 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};
2204 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2205 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2206 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2207 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
2208 static const WCHAR syswow64W[] = {'s','y','s','w','o','w','6','4',0};
2209 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
2210 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2211 static const WCHAR wow_regeditW[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
2213 static struct
2215 const WCHAR *source;
2216 const WCHAR *dos_target;
2217 const char *unix_target;
2218 } redirects[] =
2220 { catrootW, NULL, NULL },
2221 { catroot2W, NULL, NULL },
2222 { driversstoreW, NULL, NULL },
2223 { driversetcW, NULL, NULL },
2224 { logfilesW, NULL, NULL },
2225 { spoolW, NULL, NULL },
2226 { system32W, syswow64W, NULL },
2227 { sysnativeW, system32W, NULL },
2228 { regeditW, wow_regeditW, NULL }
2231 static unsigned int nb_redirects;
2234 /***********************************************************************
2235 * get_redirect_target
2237 * Find the target unix name for a redirected dir.
2239 static const char *get_redirect_target( const char *windows_dir, const WCHAR *name )
2241 int used_default, len, pos, win_len = strlen( windows_dir );
2242 char *unix_name, *unix_target = NULL;
2243 NTSTATUS status;
2245 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, win_len + MAX_DIR_ENTRY_LEN + 2 )))
2246 return NULL;
2247 memcpy( unix_name, windows_dir, win_len );
2248 pos = win_len;
2250 while (*name)
2252 const WCHAR *end, *next;
2254 for (end = name; *end; end++) if (IS_SEPARATOR(*end)) break;
2255 for (next = end; *next; next++) if (!IS_SEPARATOR(*next)) break;
2257 status = find_file_in_dir( unix_name, pos, name, end - name, FALSE, NULL );
2258 if (status == STATUS_OBJECT_PATH_NOT_FOUND && !*next) /* not finding last element is ok */
2260 len = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2261 MAX_DIR_ENTRY_LEN - (pos - win_len), NULL, &used_default );
2262 if (len > 0 && !used_default)
2264 unix_name[pos] = '/';
2265 pos += len + 1;
2266 unix_name[pos] = 0;
2267 break;
2270 if (status) goto done;
2271 pos += strlen( unix_name + pos );
2272 name = next;
2275 if ((unix_target = RtlAllocateHeap( GetProcessHeap(), 0, pos - win_len )))
2276 memcpy( unix_target, unix_name + win_len + 1, pos - win_len );
2278 done:
2279 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2280 return unix_target;
2284 /***********************************************************************
2285 * init_redirects
2287 static void init_redirects(void)
2289 UNICODE_STRING nt_name;
2290 ANSI_STRING unix_name;
2291 NTSTATUS status;
2292 struct stat st;
2293 unsigned int i;
2295 if (!RtlDosPathNameToNtPathName_U( user_shared_data->NtSystemRoot, &nt_name, NULL, NULL ))
2297 ERR( "can't convert %s\n", debugstr_w(user_shared_data->NtSystemRoot) );
2298 return;
2300 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN_IF, FALSE );
2301 RtlFreeUnicodeString( &nt_name );
2302 if (status)
2304 ERR( "cannot open %s (%x)\n", debugstr_w(user_shared_data->NtSystemRoot), status );
2305 return;
2307 if (!stat( unix_name.Buffer, &st ))
2309 windir.dev = st.st_dev;
2310 windir.ino = st.st_ino;
2311 nb_redirects = sizeof(redirects) / sizeof(redirects[0]);
2312 for (i = 0; i < nb_redirects; i++)
2314 if (!redirects[i].dos_target) continue;
2315 redirects[i].unix_target = get_redirect_target( unix_name.Buffer, redirects[i].dos_target );
2316 TRACE( "%s -> %s\n", debugstr_w(redirects[i].source), redirects[i].unix_target );
2319 RtlFreeAnsiString( &unix_name );
2324 /***********************************************************************
2325 * match_redirect
2327 * Check if path matches a redirect name. If yes, return matched length.
2329 static int match_redirect( const WCHAR *path, int len, const WCHAR *redir, BOOLEAN check_case )
2331 int i = 0;
2333 while (i < len && *redir)
2335 if (IS_SEPARATOR(path[i]))
2337 if (*redir++ != '\\') return 0;
2338 while (i < len && IS_SEPARATOR(path[i])) i++;
2339 continue; /* move on to next path component */
2341 else if (check_case)
2343 if (path[i] != *redir) return 0;
2345 else
2347 if (tolowerW(path[i]) != tolowerW(*redir)) return 0;
2349 i++;
2350 redir++;
2352 if (*redir) return 0;
2353 if (i < len && !IS_SEPARATOR(path[i])) return 0;
2354 while (i < len && IS_SEPARATOR(path[i])) i++;
2355 return i;
2359 /***********************************************************************
2360 * get_redirect_path
2362 * Retrieve the Unix path corresponding to a redirected path if any.
2364 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, BOOLEAN check_case )
2366 unsigned int i;
2367 int len;
2369 for (i = 0; i < nb_redirects; i++)
2371 if ((len = match_redirect( name, length, redirects[i].source, check_case )))
2373 if (!redirects[i].unix_target) break;
2374 unix_name[pos++] = '/';
2375 strcpy( unix_name + pos, redirects[i].unix_target );
2376 return len;
2379 return 0;
2382 #else /* _WIN64 */
2384 /* there are no redirects on 64-bit */
2386 static const unsigned int nb_redirects = 0;
2388 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, BOOLEAN check_case )
2390 return 0;
2393 #endif
2395 /***********************************************************************
2396 * DIR_init_windows_dir
2398 void DIR_init_windows_dir( const WCHAR *win, const WCHAR *sys )
2400 /* FIXME: should probably store paths as NT file names */
2402 RtlCreateUnicodeString( &system_dir, sys );
2404 #ifndef _WIN64
2405 if (is_wow64) init_redirects();
2406 #endif
2410 /******************************************************************************
2411 * get_dos_device
2413 * Get the Unix path of a DOS device.
2415 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *unix_name_ret )
2417 const char *config_dir = wine_get_config_dir();
2418 struct stat st;
2419 char *unix_name, *new_name, *dev;
2420 unsigned int i;
2421 int unix_len;
2423 /* make sure the device name is ASCII */
2424 for (i = 0; i < name_len; i++)
2425 if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
2427 unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
2429 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2430 return STATUS_NO_MEMORY;
2432 strcpy( unix_name, config_dir );
2433 strcat( unix_name, "/dosdevices/" );
2434 dev = unix_name + strlen(unix_name);
2436 for (i = 0; i < name_len; i++) dev[i] = (char)tolowerW(name[i]);
2437 dev[i] = 0;
2439 /* special case for drive devices */
2440 if (name_len == 2 && dev[1] == ':')
2442 dev[i++] = ':';
2443 dev[i] = 0;
2446 for (;;)
2448 if (!stat( unix_name, &st ))
2450 TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
2451 unix_name_ret->Buffer = unix_name;
2452 unix_name_ret->Length = strlen(unix_name);
2453 unix_name_ret->MaximumLength = unix_len;
2454 return STATUS_SUCCESS;
2456 if (!dev) break;
2458 /* now try some defaults for it */
2459 if (!strcmp( dev, "aux" ))
2461 strcpy( dev, "com1" );
2462 continue;
2464 if (!strcmp( dev, "prn" ))
2466 strcpy( dev, "lpt1" );
2467 continue;
2470 new_name = NULL;
2471 if (dev[1] == ':' && dev[2] == ':') /* drive device */
2473 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
2474 new_name = get_default_drive_device( unix_name );
2476 else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( atoi(dev + 3 ));
2477 else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( atoi(dev + 3 ));
2479 if (!new_name) break;
2481 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2482 unix_name = new_name;
2483 unix_len = strlen(unix_name) + 1;
2484 dev = NULL; /* last try */
2486 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2487 return STATUS_BAD_DEVICE_TYPE;
2491 /* return the length of the DOS namespace prefix if any */
2492 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
2494 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
2495 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2497 if (name->Length >= sizeof(nt_prefixW) &&
2498 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
2499 return sizeof(nt_prefixW) / sizeof(WCHAR);
2501 if (name->Length >= sizeof(dosdev_prefixW) &&
2502 !memicmpW( name->Buffer, dosdev_prefixW, sizeof(dosdev_prefixW)/sizeof(WCHAR) ))
2503 return sizeof(dosdev_prefixW) / sizeof(WCHAR);
2505 return 0;
2509 /******************************************************************************
2510 * find_file_id
2512 * Recursively search directories from the dir queue for a given inode.
2514 static NTSTATUS find_file_id( ANSI_STRING *unix_name, ULONGLONG file_id, dev_t dev )
2516 unsigned int pos;
2517 DIR *dir;
2518 struct dirent *de;
2519 NTSTATUS status;
2520 struct stat st;
2522 while (!(status = next_dir_in_queue( unix_name->Buffer )))
2524 if (!(dir = opendir( unix_name->Buffer ))) continue;
2525 TRACE( "searching %s for %s\n", unix_name->Buffer, wine_dbgstr_longlong(file_id) );
2526 pos = strlen( unix_name->Buffer );
2527 if (pos + MAX_DIR_ENTRY_LEN >= unix_name->MaximumLength/sizeof(WCHAR))
2529 char *new = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name->Buffer,
2530 unix_name->MaximumLength * 2 );
2531 if (!new)
2533 closedir( dir );
2534 return STATUS_NO_MEMORY;
2536 unix_name->MaximumLength *= 2;
2537 unix_name->Buffer = new;
2539 unix_name->Buffer[pos++] = '/';
2540 while ((de = readdir( dir )))
2542 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2543 strcpy( unix_name->Buffer + pos, de->d_name );
2544 if (lstat( unix_name->Buffer, &st ) == -1) continue;
2545 if (st.st_dev != dev) continue;
2546 if (st.st_ino == file_id)
2548 closedir( dir );
2549 return STATUS_SUCCESS;
2551 if (!S_ISDIR( st.st_mode )) continue;
2552 if ((status = add_dir_to_queue( unix_name->Buffer )) != STATUS_SUCCESS)
2554 closedir( dir );
2555 return status;
2558 closedir( dir );
2560 return status;
2564 /******************************************************************************
2565 * file_id_to_unix_file_name
2567 * Lookup a file from its file id instead of its name.
2569 NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name )
2571 enum server_fd_type type;
2572 int old_cwd, root_fd, needs_close;
2573 NTSTATUS status;
2574 ULONGLONG file_id;
2575 struct stat st, root_st;
2577 if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
2578 if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
2579 memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
2581 unix_name->MaximumLength = 2 * MAX_DIR_ENTRY_LEN + 4;
2582 if (!(unix_name->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, unix_name->MaximumLength )))
2583 return STATUS_NO_MEMORY;
2584 strcpy( unix_name->Buffer, "." );
2586 if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
2587 goto done;
2589 if (type != FD_TYPE_DIR)
2591 status = STATUS_OBJECT_TYPE_MISMATCH;
2592 goto done;
2595 fstat( root_fd, &root_st );
2596 if (root_st.st_ino == file_id) /* shortcut for "." */
2598 status = STATUS_SUCCESS;
2599 goto done;
2602 RtlEnterCriticalSection( &dir_section );
2603 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
2605 /* shortcut for ".." */
2606 if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
2608 strcpy( unix_name->Buffer, ".." );
2609 status = STATUS_SUCCESS;
2611 else
2613 status = add_dir_to_queue( "." );
2614 if (!status)
2615 status = find_file_id( unix_name, file_id, root_st.st_dev );
2616 if (!status) /* get rid of "./" prefix */
2617 memmove( unix_name->Buffer, unix_name->Buffer + 2, strlen(unix_name->Buffer) - 1 );
2618 flush_dir_queue();
2620 if (fchdir( old_cwd ) == -1) chdir( "/" );
2622 else status = FILE_GetNtStatus();
2623 RtlLeaveCriticalSection( &dir_section );
2624 if (old_cwd != -1) close( old_cwd );
2626 done:
2627 if (status == STATUS_SUCCESS)
2629 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name->Buffer) );
2630 unix_name->Length = strlen( unix_name->Buffer );
2632 else
2634 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
2635 RtlFreeHeap( GetProcessHeap(), 0, unix_name->Buffer );
2637 if (needs_close) close( root_fd );
2638 return status;
2642 /******************************************************************************
2643 * lookup_unix_name
2645 * Helper for nt_to_unix_file_name
2647 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
2648 UINT disposition, BOOLEAN check_case )
2650 NTSTATUS status;
2651 int ret, used_default, len;
2652 struct stat st;
2653 char *unix_name = *buffer;
2654 const BOOL redirect = nb_redirects && ntdll_get_thread_data()->wow64_redir;
2656 /* try a shortcut first */
2658 ret = ntdll_wcstoumbs( 0, name, name_len, unix_name + pos, unix_len - pos - 1,
2659 NULL, &used_default );
2661 while (name_len && IS_SEPARATOR(*name))
2663 name++;
2664 name_len--;
2667 if (ret >= 0 && !used_default) /* if we used the default char the name didn't convert properly */
2669 char *p;
2670 unix_name[pos + ret] = 0;
2671 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
2672 if (!redirect || (!strstr( unix_name, "/windows/") && strncmp( unix_name, "windows/", 8 )))
2674 if (!stat( unix_name, &st ))
2676 /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */
2677 if (disposition == FILE_CREATE)
2678 return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED;
2679 return STATUS_SUCCESS;
2684 if (!name_len) /* empty name -> drive root doesn't exist */
2685 return STATUS_OBJECT_PATH_NOT_FOUND;
2686 if (check_case && !redirect && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
2687 return STATUS_OBJECT_NAME_NOT_FOUND;
2689 /* now do it component by component */
2691 while (name_len)
2693 const WCHAR *end, *next;
2694 BOOLEAN is_win_dir = FALSE;
2696 end = name;
2697 while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
2698 next = end;
2699 while (next < name + name_len && IS_SEPARATOR(*next)) next++;
2700 name_len -= next - name;
2702 /* grow the buffer if needed */
2704 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
2706 char *new_name;
2707 unix_len += 2 * MAX_DIR_ENTRY_LEN;
2708 if (!(new_name = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name, unix_len )))
2709 return STATUS_NO_MEMORY;
2710 unix_name = *buffer = new_name;
2713 status = find_file_in_dir( unix_name, pos, name, end - name,
2714 check_case, redirect ? &is_win_dir : NULL );
2716 /* if this is the last element, not finding it is not necessarily fatal */
2717 if (!name_len)
2719 if (status == STATUS_OBJECT_PATH_NOT_FOUND)
2721 status = STATUS_OBJECT_NAME_NOT_FOUND;
2722 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
2724 ret = ntdll_wcstoumbs( 0, name, end - name, unix_name + pos + 1,
2725 MAX_DIR_ENTRY_LEN, NULL, &used_default );
2726 if (ret > 0 && !used_default)
2728 unix_name[pos] = '/';
2729 unix_name[pos + 1 + ret] = 0;
2730 status = STATUS_NO_SUCH_FILE;
2731 break;
2735 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
2737 status = STATUS_OBJECT_NAME_COLLISION;
2741 if (status != STATUS_SUCCESS) break;
2743 pos += strlen( unix_name + pos );
2744 name = next;
2746 if (is_win_dir && (len = get_redirect_path( unix_name, pos, name, name_len, check_case )))
2748 name += len;
2749 name_len -= len;
2750 pos += strlen( unix_name + pos );
2751 TRACE( "redirecting -> %s + %s\n", debugstr_a(unix_name), debugstr_w(name) );
2755 return status;
2759 /******************************************************************************
2760 * nt_to_unix_file_name_attr
2762 NTSTATUS nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name_ret,
2763 UINT disposition )
2765 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
2766 enum server_fd_type type;
2767 int old_cwd, root_fd, needs_close;
2768 const WCHAR *name, *p;
2769 char *unix_name;
2770 int name_len, unix_len;
2771 NTSTATUS status;
2772 BOOLEAN check_case = !(attr->Attributes & OBJ_CASE_INSENSITIVE);
2774 if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
2775 return wine_nt_to_unix_file_name( attr->ObjectName, unix_name_ret, disposition, check_case );
2777 name = attr->ObjectName->Buffer;
2778 name_len = attr->ObjectName->Length / sizeof(WCHAR);
2780 if (name_len && IS_SEPARATOR(name[0])) return STATUS_INVALID_PARAMETER;
2782 /* check for invalid characters */
2783 for (p = name; p < name + name_len; p++)
2784 if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
2786 unix_len = ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
2787 unix_len += MAX_DIR_ENTRY_LEN + 3;
2788 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2789 return STATUS_NO_MEMORY;
2790 unix_name[0] = '.';
2792 if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
2794 if (type != FD_TYPE_DIR)
2796 if (needs_close) close( root_fd );
2797 status = STATUS_BAD_DEVICE_TYPE;
2799 else
2801 RtlEnterCriticalSection( &dir_section );
2802 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
2804 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1,
2805 disposition, check_case );
2806 if (fchdir( old_cwd ) == -1) chdir( "/" );
2808 else status = FILE_GetNtStatus();
2809 RtlLeaveCriticalSection( &dir_section );
2810 if (old_cwd != -1) close( old_cwd );
2811 if (needs_close) close( root_fd );
2814 else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
2816 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
2818 TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
2819 unix_name_ret->Buffer = unix_name;
2820 unix_name_ret->Length = strlen(unix_name);
2821 unix_name_ret->MaximumLength = unix_len;
2823 else
2825 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
2826 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2828 return status;
2832 /******************************************************************************
2833 * wine_nt_to_unix_file_name (NTDLL.@) Not a Windows API
2835 * Convert a file name from NT namespace to Unix namespace.
2837 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
2838 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
2839 * returned, but the unix name is still filled in properly.
2841 NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
2842 UINT disposition, BOOLEAN check_case )
2844 static const WCHAR unixW[] = {'u','n','i','x'};
2845 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
2847 NTSTATUS status = STATUS_SUCCESS;
2848 const char *config_dir = wine_get_config_dir();
2849 const WCHAR *name, *p;
2850 struct stat st;
2851 char *unix_name;
2852 int pos, ret, name_len, unix_len, prefix_len, used_default;
2853 WCHAR prefix[MAX_DIR_ENTRY_LEN];
2854 BOOLEAN is_unix = FALSE;
2856 name = nameW->Buffer;
2857 name_len = nameW->Length / sizeof(WCHAR);
2859 if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
2861 if (!(pos = get_dos_prefix_len( nameW )))
2862 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
2864 name += pos;
2865 name_len -= pos;
2867 if (!name_len) return STATUS_OBJECT_NAME_INVALID;
2869 /* check for sub-directory */
2870 for (pos = 0; pos < name_len; pos++)
2872 if (IS_SEPARATOR(name[pos])) break;
2873 if (name[pos] < 32 || strchrW( invalid_charsW, name[pos] ))
2874 return STATUS_OBJECT_NAME_INVALID;
2876 if (pos > MAX_DIR_ENTRY_LEN)
2877 return STATUS_OBJECT_NAME_INVALID;
2879 if (pos == name_len) /* no subdir, plain DOS device */
2880 return get_dos_device( name, name_len, unix_name_ret );
2882 for (prefix_len = 0; prefix_len < pos; prefix_len++)
2883 prefix[prefix_len] = tolowerW(name[prefix_len]);
2885 name += prefix_len;
2886 name_len -= prefix_len;
2888 /* check for invalid characters (all chars except 0 are valid for unix) */
2889 is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
2890 if (is_unix)
2892 for (p = name; p < name + name_len; p++)
2893 if (!*p) return STATUS_OBJECT_NAME_INVALID;
2894 check_case = TRUE;
2896 else
2898 for (p = name; p < name + name_len; p++)
2899 if (*p < 32 || strchrW( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
2902 unix_len = ntdll_wcstoumbs( 0, prefix, prefix_len, NULL, 0, NULL, NULL );
2903 unix_len += ntdll_wcstoumbs( 0, name, name_len, NULL, 0, NULL, NULL );
2904 unix_len += MAX_DIR_ENTRY_LEN + 3;
2905 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
2906 if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
2907 return STATUS_NO_MEMORY;
2908 strcpy( unix_name, config_dir );
2909 strcat( unix_name, "/dosdevices/" );
2910 pos = strlen(unix_name);
2912 ret = ntdll_wcstoumbs( 0, prefix, prefix_len, unix_name + pos, unix_len - pos - 1,
2913 NULL, &used_default );
2914 if (!ret || used_default)
2916 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2917 return STATUS_OBJECT_NAME_INVALID;
2919 pos += ret;
2921 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
2923 if (prefix_len != 2 || prefix[1] != ':')
2925 unix_name[pos] = 0;
2926 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
2928 if (!is_unix)
2930 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2931 return STATUS_BAD_DEVICE_TYPE;
2933 pos = 0; /* fall back to unix root */
2937 status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, check_case );
2938 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
2940 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
2941 unix_name_ret->Buffer = unix_name;
2942 unix_name_ret->Length = strlen(unix_name);
2943 unix_name_ret->MaximumLength = unix_len;
2945 else
2947 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
2948 RtlFreeHeap( GetProcessHeap(), 0, unix_name );
2950 return status;
2954 /******************************************************************
2955 * RtlWow64EnableFsRedirection (NTDLL.@)
2957 NTSTATUS WINAPI RtlWow64EnableFsRedirection( BOOLEAN enable )
2959 if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
2960 ntdll_get_thread_data()->wow64_redir = enable;
2961 return STATUS_SUCCESS;
2965 /******************************************************************
2966 * RtlWow64EnableFsRedirectionEx (NTDLL.@)
2968 NTSTATUS WINAPI RtlWow64EnableFsRedirectionEx( ULONG disable, ULONG *old_value )
2970 if (!is_wow64) return STATUS_NOT_IMPLEMENTED;
2971 if (((ULONG_PTR)old_value >> 16) == 0) return STATUS_ACCESS_VIOLATION;
2973 *old_value = !ntdll_get_thread_data()->wow64_redir;
2974 ntdll_get_thread_data()->wow64_redir = !disable;
2975 return STATUS_SUCCESS;
2979 /******************************************************************
2980 * RtlDoesFileExists_U (NTDLL.@)
2982 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
2984 UNICODE_STRING nt_name;
2985 FILE_BASIC_INFORMATION basic_info;
2986 OBJECT_ATTRIBUTES attr;
2987 BOOLEAN ret;
2989 if (!RtlDosPathNameToNtPathName_U( file_name, &nt_name, NULL, NULL )) return FALSE;
2991 attr.Length = sizeof(attr);
2992 attr.RootDirectory = 0;
2993 attr.ObjectName = &nt_name;
2994 attr.Attributes = OBJ_CASE_INSENSITIVE;
2995 attr.SecurityDescriptor = NULL;
2996 attr.SecurityQualityOfService = NULL;
2998 ret = NtQueryAttributesFile(&attr, &basic_info) == STATUS_SUCCESS;
3000 RtlFreeUnicodeString( &nt_name );
3001 return ret;
3005 /***********************************************************************
3006 * DIR_unmount_device
3008 * Unmount the specified device.
3010 NTSTATUS DIR_unmount_device( HANDLE handle )
3012 NTSTATUS status;
3013 int unix_fd, needs_close;
3015 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
3017 struct stat st;
3018 char *mount_point = NULL;
3020 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
3021 status = STATUS_INVALID_PARAMETER;
3022 else
3024 if ((mount_point = get_device_mount_point( st.st_rdev )))
3026 #ifdef __APPLE__
3027 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
3028 #else
3029 static const char umount[] = "umount >/dev/null 2>&1 ";
3030 #endif
3031 char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount));
3032 if (cmd)
3034 strcpy( cmd, umount );
3035 strcat( cmd, mount_point );
3036 system( cmd );
3037 RtlFreeHeap( GetProcessHeap(), 0, cmd );
3038 #ifdef linux
3039 /* umount will fail to release the loop device since we still have
3040 a handle to it, so we release it here */
3041 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3042 #endif
3044 RtlFreeHeap( GetProcessHeap(), 0, mount_point );
3047 if (needs_close) close( unix_fd );
3049 return status;
3053 /******************************************************************************
3054 * DIR_get_unix_cwd
3056 * Retrieve the Unix name of the current directory; helper for wine_unix_to_nt_file_name.
3057 * Returned value must be freed by caller.
3059 NTSTATUS DIR_get_unix_cwd( char **cwd )
3061 int old_cwd, unix_fd, needs_close;
3062 CURDIR *curdir;
3063 HANDLE handle;
3064 NTSTATUS status;
3066 RtlAcquirePebLock();
3068 if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */
3069 curdir = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir;
3070 else
3071 curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory;
3073 if (!(handle = curdir->Handle))
3075 UNICODE_STRING dirW;
3076 OBJECT_ATTRIBUTES attr;
3077 IO_STATUS_BLOCK io;
3079 if (!RtlDosPathNameToNtPathName_U( curdir->DosPath.Buffer, &dirW, NULL, NULL ))
3081 status = STATUS_OBJECT_NAME_INVALID;
3082 goto done;
3084 attr.Length = sizeof(attr);
3085 attr.RootDirectory = 0;
3086 attr.Attributes = OBJ_CASE_INSENSITIVE;
3087 attr.ObjectName = &dirW;
3088 attr.SecurityDescriptor = NULL;
3089 attr.SecurityQualityOfService = NULL;
3091 status = NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0,
3092 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
3093 RtlFreeUnicodeString( &dirW );
3094 if (status != STATUS_SUCCESS) goto done;
3097 if ((status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )) == STATUS_SUCCESS)
3099 RtlEnterCriticalSection( &dir_section );
3101 if ((old_cwd = open(".", O_RDONLY)) != -1 && fchdir( unix_fd ) != -1)
3103 unsigned int size = 512;
3105 for (;;)
3107 if (!(*cwd = RtlAllocateHeap( GetProcessHeap(), 0, size )))
3109 status = STATUS_NO_MEMORY;
3110 break;
3112 if (getcwd( *cwd, size )) break;
3113 RtlFreeHeap( GetProcessHeap(), 0, *cwd );
3114 if (errno != ERANGE)
3116 status = STATUS_OBJECT_PATH_INVALID;
3117 break;
3119 size *= 2;
3121 if (fchdir( old_cwd ) == -1) chdir( "/" );
3123 else status = FILE_GetNtStatus();
3125 RtlLeaveCriticalSection( &dir_section );
3126 if (old_cwd != -1) close( old_cwd );
3127 if (needs_close) close( unix_fd );
3129 if (!curdir->Handle) NtClose( handle );
3131 done:
3132 RtlReleasePebLock();
3133 return status;