ntdll: Improve stub of NtQueryEaFile.
[wine.git] / dlls / ntdll / unix / file.c
blob604ca866890a0bb175589450993b5cd33a869ed9
1 /*
2 * NTDLL directory and file 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 #if 0
24 #pragma makedep unix
25 #endif
27 #include "config.h"
29 #include <assert.h>
30 #include <sys/types.h>
31 #include <dirent.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <limits.h>
40 #include <unistd.h>
41 #ifdef HAVE_MNTENT_H
42 #include <mntent.h>
43 #endif
44 #include <poll.h>
45 #include <sys/stat.h>
46 #ifdef HAVE_SYS_STATVFS_H
47 # include <sys/statvfs.h>
48 #endif
49 #ifdef HAVE_SYS_SYSCALL_H
50 # include <sys/syscall.h>
51 #endif
52 #include <sys/socket.h>
53 #include <sys/time.h>
54 #include <sys/ioctl.h>
55 #ifdef HAVE_SYS_ATTR_H
56 #include <sys/attr.h>
57 #endif
58 #ifdef MAJOR_IN_MKDEV
59 # include <sys/mkdev.h>
60 #elif defined(MAJOR_IN_SYSMACROS)
61 # include <sys/sysmacros.h>
62 #endif
63 #ifdef HAVE_SYS_VNODE_H
64 # ifdef HAVE_STDINT_H
65 # include <stdint.h> /* needed for kfreebsd */
66 # endif
67 /* Work around a conflict with Solaris' system list defined in sys/list.h. */
68 #define list SYSLIST
69 #define list_next SYSLIST_NEXT
70 #define list_prev SYSLIST_PREV
71 #define list_head SYSLIST_HEAD
72 #define list_tail SYSLIST_TAIL
73 #define list_move_tail SYSLIST_MOVE_TAIL
74 #define list_remove SYSLIST_REMOVE
75 #include <sys/vnode.h>
76 #undef list
77 #undef list_next
78 #undef list_prev
79 #undef list_head
80 #undef list_tail
81 #undef list_move_tail
82 #undef list_remove
83 #endif
84 #ifdef HAVE_LINUX_IOCTL_H
85 #include <linux/ioctl.h>
86 #endif
87 #ifdef HAVE_LINUX_MAJOR_H
88 # include <linux/major.h>
89 #endif
90 #ifdef HAVE_SYS_PARAM_H
91 #include <sys/param.h>
92 #endif
93 #ifdef HAVE_SYS_CONF_H
94 #include <sys/conf.h>
95 #endif
96 #ifdef HAVE_SYS_MOUNT_H
97 #include <sys/mount.h>
98 #endif
99 #ifdef HAVE_SYS_STATFS_H
100 #include <sys/statfs.h>
101 #endif
102 #ifdef HAVE_SYS_XATTR_H
103 #include <sys/xattr.h>
104 #endif
105 #include <time.h>
106 #include <unistd.h>
108 #include "ntstatus.h"
109 #define WIN32_NO_STATUS
110 #define NONAMELESSUNION
111 #include "windef.h"
112 #include "winnt.h"
113 #include "winioctl.h"
114 #include "winternl.h"
115 #include "ddk/ntddk.h"
116 #include "ddk/ntddser.h"
117 #include "ddk/wdm.h"
118 #define WINE_MOUNTMGR_EXTENSIONS
119 #include "ddk/mountmgr.h"
120 #include "wine/server.h"
121 #include "wine/list.h"
122 #include "wine/debug.h"
123 #include "unix_private.h"
125 WINE_DEFAULT_DEBUG_CHANNEL(file);
126 WINE_DECLARE_DEBUG_CHANNEL(winediag);
128 #define MAX_DOS_DRIVES 26
130 /* just in case... */
131 #undef VFAT_IOCTL_READDIR_BOTH
132 #undef EXT2_IOC_GETFLAGS
133 #undef EXT4_CASEFOLD_FL
135 #ifdef linux
137 /* We want the real kernel dirent structure, not the libc one */
138 typedef struct
140 long d_ino;
141 long d_off;
142 unsigned short d_reclen;
143 char d_name[256];
144 } KERNEL_DIRENT;
146 /* Define the VFAT ioctl to get both short and long file names */
147 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
149 /* Define the ext2 ioctl for handling extra attributes */
150 #define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
152 /* Case-insensitivity attribute */
153 #define EXT4_CASEFOLD_FL 0x40000000
155 #ifndef O_DIRECTORY
156 # define O_DIRECTORY 0200000 /* must be directory */
157 #endif
159 #ifndef AT_NO_AUTOMOUNT
160 #define AT_NO_AUTOMOUNT 0x800
161 #endif
163 #endif /* linux */
165 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
167 #define INVALID_NT_CHARS '*','?','<','>','|','"'
168 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
170 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
172 #define MAX_IGNORED_FILES 4
174 #define SAMBA_XATTR_DOS_ATTRIB "user.DOSATTRIB"
175 #define XATTR_ATTRIBS_MASK (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)
177 struct file_identity
179 dev_t dev;
180 ino_t ino;
183 static struct file_identity ignored_files[MAX_IGNORED_FILES];
184 static unsigned int ignored_files_count;
186 union file_directory_info
188 ULONG next;
189 FILE_DIRECTORY_INFORMATION dir;
190 FILE_BOTH_DIRECTORY_INFORMATION both;
191 FILE_FULL_DIRECTORY_INFORMATION full;
192 FILE_ID_BOTH_DIRECTORY_INFORMATION id_both;
193 FILE_ID_FULL_DIRECTORY_INFORMATION id_full;
194 FILE_ID_GLOBAL_TX_DIR_INFORMATION id_tx;
195 FILE_NAMES_INFORMATION names;
198 struct dir_data_buffer
200 struct dir_data_buffer *next; /* next buffer in the list */
201 unsigned int size; /* total size of the buffer */
202 unsigned int pos; /* current position in the buffer */
203 char data[1];
206 struct dir_data_names
208 const WCHAR *long_name; /* long file name in Unicode */
209 const WCHAR *short_name; /* short file name in Unicode */
210 const char *unix_name; /* Unix file name in host encoding */
213 struct dir_data
215 unsigned int size; /* size of the names array */
216 unsigned int count; /* count of used entries in the names array */
217 unsigned int pos; /* current reading position in the names array */
218 struct file_identity id; /* directory file identity */
219 struct dir_data_names *names; /* directory file names */
220 struct dir_data_buffer *buffer; /* head of data buffers list */
223 static const unsigned int dir_data_buffer_initial_size = 4096;
224 static const unsigned int dir_data_cache_initial_size = 256;
225 static const unsigned int dir_data_names_initial_size = 64;
227 static struct dir_data **dir_data_cache;
228 static unsigned int dir_data_cache_size;
230 static BOOL show_dot_files;
231 static mode_t start_umask;
233 /* at some point we may want to allow Winelib apps to set this */
234 static const BOOL is_case_sensitive = FALSE;
236 static pthread_mutex_t dir_mutex = PTHREAD_MUTEX_INITIALIZER;
237 static pthread_mutex_t mnt_mutex = PTHREAD_MUTEX_INITIALIZER;
239 /* check if a given Unicode char is OK in a DOS short name */
240 static inline BOOL is_invalid_dos_char( WCHAR ch )
242 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
243 if (ch > 0x7f) return TRUE;
244 return wcschr( invalid_chars, ch ) != NULL;
247 /* check if the device can be a mounted volume */
248 static inline BOOL is_valid_mounted_device( const struct stat *st )
250 #if defined(linux) || defined(__sun__)
251 return S_ISBLK( st->st_mode );
252 #else
253 /* disks are char devices on *BSD */
254 return S_ISCHR( st->st_mode );
255 #endif
258 static inline void ignore_file( const char *name )
260 struct stat st;
261 assert( ignored_files_count < MAX_IGNORED_FILES );
262 if (!stat( name, &st ))
264 ignored_files[ignored_files_count].dev = st.st_dev;
265 ignored_files[ignored_files_count].ino = st.st_ino;
266 ignored_files_count++;
270 static inline BOOL is_same_file( const struct file_identity *file, const struct stat *st )
272 return st->st_dev == file->dev && st->st_ino == file->ino;
275 static inline BOOL is_ignored_file( const struct stat *st )
277 unsigned int i;
279 for (i = 0; i < ignored_files_count; i++)
280 if (is_same_file( &ignored_files[i], st )) return TRUE;
281 return FALSE;
284 static inline unsigned int dir_info_align( unsigned int len )
286 return (len + 7) & ~7;
289 static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned int len )
291 switch (class)
293 case FileDirectoryInformation:
294 return offsetof( FILE_DIRECTORY_INFORMATION, FileName[len] );
295 case FileBothDirectoryInformation:
296 return offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[len] );
297 case FileFullDirectoryInformation:
298 return offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[len] );
299 case FileIdBothDirectoryInformation:
300 return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[len] );
301 case FileIdFullDirectoryInformation:
302 return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[len] );
303 case FileIdGlobalTxDirectoryInformation:
304 return offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[len] );
305 case FileNamesInformation:
306 return offsetof( FILE_NAMES_INFORMATION, FileName[len] );
307 default:
308 assert(0);
309 return 0;
313 static inline BOOL has_wildcard( const UNICODE_STRING *mask )
315 int i;
317 if (!mask) return TRUE;
318 for (i = 0; i < mask->Length / sizeof(WCHAR); i++)
319 if (mask->Buffer[i] == '*' || mask->Buffer[i] == '?') return TRUE;
320 return FALSE;
323 NTSTATUS errno_to_status( int err )
325 TRACE( "errno = %d\n", err );
326 switch (err)
328 case EAGAIN: return STATUS_SHARING_VIOLATION;
329 case EBADF: return STATUS_INVALID_HANDLE;
330 case EBUSY: return STATUS_DEVICE_BUSY;
331 case ENOSPC: return STATUS_DISK_FULL;
332 case EPERM:
333 case EROFS:
334 case EACCES: return STATUS_ACCESS_DENIED;
335 case ENOTDIR: return STATUS_OBJECT_PATH_NOT_FOUND;
336 case ENOENT: return STATUS_OBJECT_NAME_NOT_FOUND;
337 case EISDIR: return STATUS_INVALID_DEVICE_REQUEST;
338 case EMFILE:
339 case ENFILE: return STATUS_TOO_MANY_OPENED_FILES;
340 case EINVAL: return STATUS_INVALID_PARAMETER;
341 case ENOTEMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
342 case EPIPE: return STATUS_PIPE_DISCONNECTED;
343 case EIO: return STATUS_DEVICE_NOT_READY;
344 #ifdef ENOMEDIUM
345 case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE;
346 #endif
347 case ENXIO: return STATUS_NO_SUCH_DEVICE;
348 case ENOTTY:
349 case EOPNOTSUPP:return STATUS_NOT_SUPPORTED;
350 case ECONNRESET:return STATUS_PIPE_DISCONNECTED;
351 case EFAULT: return STATUS_ACCESS_VIOLATION;
352 case ESPIPE: return STATUS_ILLEGAL_FUNCTION;
353 case ELOOP: return STATUS_REPARSE_POINT_NOT_RESOLVED;
354 #ifdef ETIME /* Missing on FreeBSD */
355 case ETIME: return STATUS_IO_TIMEOUT;
356 #endif
357 case ENOEXEC: /* ?? */
358 case EEXIST: /* ?? */
359 default:
360 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
361 return STATUS_UNSUCCESSFUL;
366 static int xattr_fremove( int filedes, const char *name )
368 #ifdef HAVE_SYS_XATTR_H
369 # ifdef XATTR_ADDITIONAL_OPTIONS
370 return fremovexattr( filedes, name, 0 );
371 # else
372 return fremovexattr( filedes, name );
373 # endif
374 #else
375 errno = ENOSYS;
376 return -1;
377 #endif
381 static int xattr_fset( int filedes, const char *name, const void *value, size_t size )
383 #ifdef HAVE_SYS_XATTR_H
384 # ifdef XATTR_ADDITIONAL_OPTIONS
385 return fsetxattr( filedes, name, value, size, 0, 0 );
386 # else
387 return fsetxattr( filedes, name, value, size, 0 );
388 # endif
389 #else
390 errno = ENOSYS;
391 return -1;
392 #endif
396 static int xattr_get( const char *path, const char *name, void *value, size_t size )
398 #ifdef HAVE_SYS_XATTR_H
399 # ifdef XATTR_ADDITIONAL_OPTIONS
400 return getxattr( path, name, value, size, 0, 0 );
401 # else
402 return getxattr( path, name, value, size );
403 # endif
404 #else
405 errno = ENOSYS;
406 return -1;
407 #endif
411 static int xattr_fget( int filedes, const char *name, void *value, size_t size )
413 #ifdef HAVE_SYS_XATTR_H
414 # ifdef XATTR_ADDITIONAL_OPTIONS
415 return fgetxattr( filedes, name, value, size, 0, 0 );
416 # else
417 return fgetxattr( filedes, name, value, size );
418 # endif
419 #else
420 errno = ENOSYS;
421 return -1;
422 #endif
426 /* get space from the current directory data buffer, allocating a new one if necessary */
427 static void *get_dir_data_space( struct dir_data *data, unsigned int size )
429 struct dir_data_buffer *buffer = data->buffer;
430 void *ret;
432 if (!buffer || size > buffer->size - buffer->pos)
434 unsigned int new_size = buffer ? buffer->size * 2 : dir_data_buffer_initial_size;
435 if (new_size < size) new_size = size;
436 if (!(buffer = malloc( offsetof( struct dir_data_buffer, data[new_size] ) ))) return NULL;
437 buffer->pos = 0;
438 buffer->size = new_size;
439 buffer->next = data->buffer;
440 data->buffer = buffer;
442 ret = buffer->data + buffer->pos;
443 buffer->pos += size;
444 return ret;
447 /* add a string to the directory data buffer */
448 static const char *add_dir_data_nameA( struct dir_data *data, const char *name )
450 /* keep buffer data WCHAR-aligned */
451 char *ptr = get_dir_data_space( data, (strlen( name ) + sizeof(WCHAR)) & ~(sizeof(WCHAR) - 1) );
452 if (ptr) strcpy( ptr, name );
453 return ptr;
456 /* add a Unicode string to the directory data buffer */
457 static const WCHAR *add_dir_data_nameW( struct dir_data *data, const WCHAR *name )
459 WCHAR *ptr = get_dir_data_space( data, (wcslen( name ) + 1) * sizeof(WCHAR) );
460 if (ptr) wcscpy( ptr, name );
461 return ptr;
464 /* add an entry to the directory names array */
465 static BOOL add_dir_data_names( struct dir_data *data, const WCHAR *long_name,
466 const WCHAR *short_name, const char *unix_name )
468 static const WCHAR empty[1];
469 struct dir_data_names *names = data->names;
471 if (data->count >= data->size)
473 unsigned int new_size = max( data->size * 2, dir_data_names_initial_size );
475 if (!(names = realloc( names, new_size * sizeof(*names) ))) return FALSE;
476 data->size = new_size;
477 data->names = names;
480 if (short_name[0])
482 if (!(names[data->count].short_name = add_dir_data_nameW( data, short_name ))) return FALSE;
484 else names[data->count].short_name = empty;
486 if (!(names[data->count].long_name = add_dir_data_nameW( data, long_name ))) return FALSE;
487 if (!(names[data->count].unix_name = add_dir_data_nameA( data, unix_name ))) return FALSE;
488 data->count++;
489 return TRUE;
492 /* free the complete directory data structure */
493 static void free_dir_data( struct dir_data *data )
495 struct dir_data_buffer *buffer, *next;
497 if (!data) return;
499 for (buffer = data->buffer; buffer; buffer = next)
501 next = buffer->next;
502 free( buffer );
504 free( data->names );
505 free( data );
509 /* support for a directory queue for filesystem searches */
511 struct dir_name
513 struct list entry;
514 char name[1];
517 static struct list dir_queue = LIST_INIT( dir_queue );
519 static NTSTATUS add_dir_to_queue( const char *name )
521 int len = strlen( name ) + 1;
522 struct dir_name *dir = malloc( offsetof( struct dir_name, name[len] ));
523 if (!dir) return STATUS_NO_MEMORY;
524 strcpy( dir->name, name );
525 list_add_tail( &dir_queue, &dir->entry );
526 return STATUS_SUCCESS;
529 static NTSTATUS next_dir_in_queue( char *name )
531 struct list *head = list_head( &dir_queue );
532 if (head)
534 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
535 strcpy( name, dir->name );
536 list_remove( &dir->entry );
537 free( dir );
538 return STATUS_SUCCESS;
540 return STATUS_OBJECT_NAME_NOT_FOUND;
543 static void flush_dir_queue(void)
545 struct list *head;
547 while ((head = list_head( &dir_queue )))
549 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
550 list_remove( &dir->entry );
551 free( dir );
556 #ifdef __ANDROID__
558 static char *unescape_field( char *str )
560 char *in, *out;
562 for (in = out = str; *in; in++, out++)
564 *out = *in;
565 if (in[0] == '\\')
567 if (in[1] == '\\')
569 out[0] = '\\';
570 in++;
572 else if (in[1] == '0' && in[2] == '4' && in[3] == '0')
574 out[0] = ' ';
575 in += 3;
577 else if (in[1] == '0' && in[2] == '1' && in[3] == '1')
579 out[0] = '\t';
580 in += 3;
582 else if (in[1] == '0' && in[2] == '1' && in[3] == '2')
584 out[0] = '\n';
585 in += 3;
587 else if (in[1] == '1' && in[2] == '3' && in[3] == '4')
589 out[0] = '\\';
590 in += 3;
594 *out = '\0';
596 return str;
599 static inline char *get_field( char **str )
601 char *ret;
603 ret = strsep( str, " \t" );
604 if (*str) *str += strspn( *str, " \t" );
606 return ret;
608 /************************************************************************
609 * getmntent_replacement
611 * getmntent replacement for Android.
613 * NB returned static buffer is not thread safe; protect with mnt_mutex.
615 static struct mntent *getmntent_replacement( FILE *f )
617 static struct mntent entry;
618 static char buf[4096];
619 char *p, *start;
623 if (!fgets( buf, sizeof(buf), f )) return NULL;
624 p = strchr( buf, '\n' );
625 if (p) *p = '\0';
626 else /* Partially unread line, move file ptr to end */
628 char tmp[1024];
629 while (fgets( tmp, sizeof(tmp), f ))
630 if (strchr( tmp, '\n' )) break;
632 start = buf + strspn( buf, " \t" );
633 } while (start[0] == '\0' || start[0] == '#');
635 p = get_field( &start );
636 entry.mnt_fsname = p ? unescape_field( p ) : (char *)"";
638 p = get_field( &start );
639 entry.mnt_dir = p ? unescape_field( p ) : (char *)"";
641 p = get_field( &start );
642 entry.mnt_type = p ? unescape_field( p ) : (char *)"";
644 p = get_field( &start );
645 entry.mnt_opts = p ? unescape_field( p ) : (char *)"";
647 p = get_field( &start );
648 entry.mnt_freq = p ? atoi(p) : 0;
650 p = get_field( &start );
651 entry.mnt_passno = p ? atoi(p) : 0;
653 return &entry;
655 #define getmntent getmntent_replacement
656 #endif
658 /***********************************************************************
659 * parse_mount_entries
661 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
664 #ifdef sun
665 #include <sys/vfstab.h>
666 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
668 struct vfstab entry;
669 struct stat st;
670 char *device;
672 while (! getvfsent( f, &entry ))
674 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
675 if (!strcmp( entry.vfs_fstype, "nfs" ) ||
676 !strcmp( entry.vfs_fstype, "smbfs" ) ||
677 !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
679 if (stat( entry.vfs_mountp, &st ) == -1) continue;
680 if (st.st_dev != dev || st.st_ino != ino) continue;
681 if (!strcmp( entry.vfs_fstype, "fd" ))
683 if ((device = strstr( entry.vfs_mntopts, "dev=" )))
685 char *p = strchr( device + 4, ',' );
686 if (p) *p = 0;
687 return device + 4;
690 else
691 return entry.vfs_special;
693 return NULL;
695 #endif
697 #ifdef linux
698 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
700 struct mntent *entry;
701 struct stat st;
702 char *device;
704 while ((entry = getmntent( f )))
706 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
707 if (!strcmp( entry->mnt_type, "nfs" ) ||
708 !strcmp( entry->mnt_type, "cifs" ) ||
709 !strcmp( entry->mnt_type, "smbfs" ) ||
710 !strcmp( entry->mnt_type, "ncpfs" )) continue;
712 if (stat( entry->mnt_dir, &st ) == -1) continue;
713 if (st.st_dev != dev || st.st_ino != ino) continue;
714 if (!strcmp( entry->mnt_type, "supermount" ))
716 if ((device = strstr( entry->mnt_opts, "dev=" )))
718 char *p = strchr( device + 4, ',' );
719 if (p) *p = 0;
720 return device + 4;
723 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
725 /* if device is a regular file check for a loop mount */
726 if ((device = strstr( entry->mnt_opts, "loop=" )))
728 char *p = strchr( device + 5, ',' );
729 if (p) *p = 0;
730 return device + 5;
733 else
734 return entry->mnt_fsname;
736 return NULL;
738 #endif
740 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
741 #include <fstab.h>
742 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
744 struct fstab *entry;
745 struct stat st;
747 while ((entry = getfsent()))
749 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
750 if (!strcmp( entry->fs_vfstype, "nfs" ) ||
751 !strcmp( entry->fs_vfstype, "smbfs" ) ||
752 !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
754 if (stat( entry->fs_file, &st ) == -1) continue;
755 if (st.st_dev != dev || st.st_ino != ino) continue;
756 return entry->fs_spec;
758 return NULL;
760 #endif
762 #ifdef sun
763 #include <sys/mnttab.h>
764 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
766 struct mnttab entry;
767 struct stat st;
768 char *device;
771 while (( ! getmntent( f, &entry) ))
773 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
774 if (!strcmp( entry.mnt_fstype, "nfs" ) ||
775 !strcmp( entry.mnt_fstype, "smbfs" ) ||
776 !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
778 if (stat( entry.mnt_mountp, &st ) == -1) continue;
779 if (st.st_dev != dev || st.st_ino != ino) continue;
780 if (!strcmp( entry.mnt_fstype, "fd" ))
782 if ((device = strstr( entry.mnt_mntopts, "dev=" )))
784 char *p = strchr( device + 4, ',' );
785 if (p) *p = 0;
786 return device + 4;
789 else
790 return entry.mnt_special;
792 return NULL;
794 #endif
796 /***********************************************************************
797 * get_default_drive_device
799 * Return the default device to use for a given drive mount point.
801 static char *get_default_drive_device( const char *root )
803 char *ret = NULL;
805 #ifdef linux
806 FILE *f;
807 char *device = NULL;
808 int fd, res = -1;
809 struct stat st;
811 /* try to open it first to force it to get mounted */
812 if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
814 res = fstat( fd, &st );
815 close( fd );
817 /* now try normal stat just in case */
818 if (res == -1) res = stat( root, &st );
819 if (res == -1) return NULL;
821 mutex_lock( &mnt_mutex );
823 #ifdef __ANDROID__
824 if ((f = fopen( "/proc/mounts", "r" )))
826 device = parse_mount_entries( f, st.st_dev, st.st_ino );
827 fclose( f );
829 #else
830 if ((f = fopen( "/etc/mtab", "r" )))
832 device = parse_mount_entries( f, st.st_dev, st.st_ino );
833 fclose( f );
835 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
836 if (!device && (f = fopen( "/etc/fstab", "r" )))
838 device = parse_mount_entries( f, st.st_dev, st.st_ino );
839 fclose( f );
841 #endif
842 if (device) ret = strdup( device );
843 mutex_unlock( &mnt_mutex );
845 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
846 char *device = NULL;
847 int fd, res = -1;
848 struct stat st;
850 /* try to open it first to force it to get mounted */
851 if ((fd = open( root, O_RDONLY )) != -1)
853 res = fstat( fd, &st );
854 close( fd );
856 /* now try normal stat just in case */
857 if (res == -1) res = stat( root, &st );
858 if (res == -1) return NULL;
860 mutex_lock( &mnt_mutex );
862 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
863 * pass NULL. Leave the argument in for symmetry.
865 device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
866 if (device) ret = strdup( device );
867 mutex_unlock( &mnt_mutex );
869 #elif defined( sun )
870 FILE *f;
871 char *device = NULL;
872 int fd, res = -1;
873 struct stat st;
875 /* try to open it first to force it to get mounted */
876 if ((fd = open( root, O_RDONLY )) != -1)
878 res = fstat( fd, &st );
879 close( fd );
881 /* now try normal stat just in case */
882 if (res == -1) res = stat( root, &st );
883 if (res == -1) return NULL;
885 mutex_lock( &mnt_mutex );
887 if ((f = fopen( "/etc/mnttab", "r" )))
889 device = parse_mount_entries( f, st.st_dev, st.st_ino);
890 fclose( f );
892 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
893 if (!device && (f = fopen( "/etc/vfstab", "r" )))
895 device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
896 fclose( f );
898 if (device) ret = strdup( device );
899 mutex_unlock( &mnt_mutex );
901 #elif defined(__APPLE__)
902 struct statfs *mntStat;
903 struct stat st;
904 int i;
905 int mntSize;
906 dev_t dev;
907 ino_t ino;
908 static const char path_bsd_device[] = "/dev/disk";
909 int res;
911 res = stat( root, &st );
912 if (res == -1) return NULL;
914 dev = st.st_dev;
915 ino = st.st_ino;
917 mutex_lock( &mnt_mutex );
919 mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
921 for (i = 0; i < mntSize && !ret; i++)
923 if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
924 if (st.st_dev != dev || st.st_ino != ino) continue;
926 /* FIXME add support for mounted network drive */
927 if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
929 /* set return value to the corresponding raw BSD node */
930 ret = malloc( strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
931 if (ret)
933 strcpy(ret, "/dev/r");
934 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
938 mutex_unlock( &mnt_mutex );
939 #else
940 static int warned;
941 if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
942 #endif
943 return ret;
947 /***********************************************************************
948 * get_device_mount_point
950 * Return the current mount point for a device.
952 static char *get_device_mount_point( dev_t dev )
954 char *ret = NULL;
956 #ifdef linux
957 FILE *f;
959 mutex_lock( &mnt_mutex );
961 #ifdef __ANDROID__
962 if ((f = fopen( "/proc/mounts", "r" )))
963 #else
964 if ((f = fopen( "/etc/mtab", "r" )))
965 #endif
967 struct mntent *entry;
968 struct stat st;
969 char *p, *device;
971 while ((entry = getmntent( f )))
973 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
974 if (!strcmp( entry->mnt_type, "nfs" ) ||
975 !strcmp( entry->mnt_type, "cifs" ) ||
976 !strcmp( entry->mnt_type, "smbfs" ) ||
977 !strcmp( entry->mnt_type, "ncpfs" )) continue;
979 if (!strcmp( entry->mnt_type, "supermount" ))
981 if ((device = strstr( entry->mnt_opts, "dev=" )))
983 device += 4;
984 if ((p = strchr( device, ',' ))) *p = 0;
987 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
989 /* if device is a regular file check for a loop mount */
990 if ((device = strstr( entry->mnt_opts, "loop=" )))
992 device += 5;
993 if ((p = strchr( device, ',' ))) *p = 0;
996 else device = entry->mnt_fsname;
998 if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
1000 ret = strdup( entry->mnt_dir );
1001 break;
1004 fclose( f );
1006 mutex_unlock( &mnt_mutex );
1007 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1008 struct statfs *entry;
1009 struct stat st;
1010 int i, size;
1012 mutex_lock( &mnt_mutex );
1014 size = getmntinfo( &entry, MNT_NOWAIT );
1015 for (i = 0; i < size; i++)
1017 if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
1018 if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
1020 ret = strdup( entry[i].f_mntonname );
1021 break;
1024 mutex_unlock( &mnt_mutex );
1025 #else
1026 static int warned;
1027 if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
1028 #endif
1029 return ret;
1033 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1034 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1036 struct get_fsid
1038 ULONG size;
1039 dev_t dev;
1040 fsid_t fsid;
1043 struct fs_cache
1045 dev_t dev;
1046 fsid_t fsid;
1047 BOOLEAN case_sensitive;
1048 } fs_cache[64];
1050 struct vol_caps
1052 ULONG size;
1053 vol_capabilities_attr_t caps;
1056 /***********************************************************************
1057 * look_up_fs_cache
1059 * Checks if the specified file system is in the cache.
1061 static struct fs_cache *look_up_fs_cache( dev_t dev )
1063 int i;
1064 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1065 if (fs_cache[i].dev == dev)
1066 return fs_cache+i;
1067 return NULL;
1070 /***********************************************************************
1071 * add_fs_cache
1073 * Adds the specified file system to the cache.
1075 static void add_fs_cache( dev_t dev, fsid_t fsid, BOOLEAN case_sensitive )
1077 int i;
1078 struct fs_cache *entry = look_up_fs_cache( dev );
1079 static int once = 0;
1080 if (entry)
1082 /* Update the cache */
1083 entry->fsid = fsid;
1084 entry->case_sensitive = case_sensitive;
1085 return;
1088 /* Add a new entry */
1089 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1090 if (fs_cache[i].dev == 0)
1092 /* This entry is empty, use it */
1093 fs_cache[i].dev = dev;
1094 fs_cache[i].fsid = fsid;
1095 fs_cache[i].case_sensitive = case_sensitive;
1096 return;
1099 /* Cache is out of space, warn */
1100 if (!once++)
1101 WARN( "FS cache is out of space, expect performance problems\n" );
1104 /***********************************************************************
1105 * get_dir_case_sensitivity_attr
1107 * Checks if the volume containing the specified directory is case
1108 * sensitive or not. Uses getattrlist(2).
1110 static int get_dir_case_sensitivity_attr( const char *dir )
1112 char *mntpoint;
1113 struct attrlist attr;
1114 struct vol_caps caps;
1115 struct get_fsid get_fsid;
1116 struct fs_cache *entry;
1118 /* First get the FS ID of the volume */
1119 attr.bitmapcount = ATTR_BIT_MAP_COUNT;
1120 attr.reserved = 0;
1121 attr.commonattr = ATTR_CMN_DEVID|ATTR_CMN_FSID;
1122 attr.volattr = attr.dirattr = attr.fileattr = attr.forkattr = 0;
1123 get_fsid.size = 0;
1124 if (getattrlist( dir, &attr, &get_fsid, sizeof(get_fsid), 0 ) != 0 ||
1125 get_fsid.size != sizeof(get_fsid))
1126 return -1;
1127 /* Try to look it up in the cache */
1128 entry = look_up_fs_cache( get_fsid.dev );
1129 if (entry && !memcmp( &entry->fsid, &get_fsid.fsid, sizeof(fsid_t) ))
1130 /* Cache lookup succeeded */
1131 return entry->case_sensitive;
1132 /* Cache is stale at this point, we have to update it */
1134 mntpoint = get_device_mount_point( get_fsid.dev );
1135 /* Now look up the case-sensitivity */
1136 attr.commonattr = 0;
1137 attr.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES;
1138 if (getattrlist( mntpoint, &attr, &caps, sizeof(caps), 0 ) < 0)
1140 free( mntpoint );
1141 add_fs_cache( get_fsid.dev, get_fsid.fsid, TRUE );
1142 return TRUE;
1144 free( mntpoint );
1145 if (caps.size == sizeof(caps) &&
1146 (caps.caps.valid[VOL_CAPABILITIES_FORMAT] &
1147 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING)) ==
1148 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING))
1150 BOOLEAN ret;
1152 if ((caps.caps.capabilities[VOL_CAPABILITIES_FORMAT] &
1153 VOL_CAP_FMT_CASE_SENSITIVE) != VOL_CAP_FMT_CASE_SENSITIVE)
1154 ret = FALSE;
1155 else
1156 ret = TRUE;
1157 /* Update the cache */
1158 add_fs_cache( get_fsid.dev, get_fsid.fsid, ret );
1159 return ret;
1161 return FALSE;
1163 #endif
1165 /***********************************************************************
1166 * get_dir_case_sensitivity_stat
1168 * Checks if the volume containing the specified directory is case
1169 * sensitive or not. Uses (f)statfs(2), statvfs(2), fstatat(2), or ioctl(2).
1171 static BOOLEAN get_dir_case_sensitivity_stat( const char *dir )
1173 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1174 struct statfs stfs;
1176 if (statfs( dir, &stfs ) == -1) return TRUE;
1177 /* Assume these file systems are always case insensitive.*/
1178 if (!strcmp( stfs.f_fstypename, "fusefs" ) &&
1179 !strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1180 return FALSE;
1181 /* msdosfs was case-insensitive since FreeBSD 8, if not earlier */
1182 if (!strcmp( stfs.f_fstypename, "msdosfs" ) ||
1183 /* older CIFS protocol versions uppercase filename on the client,
1184 * newer versions should be case-insensitive on the server anyway */
1185 !strcmp( stfs.f_fstypename, "smbfs" ))
1186 return FALSE;
1187 /* no ntfs-3g: modern fusefs has no way to report the filesystem on FreeBSD
1188 * no cd9660 or udf, they're case-sensitive on FreeBSD
1190 #ifdef __APPLE__
1191 if (!strcmp( stfs.f_fstypename, "msdos" ) ||
1192 !strcmp( stfs.f_fstypename, "cd9660" ) ||
1193 !strcmp( stfs.f_fstypename, "udf" ) ||
1194 !strcmp( stfs.f_fstypename, "ntfs" ))
1195 return FALSE;
1196 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1197 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_fssubtype == 0 ||
1198 stfs.f_fssubtype == 1 ||
1199 stfs.f_fssubtype == 128))
1200 return FALSE;
1201 #else
1202 /* The field says "reserved", but a quick look at the kernel source
1203 * tells us that this "reserved" field is really the same as the
1204 * "fssubtype" field from the inode64 structure (see munge_statfs()
1205 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1207 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_reserved1 == 0 ||
1208 stfs.f_reserved1 == 1 ||
1209 stfs.f_reserved1 == 128))
1210 return FALSE;
1211 #endif
1212 #endif
1213 return TRUE;
1215 #elif defined(__NetBSD__)
1216 struct statvfs stfs;
1218 if (statvfs( dir, &stfs ) == -1) return TRUE;
1219 /* Only assume CIOPFS is case insensitive. */
1220 if (strcmp( stfs.f_fstypename, "fusefs" ) ||
1221 strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1222 return FALSE;
1223 return TRUE;
1225 #elif defined(__linux__)
1226 BOOLEAN sens = TRUE;
1227 struct statfs stfs;
1228 struct stat st;
1229 int fd, flags;
1231 if ((fd = open( dir, O_RDONLY | O_NONBLOCK )) == -1)
1232 return TRUE;
1234 if (ioctl( fd, EXT2_IOC_GETFLAGS, &flags ) != -1 && (flags & EXT4_CASEFOLD_FL))
1236 sens = FALSE;
1238 else if (fstatfs( fd, &stfs ) == 0 && /* CIOPFS is case insensitive. Instead of */
1239 stfs.f_type == 0x65735546 /* FUSE_SUPER_MAGIC */ && /* parsing mtab to discover if the FUSE FS */
1240 fstatat( fd, ".ciopfs", &st, AT_NO_AUTOMOUNT ) == 0) /* is CIOPFS, look for .ciopfs in the dir. */
1242 sens = FALSE;
1245 close( fd );
1246 return sens;
1247 #else
1248 return TRUE;
1249 #endif
1253 /***********************************************************************
1254 * get_dir_case_sensitivity
1256 * Checks if the volume containing the specified directory is case
1257 * sensitive or not. Uses multiple methods, depending on platform.
1259 static BOOLEAN get_dir_case_sensitivity( const char *dir )
1261 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1262 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1263 int case_sensitive = get_dir_case_sensitivity_attr( dir );
1264 if (case_sensitive != -1) return case_sensitive;
1265 #endif
1266 return get_dir_case_sensitivity_stat( dir );
1270 /***********************************************************************
1271 * is_hidden_file
1273 * Check if the specified file should be hidden based on its name and the show dot files option.
1275 static BOOL is_hidden_file( const UNICODE_STRING *name )
1277 WCHAR *p, *end;
1279 if (show_dot_files) return FALSE;
1281 end = p = name->Buffer + name->Length/sizeof(WCHAR);
1282 while (p > name->Buffer && p[-1] == '\\') p--;
1283 while (p > name->Buffer && p[-1] != '\\') p--;
1284 return (p < end && *p == '.');
1288 /***********************************************************************
1289 * hash_short_file_name
1291 * Transform a Unix file name into a hashed DOS name. If the name is not a valid
1292 * DOS name, it is replaced by a hashed version that fits in 8.3 format.
1293 * 'buffer' must be at least 12 characters long.
1294 * Returns length of short name in bytes; short name is NOT null-terminated.
1296 static ULONG hash_short_file_name( const WCHAR *name, int length, LPWSTR buffer )
1298 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1300 LPCWSTR p, ext, end = name + length;
1301 LPWSTR dst;
1302 unsigned short hash;
1303 int i;
1305 /* Compute the hash code of the file name */
1306 /* If you know something about hash functions, feel free to */
1307 /* insert a better algorithm here... */
1308 if (!is_case_sensitive)
1310 for (p = name, hash = 0xbeef; p < end - 1; p++)
1311 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p) ^ (towlower(p[1]) << 8);
1312 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p); /* Last character */
1314 else
1316 for (p = name, hash = 0xbeef; p < end - 1; p++)
1317 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
1318 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
1321 /* Find last dot for start of the extension */
1322 for (p = name + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
1324 /* Copy first 4 chars, replacing invalid chars with '_' */
1325 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
1327 if (p == end || p == ext) break;
1328 *dst++ = is_invalid_dos_char(*p) ? '_' : *p;
1330 /* Pad to 5 chars with '~' */
1331 while (i-- >= 0) *dst++ = '~';
1333 /* Insert hash code converted to 3 ASCII chars */
1334 *dst++ = hash_chars[(hash >> 10) & 0x1f];
1335 *dst++ = hash_chars[(hash >> 5) & 0x1f];
1336 *dst++ = hash_chars[hash & 0x1f];
1338 /* Copy the first 3 chars of the extension (if any) */
1339 if (ext)
1341 *dst++ = '.';
1342 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
1343 *dst++ = is_invalid_dos_char(*ext) ? '_' : *ext;
1345 return dst - buffer;
1349 /***********************************************************************
1350 * match_filename
1352 * Check a long file name against a mask.
1354 * Tests (done in W95 DOS shell - case insensitive):
1355 * *.txt test1.test.txt *
1356 * *st1* test1.txt *
1357 * *.t??????.t* test1.ta.tornado.txt *
1358 * *tornado* test1.ta.tornado.txt *
1359 * t*t test1.ta.tornado.txt *
1360 * ?est* test1.txt *
1361 * ?est??? test1.txt -
1362 * *test1.txt* test1.txt *
1363 * h?l?o*t.dat hellothisisatest.dat *
1365 static BOOLEAN match_filename( const WCHAR *name, int length, const UNICODE_STRING *mask_str )
1367 BOOL mismatch;
1368 const WCHAR *mask = mask_str->Buffer;
1369 const WCHAR *name_end = name + length;
1370 const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
1371 const WCHAR *lastjoker = NULL;
1372 const WCHAR *next_to_retry = NULL;
1374 while (name < name_end && mask < mask_end)
1376 switch(*mask)
1378 case '*':
1379 mask++;
1380 while (mask < mask_end && *mask == '*') mask++; /* Skip consecutive '*' */
1381 if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
1382 lastjoker = mask;
1384 /* skip to the next match after the joker(s) */
1385 if (is_case_sensitive)
1386 while (name < name_end && (*name != *mask)) name++;
1387 else
1388 while (name < name_end && (towupper(*name) != towupper(*mask))) name++;
1389 next_to_retry = name;
1390 break;
1391 case '?':
1392 case '>':
1393 mask++;
1394 name++;
1395 break;
1396 default:
1397 if (is_case_sensitive) mismatch = (*mask != *name);
1398 else mismatch = (towupper(*mask) != towupper(*name));
1400 if (!mismatch)
1402 mask++;
1403 name++;
1404 if (mask == mask_end)
1406 if (name == name_end) return TRUE;
1407 if (lastjoker) mask = lastjoker;
1410 else /* mismatch ! */
1412 if (lastjoker) /* we had an '*', so we can try unlimitedly */
1414 mask = lastjoker;
1416 /* this scan sequence was a mismatch, so restart
1417 * 1 char after the first char we checked last time */
1418 next_to_retry++;
1419 name = next_to_retry;
1421 else return FALSE; /* bad luck */
1423 break;
1426 while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
1427 mask++; /* Ignore trailing '.' or '*' in mask */
1428 return (name == name_end && mask == mask_end);
1432 /***********************************************************************
1433 * is_legal_8dot3_name
1435 * Simplified version of RtlIsNameLegalDOS8Dot3.
1437 static BOOLEAN is_legal_8dot3_name( const WCHAR *name, int len )
1439 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,':','/','\\',0 };
1440 int i, dot = -1;
1442 if (len > 12) return FALSE;
1444 /* a starting . is invalid, except for . and .. */
1445 if (len > 0 && name[0] == '.') return (len == 1 || (len == 2 && name[1] == '.'));
1447 for (i = 0; i < len; i++)
1449 if (name[i] > 0x7f) return FALSE;
1450 if (wcschr( invalid_chars, name[i] )) return FALSE;
1451 if (name[i] == '.')
1453 if (dot != -1) return FALSE;
1454 dot = i;
1458 if (dot == -1) return (len <= 8);
1459 if (dot > 8) return FALSE;
1460 return (len - dot > 1 && len - dot < 5);
1464 /***********************************************************************
1465 * append_entry
1467 * Add a file to the directory data if it matches the mask.
1469 static BOOL append_entry( struct dir_data *data, const char *long_name,
1470 const char *short_name, const UNICODE_STRING *mask )
1472 int long_len, short_len;
1473 WCHAR long_nameW[MAX_DIR_ENTRY_LEN + 1];
1474 WCHAR short_nameW[13];
1476 long_len = ntdll_umbstowcs( long_name, strlen(long_name), long_nameW, ARRAY_SIZE(long_nameW) );
1477 if (long_len == ARRAY_SIZE(long_nameW)) return TRUE;
1478 long_nameW[long_len] = 0;
1480 if (short_name)
1482 short_len = ntdll_umbstowcs( short_name, strlen(short_name),
1483 short_nameW, ARRAY_SIZE( short_nameW ) - 1 );
1485 else /* generate a short name if necessary */
1487 short_len = 0;
1488 if (!is_legal_8dot3_name( long_nameW, long_len ))
1489 short_len = hash_short_file_name( long_nameW, long_len, short_nameW );
1491 short_nameW[short_len] = 0;
1492 wcsupr( short_nameW );
1494 TRACE( "long %s short %s mask %s\n",
1495 debugstr_w( long_nameW ), debugstr_w( short_nameW ), debugstr_us( mask ));
1497 if (mask && !match_filename( long_nameW, long_len, mask ))
1499 if (!short_len) return TRUE; /* no short name to match */
1500 if (!match_filename( short_nameW, short_len, mask )) return TRUE;
1503 return add_dir_data_names( data, long_nameW, short_nameW, long_name );
1507 /* fetch the attributes of a file */
1508 static inline ULONG get_file_attributes( const struct stat *st )
1510 ULONG attr;
1512 if (S_ISDIR(st->st_mode))
1513 attr = FILE_ATTRIBUTE_DIRECTORY;
1514 else
1515 attr = FILE_ATTRIBUTE_ARCHIVE;
1516 if (!(st->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
1517 attr |= FILE_ATTRIBUTE_READONLY;
1518 return attr;
1522 /* decode the xattr-stored DOS attributes */
1523 static int parse_samba_dos_attrib_data( char *data, int len )
1525 char *end;
1526 int val;
1528 if (len > 2 && data[0] == '0' && data[1] == 'x')
1530 data[len] = 0;
1531 val = strtol( data, &end, 16 );
1532 if (!*end) return val & XATTR_ATTRIBS_MASK;
1534 else
1536 static BOOL once;
1537 if (!once++) FIXME( "Unhandled " SAMBA_XATTR_DOS_ATTRIB " extended attribute value.\n" );
1539 return 0;
1543 static BOOL fd_is_mount_point( int fd, const struct stat *st )
1545 struct stat parent;
1546 return S_ISDIR( st->st_mode ) && !fstatat( fd, "..", &parent, 0 )
1547 && (parent.st_dev != st->st_dev || parent.st_ino == st->st_ino);
1551 /* get the stat info and file attributes for a file (by file descriptor) */
1552 static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr )
1554 char attr_data[65];
1555 int attr_len, ret;
1557 *attr = 0;
1558 ret = fstat( fd, st );
1559 if (ret == -1) return ret;
1560 *attr |= get_file_attributes( st );
1561 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1562 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st ))
1563 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1565 attr_len = xattr_fget( fd, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 );
1566 if (attr_len != -1)
1567 *attr |= parse_samba_dos_attrib_data( attr_data, attr_len );
1568 else if (errno != ENODATA && errno != ENOTSUP)
1569 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)\n",
1570 errno, strerror( errno ) );
1572 return ret;
1576 static int fd_set_dos_attrib( int fd, ULONG attr )
1578 /* we only store the HIDDEN and SYSTEM attributes */
1579 attr &= XATTR_ATTRIBS_MASK;
1580 if (attr != 0)
1582 /* encode the attributes in Samba 3 ASCII format. Samba 4 has extended
1583 * this format with more features, but retains compatibility with the
1584 * earlier format. */
1585 char data[11];
1586 int len = sprintf( data, "0x%x", attr );
1587 return xattr_fset( fd, SAMBA_XATTR_DOS_ATTRIB, data, len );
1589 else return xattr_fremove( fd, SAMBA_XATTR_DOS_ATTRIB );
1593 /* set the stat info and file attributes for a file (by file descriptor) */
1594 NTSTATUS fd_set_file_info( int fd, ULONG attr )
1596 struct stat st;
1598 if (fstat( fd, &st ) == -1) return errno_to_status( errno );
1599 if (attr & FILE_ATTRIBUTE_READONLY)
1601 if (S_ISDIR( st.st_mode))
1602 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
1603 else
1604 st.st_mode &= ~0222; /* clear write permission bits */
1606 else
1608 /* add write permission only where we already have read permission */
1609 st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~start_umask);
1611 if (fchmod( fd, st.st_mode ) == -1) return errno_to_status( errno );
1613 if (fd_set_dos_attrib( fd, attr ) == -1 && errno != ENOTSUP)
1614 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)\n",
1615 errno, strerror( errno ) );
1617 return STATUS_SUCCESS;
1621 /* get the stat info and file attributes for a file (by name) */
1622 static int get_file_info( const char *path, struct stat *st, ULONG *attr )
1624 char *parent_path;
1625 char attr_data[65];
1626 int attr_len, ret;
1628 *attr = 0;
1629 ret = lstat( path, st );
1630 if (ret == -1) return ret;
1631 if (S_ISLNK( st->st_mode ))
1633 ret = stat( path, st );
1634 if (ret == -1) return ret;
1635 /* is a symbolic link and a directory, consider these "reparse points" */
1636 if (S_ISDIR( st->st_mode )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1638 else if (S_ISDIR( st->st_mode ) && (parent_path = malloc( strlen(path) + 4 )))
1640 struct stat parent_st;
1642 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1643 strcpy( parent_path, path );
1644 strcat( parent_path, "/.." );
1645 if (!stat( parent_path, &parent_st )
1646 && (st->st_dev != parent_st.st_dev || st->st_ino == parent_st.st_ino))
1647 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1649 free( parent_path );
1651 *attr |= get_file_attributes( st );
1653 attr_len = xattr_get( path, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 );
1654 if (attr_len != -1)
1655 *attr |= parse_samba_dos_attrib_data( attr_data, attr_len );
1656 else if (errno != ENODATA && errno != ENOTSUP)
1657 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB " from \"%s\". errno %d (%s)\n",
1658 path, errno, strerror( errno ) );
1660 return ret;
1664 #if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
1665 static int futimens( int fd, const struct timespec spec[2] )
1667 return syscall( __NR_utimensat, fd, NULL, spec, 0 );
1669 #define HAVE_FUTIMENS
1670 #endif /* __ANDROID__ */
1672 #ifndef UTIME_OMIT
1673 #define UTIME_OMIT ((1 << 30) - 2)
1674 #endif
1676 static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
1677 const LARGE_INTEGER *atime, NTSTATUS *status )
1679 #ifdef HAVE_FUTIMENS
1680 struct timespec tv[2];
1682 tv[0].tv_sec = tv[1].tv_sec = 0;
1683 tv[0].tv_nsec = tv[1].tv_nsec = UTIME_OMIT;
1684 if (atime->QuadPart)
1686 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1687 tv[0].tv_nsec = (atime->QuadPart % 10000000) * 100;
1689 if (mtime->QuadPart)
1691 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1692 tv[1].tv_nsec = (mtime->QuadPart % 10000000) * 100;
1694 #ifdef __APPLE__
1695 if (!&futimens) return FALSE;
1696 #endif
1697 if (futimens( fd, tv ) == -1) *status = errno_to_status( errno );
1698 else *status = STATUS_SUCCESS;
1699 return TRUE;
1700 #else
1701 return FALSE;
1702 #endif
1706 static NTSTATUS set_file_times( int fd, const LARGE_INTEGER *mtime, const LARGE_INTEGER *atime )
1708 NTSTATUS status = STATUS_SUCCESS;
1709 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1710 struct timeval tv[2];
1711 struct stat st;
1712 #endif
1714 if (set_file_times_precise( fd, mtime, atime, &status ))
1715 return status;
1717 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1718 if (!atime->QuadPart || !mtime->QuadPart)
1721 tv[0].tv_sec = tv[0].tv_usec = 0;
1722 tv[1].tv_sec = tv[1].tv_usec = 0;
1723 if (!fstat( fd, &st ))
1725 tv[0].tv_sec = st.st_atime;
1726 tv[1].tv_sec = st.st_mtime;
1727 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1728 tv[0].tv_usec = st.st_atim.tv_nsec / 1000;
1729 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1730 tv[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
1731 #endif
1732 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1733 tv[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1734 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1735 tv[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
1736 #endif
1739 if (atime->QuadPart)
1741 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1742 tv[0].tv_usec = (atime->QuadPart % 10000000) / 10;
1744 if (mtime->QuadPart)
1746 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1747 tv[1].tv_usec = (mtime->QuadPart % 10000000) / 10;
1749 #ifdef HAVE_FUTIMES
1750 if (futimes( fd, tv ) == -1) status = errno_to_status( errno );
1751 #elif defined(HAVE_FUTIMESAT)
1752 if (futimesat( fd, NULL, tv ) == -1) status = errno_to_status( errno );
1753 #endif
1755 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1756 FIXME( "setting file times not supported\n" );
1757 status = STATUS_NOT_IMPLEMENTED;
1758 #endif
1759 return status;
1763 static inline void get_file_times( const struct stat *st, LARGE_INTEGER *mtime, LARGE_INTEGER *ctime,
1764 LARGE_INTEGER *atime, LARGE_INTEGER *creation )
1766 mtime->QuadPart = ticks_from_time_t( st->st_mtime );
1767 ctime->QuadPart = ticks_from_time_t( st->st_ctime );
1768 atime->QuadPart = ticks_from_time_t( st->st_atime );
1769 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1770 mtime->QuadPart += st->st_mtim.tv_nsec / 100;
1771 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1772 mtime->QuadPart += st->st_mtimespec.tv_nsec / 100;
1773 #endif
1774 #ifdef HAVE_STRUCT_STAT_ST_CTIM
1775 ctime->QuadPart += st->st_ctim.tv_nsec / 100;
1776 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1777 ctime->QuadPart += st->st_ctimespec.tv_nsec / 100;
1778 #endif
1779 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1780 atime->QuadPart += st->st_atim.tv_nsec / 100;
1781 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1782 atime->QuadPart += st->st_atimespec.tv_nsec / 100;
1783 #endif
1784 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1785 creation->QuadPart = ticks_from_time_t( st->st_birthtime );
1786 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
1787 creation->QuadPart += st->st_birthtim.tv_nsec / 100;
1788 #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1789 creation->QuadPart += st->st_birthtimespec.tv_nsec / 100;
1790 #endif
1791 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
1792 creation->QuadPart = ticks_from_time_t( st->__st_birthtime );
1793 #ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
1794 creation->QuadPart += st->__st_birthtim.tv_nsec / 100;
1795 #endif
1796 #else
1797 *creation = *mtime;
1798 #endif
1802 /* fill in the file information that depends on the stat and attribute info */
1803 static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr,
1804 FILE_INFORMATION_CLASS class )
1806 switch (class)
1808 case FileBasicInformation:
1810 FILE_BASIC_INFORMATION *info = ptr;
1812 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1813 &info->LastAccessTime, &info->CreationTime );
1814 info->FileAttributes = attr;
1816 break;
1817 case FileStandardInformation:
1819 FILE_STANDARD_INFORMATION *info = ptr;
1821 if ((info->Directory = S_ISDIR(st->st_mode)))
1823 info->AllocationSize.QuadPart = 0;
1824 info->EndOfFile.QuadPart = 0;
1825 info->NumberOfLinks = 1;
1827 else
1829 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1830 info->EndOfFile.QuadPart = st->st_size;
1831 info->NumberOfLinks = st->st_nlink;
1834 break;
1835 case FileInternalInformation:
1837 FILE_INTERNAL_INFORMATION *info = ptr;
1838 info->IndexNumber.QuadPart = st->st_ino;
1840 break;
1841 case FileEndOfFileInformation:
1843 FILE_END_OF_FILE_INFORMATION *info = ptr;
1844 info->EndOfFile.QuadPart = S_ISDIR(st->st_mode) ? 0 : st->st_size;
1846 break;
1847 case FileAllInformation:
1849 FILE_ALL_INFORMATION *info = ptr;
1850 fill_file_info( st, attr, &info->BasicInformation, FileBasicInformation );
1851 fill_file_info( st, attr, &info->StandardInformation, FileStandardInformation );
1852 fill_file_info( st, attr, &info->InternalInformation, FileInternalInformation );
1854 break;
1855 /* all directory structures start with the FileDirectoryInformation layout */
1856 case FileBothDirectoryInformation:
1857 case FileFullDirectoryInformation:
1858 case FileDirectoryInformation:
1860 FILE_DIRECTORY_INFORMATION *info = ptr;
1862 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1863 &info->LastAccessTime, &info->CreationTime );
1864 if (S_ISDIR(st->st_mode))
1866 info->AllocationSize.QuadPart = 0;
1867 info->EndOfFile.QuadPart = 0;
1869 else
1871 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1872 info->EndOfFile.QuadPart = st->st_size;
1874 info->FileAttributes = attr;
1876 break;
1877 case FileIdFullDirectoryInformation:
1879 FILE_ID_FULL_DIRECTORY_INFORMATION *info = ptr;
1880 info->FileId.QuadPart = st->st_ino;
1881 fill_file_info( st, attr, info, FileDirectoryInformation );
1883 break;
1884 case FileIdBothDirectoryInformation:
1886 FILE_ID_BOTH_DIRECTORY_INFORMATION *info = ptr;
1887 info->FileId.QuadPart = st->st_ino;
1888 fill_file_info( st, attr, info, FileDirectoryInformation );
1890 break;
1891 case FileIdGlobalTxDirectoryInformation:
1893 FILE_ID_GLOBAL_TX_DIR_INFORMATION *info = ptr;
1894 info->FileId.QuadPart = st->st_ino;
1895 fill_file_info( st, attr, info, FileDirectoryInformation );
1897 break;
1899 default:
1900 return STATUS_INVALID_INFO_CLASS;
1902 return STATUS_SUCCESS;
1906 static NTSTATUS server_get_unix_name( HANDLE handle, char **unix_name )
1908 data_size_t size = 1024;
1909 NTSTATUS ret;
1910 char *name;
1912 for (;;)
1914 if (!(name = malloc( size + 1 ))) return STATUS_NO_MEMORY;
1916 SERVER_START_REQ( get_handle_unix_name )
1918 req->handle = wine_server_obj_handle( handle );
1919 wine_server_set_reply( req, name, size );
1920 ret = wine_server_call( req );
1921 size = reply->name_len;
1923 SERVER_END_REQ;
1925 if (!ret)
1927 name[size] = 0;
1928 *unix_name = name;
1929 break;
1931 free( name );
1932 if (ret != STATUS_BUFFER_OVERFLOW) break;
1934 return ret;
1937 static NTSTATUS fill_name_info( const char *unix_name, FILE_NAME_INFORMATION *info, LONG *name_len )
1939 WCHAR *nt_name;
1940 NTSTATUS status;
1942 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
1944 const WCHAR *ptr = nt_name;
1945 const WCHAR *end = ptr + wcslen( nt_name );
1947 /* Skip the volume mount point. */
1948 while (ptr != end && *ptr == '\\') ++ptr;
1949 while (ptr != end && *ptr != '\\') ++ptr;
1950 while (ptr != end && *ptr == '\\') ++ptr;
1951 while (ptr != end && *ptr != '\\') ++ptr;
1953 info->FileNameLength = (end - ptr) * sizeof(WCHAR);
1954 if (*name_len < info->FileNameLength) status = STATUS_BUFFER_OVERFLOW;
1955 else *name_len = info->FileNameLength;
1957 memcpy( info->FileName, ptr, *name_len );
1958 free( nt_name );
1961 return status;
1965 static NTSTATUS get_full_size_info(int fd, FILE_FS_FULL_SIZE_INFORMATION *info) {
1966 struct stat st;
1967 ULONGLONG bsize;
1969 #if !defined(linux) || !defined(HAVE_FSTATFS)
1970 struct statvfs stfs;
1971 #else
1972 struct statfs stfs;
1973 #endif
1975 if (fstat( fd, &st ) < 0) return errno_to_status( errno );
1976 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) return STATUS_INVALID_DEVICE_REQUEST;
1978 /* Linux's fstatvfs is buggy */
1979 #if !defined(linux) || !defined(HAVE_FSTATFS)
1980 if (fstatvfs( fd, &stfs ) < 0) return errno_to_status( errno );
1981 bsize = stfs.f_frsize;
1982 #else
1983 if (fstatfs( fd, &stfs ) < 0) return errno_to_status( errno );
1984 bsize = stfs.f_bsize;
1985 #endif
1986 if (bsize == 2048) /* assume CD-ROM */
1988 info->BytesPerSector = 2048;
1989 info->SectorsPerAllocationUnit = 1;
1991 else
1993 info->BytesPerSector = 512;
1994 info->SectorsPerAllocationUnit = 8;
1996 info->TotalAllocationUnits.QuadPart = bsize * stfs.f_blocks / (info->BytesPerSector * info->SectorsPerAllocationUnit);
1997 info->CallerAvailableAllocationUnits.QuadPart = bsize * stfs.f_bavail / (info->BytesPerSector * info->SectorsPerAllocationUnit);
1998 info->ActualAvailableAllocationUnits.QuadPart = bsize * stfs.f_bfree / (info->BytesPerSector * info->SectorsPerAllocationUnit);
1999 return STATUS_SUCCESS;
2003 static NTSTATUS server_get_file_info( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer,
2004 ULONG length, FILE_INFORMATION_CLASS info_class )
2006 SERVER_START_REQ( get_file_info )
2008 req->handle = wine_server_obj_handle( handle );
2009 req->info_class = info_class;
2010 wine_server_set_reply( req, buffer, length );
2011 io->u.Status = wine_server_call( req );
2012 io->Information = wine_server_reply_size( reply );
2014 SERVER_END_REQ;
2015 if (io->u.Status == STATUS_NOT_IMPLEMENTED)
2016 FIXME( "Unsupported info class %x\n", info_class );
2017 return io->u.Status;
2022 static NTSTATUS server_open_file_object( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
2023 ULONG sharing, ULONG options )
2025 NTSTATUS status;
2027 SERVER_START_REQ( open_file_object )
2029 req->access = access;
2030 req->attributes = attr->Attributes;
2031 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
2032 req->sharing = sharing;
2033 req->options = options;
2034 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
2035 status = wine_server_call( req );
2036 *handle = wine_server_ptr_handle( reply->handle );
2038 SERVER_END_REQ;
2039 return status;
2043 /* retrieve device/inode number for all the drives */
2044 static unsigned int get_drives_info( struct file_identity info[MAX_DOS_DRIVES] )
2046 static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
2047 static struct file_identity cache[MAX_DOS_DRIVES];
2048 static time_t last_update;
2049 static unsigned int nb_drives;
2050 unsigned int ret;
2051 time_t now = time(NULL);
2053 mutex_lock( &cache_mutex );
2054 if (now != last_update)
2056 char *buffer, *p;
2057 struct stat st;
2058 unsigned int i;
2060 if ((buffer = malloc( strlen(config_dir) + sizeof("/dosdevices/a:") )))
2062 strcpy( buffer, config_dir );
2063 strcat( buffer, "/dosdevices/a:" );
2064 p = buffer + strlen(buffer) - 2;
2066 for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
2068 *p = 'a' + i;
2069 if (!stat( buffer, &st ))
2071 cache[i].dev = st.st_dev;
2072 cache[i].ino = st.st_ino;
2073 nb_drives++;
2075 else
2077 cache[i].dev = 0;
2078 cache[i].ino = 0;
2081 free( buffer );
2083 last_update = now;
2085 memcpy( info, cache, sizeof(cache) );
2086 ret = nb_drives;
2087 mutex_unlock( &cache_mutex );
2088 return ret;
2092 /* Find a DOS device which can act as the root of "path".
2093 * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
2094 static int find_dos_device( const char *path )
2096 int len = strlen(path);
2097 int drive;
2098 char *buffer;
2099 struct stat st;
2100 struct file_identity info[MAX_DOS_DRIVES];
2101 dev_t dev_id;
2103 if (!get_drives_info( info )) return -1;
2105 if (stat( path, &st ) < 0) return -1;
2106 dev_id = st.st_dev;
2108 /* strip off trailing slashes */
2109 while (len > 1 && path[len - 1] == '/') len--;
2111 /* make a copy of the path */
2112 if (!(buffer = malloc( len + 1 ))) return -1;
2113 memcpy( buffer, path, len );
2114 buffer[len] = 0;
2116 for (;;)
2118 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
2120 if (st.st_dev != dev_id) break;
2122 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
2124 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
2126 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2127 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
2128 free( buffer );
2129 return drive;
2133 if (len <= 1) break; /* reached root */
2134 while (len > 1 && path[len - 1] != '/') len--;
2135 while (len > 1 && path[len - 1] == '/') len--;
2136 buffer[len] = 0;
2138 free( buffer );
2139 return -1;
2142 static NTSTATUS get_mountmgr_fs_info( HANDLE handle, int fd, struct mountmgr_unix_drive *drive, ULONG size )
2144 OBJECT_ATTRIBUTES attr;
2145 UNICODE_STRING string;
2146 char *unix_name;
2147 IO_STATUS_BLOCK io = {0};
2148 HANDLE mountmgr;
2149 NTSTATUS status;
2150 int letter;
2152 if ((status = server_get_unix_name( handle, &unix_name ))) return status;
2153 letter = find_dos_device( unix_name );
2154 free( unix_name );
2156 memset( drive, 0, sizeof(*drive) );
2157 if (letter == -1)
2159 struct stat st;
2161 fstat( fd, &st );
2162 drive->unix_dev = st.st_rdev ? st.st_rdev : st.st_dev;
2164 else
2165 drive->letter = 'a' + letter;
2167 init_unicode_string( &string, MOUNTMGR_DEVICE_NAME );
2168 InitializeObjectAttributes( &attr, &string, 0, NULL, NULL );
2169 status = server_open_file_object( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr,
2170 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT );
2171 if (status) return status;
2173 status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE,
2174 drive, sizeof(*drive), drive, size );
2175 NtClose( mountmgr );
2176 if (status == STATUS_BUFFER_OVERFLOW) status = STATUS_SUCCESS;
2177 else if (status) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status);
2178 return status;
2182 /***********************************************************************
2183 * get_dir_data_entry
2185 * Return a directory entry from the cached data.
2187 static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, IO_STATUS_BLOCK *io,
2188 ULONG max_length, FILE_INFORMATION_CLASS class,
2189 union file_directory_info **last_info )
2191 const struct dir_data_names *names = &dir_data->names[dir_data->pos];
2192 union file_directory_info *info;
2193 struct stat st;
2194 ULONG name_len, start, dir_size, attributes;
2196 if (get_file_info( names->unix_name, &st, &attributes ) == -1)
2198 TRACE( "file no longer exists %s\n", names->unix_name );
2199 return STATUS_SUCCESS;
2201 if (is_ignored_file( &st ))
2203 TRACE( "ignoring file %s\n", names->unix_name );
2204 return STATUS_SUCCESS;
2206 start = dir_info_align( io->Information );
2207 dir_size = dir_info_size( class, 0 );
2208 if (start + dir_size > max_length) return STATUS_MORE_ENTRIES;
2210 max_length -= start + dir_size;
2211 name_len = wcslen( names->long_name ) * sizeof(WCHAR);
2212 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2213 if (*last_info && name_len > max_length) return STATUS_MORE_ENTRIES;
2215 info = (union file_directory_info *)((char *)info_ptr + start);
2216 info->dir.NextEntryOffset = 0;
2217 info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
2219 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2220 if (class != FileNamesInformation)
2222 if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */
2224 if (!show_dot_files && names->long_name[0] == '.' && names->long_name[1] &&
2225 (names->long_name[1] != '.' || names->long_name[2]))
2226 attributes |= FILE_ATTRIBUTE_HIDDEN;
2228 fill_file_info( &st, attributes, info, class );
2231 switch (class)
2233 case FileDirectoryInformation:
2234 info->dir.FileNameLength = name_len;
2235 break;
2237 case FileFullDirectoryInformation:
2238 info->full.EaSize = 0; /* FIXME */
2239 info->full.FileNameLength = name_len;
2240 break;
2242 case FileIdFullDirectoryInformation:
2243 info->id_full.EaSize = 0; /* FIXME */
2244 info->id_full.FileNameLength = name_len;
2245 break;
2247 case FileBothDirectoryInformation:
2248 info->both.EaSize = 0; /* FIXME */
2249 info->both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2250 memcpy( info->both.ShortName, names->short_name, info->both.ShortNameLength );
2251 info->both.FileNameLength = name_len;
2252 break;
2254 case FileIdBothDirectoryInformation:
2255 info->id_both.EaSize = 0; /* FIXME */
2256 info->id_both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2257 memcpy( info->id_both.ShortName, names->short_name, info->id_both.ShortNameLength );
2258 info->id_both.FileNameLength = name_len;
2259 break;
2261 case FileIdGlobalTxDirectoryInformation:
2262 info->id_tx.TxInfoFlags = 0;
2263 info->id_tx.FileNameLength = name_len;
2264 break;
2266 case FileNamesInformation:
2267 info->names.FileNameLength = name_len;
2268 break;
2270 default:
2271 assert(0);
2272 return 0;
2275 memcpy( (char *)info + dir_size, names->long_name, min( name_len, max_length ) );
2276 io->Information = start + dir_size + min( name_len, max_length );
2277 if (*last_info) (*last_info)->next = (char *)info - (char *)*last_info;
2278 *last_info = info;
2279 return name_len > max_length ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
2282 #ifdef VFAT_IOCTL_READDIR_BOTH
2284 /***********************************************************************
2285 * read_directory_vfat
2287 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2289 static NTSTATUS read_directory_data_vfat( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2291 char *short_name, *long_name;
2292 KERNEL_DIRENT de[2];
2293 NTSTATUS status = STATUS_NO_MEMORY;
2294 off_t old_pos = lseek( fd, 0, SEEK_CUR );
2296 lseek( fd, 0, SEEK_SET );
2298 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
2300 if (errno != ENOENT)
2302 status = STATUS_NOT_SUPPORTED;
2303 goto done;
2305 de[0].d_reclen = 0;
2308 if (!append_entry( data, ".", NULL, mask )) goto done;
2309 if (!append_entry( data, "..", NULL, mask )) goto done;
2311 while (de[0].d_reclen)
2313 if (strcmp( de[0].d_name, "." ) && strcmp( de[0].d_name, ".." ))
2315 if (de[1].d_name[0])
2317 short_name = de[0].d_name;
2318 long_name = de[1].d_name;
2320 else
2322 long_name = de[0].d_name;
2323 short_name = NULL;
2325 if (!append_entry( data, long_name, short_name, mask )) goto done;
2327 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
2329 status = STATUS_SUCCESS;
2330 done:
2331 lseek( fd, old_pos, SEEK_SET );
2332 return status;
2334 #endif /* VFAT_IOCTL_READDIR_BOTH */
2337 #ifdef HAVE_GETATTRLIST
2338 /***********************************************************************
2339 * read_directory_getattrlist
2341 * Read a single file from a directory by determining whether the file
2342 * identified by mask exists using getattrlist.
2344 static NTSTATUS read_directory_data_getattrlist( struct dir_data *data, const char *unix_name )
2346 struct attrlist attrlist;
2347 #include "pshpack4.h"
2348 struct
2350 u_int32_t length;
2351 struct attrreference name_reference;
2352 fsobj_type_t type;
2353 char name[NAME_MAX * 3 + 1];
2354 } buffer;
2355 #include "poppack.h"
2357 memset( &attrlist, 0, sizeof(attrlist) );
2358 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
2359 attrlist.commonattr = ATTR_CMN_NAME | ATTR_CMN_OBJTYPE;
2360 if (getattrlist( unix_name, &attrlist, &buffer, sizeof(buffer), FSOPT_NOFOLLOW ) == -1)
2361 return STATUS_NO_SUCH_FILE;
2362 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2363 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2364 if (buffer.type == VLNK)
2366 u_int32_t dummy;
2367 attrlist.commonattr = 0;
2368 if (getattrlist( unix_name, &attrlist, &dummy, sizeof(dummy), 0 ) == -1)
2369 return STATUS_NO_SUCH_FILE;
2372 TRACE( "found %s\n", buffer.name );
2374 if (!append_entry( data, buffer.name, NULL, NULL )) return STATUS_NO_MEMORY;
2376 return STATUS_SUCCESS;
2378 #endif /* HAVE_GETATTRLIST */
2381 /***********************************************************************
2382 * read_directory_stat
2384 * Read a single file from a directory by determining whether the file
2385 * identified by mask exists using stat.
2387 static NTSTATUS read_directory_data_stat( struct dir_data *data, const char *unix_name )
2389 struct stat st;
2391 /* if the file system is not case sensitive we can't find the actual name through stat() */
2392 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE;
2393 if (stat( unix_name, &st ) == -1) return STATUS_NO_SUCH_FILE;
2395 TRACE( "found %s\n", unix_name );
2397 if (!append_entry( data, unix_name, NULL, NULL )) return STATUS_NO_MEMORY;
2399 return STATUS_SUCCESS;
2403 /***********************************************************************
2404 * read_directory_readdir
2406 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2408 static NTSTATUS read_directory_data_readdir( struct dir_data *data, const UNICODE_STRING *mask )
2410 struct dirent *de;
2411 NTSTATUS status = STATUS_NO_MEMORY;
2412 DIR *dir = opendir( "." );
2414 if (!dir) return STATUS_NO_SUCH_FILE;
2416 if (!append_entry( data, ".", NULL, mask )) goto done;
2417 if (!append_entry( data, "..", NULL, mask )) goto done;
2418 while ((de = readdir( dir )))
2420 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2421 if (!append_entry( data, de->d_name, NULL, mask )) goto done;
2423 status = STATUS_SUCCESS;
2425 done:
2426 closedir( dir );
2427 return status;
2431 /***********************************************************************
2432 * read_directory_data
2434 * Read the full contents of a directory, using one of the above helper functions.
2436 static NTSTATUS read_directory_data( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2438 NTSTATUS status;
2440 #ifdef VFAT_IOCTL_READDIR_BOTH
2441 if (!(status = read_directory_data_vfat( data, fd, mask ))) return status;
2442 #endif
2444 if (!has_wildcard( mask ))
2446 /* convert the mask to a Unix name and check for it */
2447 char unix_name[MAX_DIR_ENTRY_LEN * 3 + 1];
2448 int ret = ntdll_wcstoumbs( mask->Buffer, mask->Length / sizeof(WCHAR),
2449 unix_name, sizeof(unix_name) - 1, TRUE );
2450 if (ret > 0)
2452 unix_name[ret] = 0;
2453 #ifdef HAVE_GETATTRLIST
2454 if (!(status = read_directory_data_getattrlist( data, unix_name ))) return status;
2455 #endif
2456 if (!(status = read_directory_data_stat( data, unix_name ))) return status;
2460 return read_directory_data_readdir( data, mask );
2464 /* compare file names for directory sorting */
2465 static int name_compare( const void *a, const void *b )
2467 const struct dir_data_names *file_a = (const struct dir_data_names *)a;
2468 const struct dir_data_names *file_b = (const struct dir_data_names *)b;
2469 int ret = wcsicmp( file_a->long_name, file_b->long_name );
2470 if (!ret) ret = wcscmp( file_a->long_name, file_b->long_name );
2471 return ret;
2475 /***********************************************************************
2476 * init_cached_dir_data
2478 * Initialize the cached directory contents.
2480 static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const UNICODE_STRING *mask )
2482 struct dir_data *data;
2483 struct stat st;
2484 NTSTATUS status;
2485 unsigned int i;
2487 if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY;
2489 if ((status = read_directory_data( data, fd, mask )))
2491 free_dir_data( data );
2492 return status;
2495 /* sort filenames, but not "." and ".." */
2496 i = 0;
2497 if (i < data->count && !strcmp( data->names[i].unix_name, "." )) i++;
2498 if (i < data->count && !strcmp( data->names[i].unix_name, ".." )) i++;
2499 if (i < data->count) qsort( data->names + i, data->count - i, sizeof(*data->names), name_compare );
2501 if (data->count)
2503 fstat( fd, &st );
2504 data->id.dev = st.st_dev;
2505 data->id.ino = st.st_ino;
2508 TRACE( "mask %s found %u files\n", debugstr_us( mask ), data->count );
2509 for (i = 0; i < data->count; i++)
2510 TRACE( "%s %s\n", debugstr_w(data->names[i].long_name), debugstr_w(data->names[i].short_name) );
2512 *data_ret = data;
2513 return data->count ? STATUS_SUCCESS : STATUS_NO_SUCH_FILE;
2517 /***********************************************************************
2518 * get_cached_dir_data
2520 * Retrieve the cached directory data, or initialize it if necessary.
2522 static NTSTATUS get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd,
2523 const UNICODE_STRING *mask )
2525 unsigned int i;
2526 int entry = -1, free_entries[16];
2527 NTSTATUS status;
2529 SERVER_START_REQ( get_directory_cache_entry )
2531 req->handle = wine_server_obj_handle( handle );
2532 wine_server_set_reply( req, free_entries, sizeof(free_entries) );
2533 if (!(status = wine_server_call( req ))) entry = reply->entry;
2535 for (i = 0; i < wine_server_reply_size( reply ) / sizeof(*free_entries); i++)
2537 int free_idx = free_entries[i];
2538 if (free_idx < dir_data_cache_size)
2540 free_dir_data( dir_data_cache[free_idx] );
2541 dir_data_cache[free_idx] = NULL;
2545 SERVER_END_REQ;
2547 if (status)
2549 if (status == STATUS_SHARING_VIOLATION) FIXME( "shared directory handle not supported yet\n" );
2550 return status;
2553 if (entry >= dir_data_cache_size)
2555 unsigned int size = max( dir_data_cache_initial_size, max( dir_data_cache_size * 2, entry + 1 ) );
2556 struct dir_data **new_cache = realloc( dir_data_cache, size * sizeof(*new_cache) );
2558 if (!new_cache) return STATUS_NO_MEMORY;
2559 memset( new_cache + dir_data_cache_size, 0, (size - dir_data_cache_size) * sizeof(*new_cache) );
2560 dir_data_cache = new_cache;
2561 dir_data_cache_size = size;
2564 if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask );
2566 *data_ret = dir_data_cache[entry];
2567 return status;
2571 /******************************************************************************
2572 * NtQueryDirectoryFile (NTDLL.@)
2574 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine,
2575 void *apc_context, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
2576 FILE_INFORMATION_CLASS info_class, BOOLEAN single_entry,
2577 UNICODE_STRING *mask, BOOLEAN restart_scan )
2579 int cwd, fd, needs_close;
2580 enum server_fd_type type;
2581 struct dir_data *data;
2582 NTSTATUS status;
2584 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2585 handle, event, apc_routine, apc_context, io, buffer,
2586 length, info_class, single_entry, debugstr_us(mask),
2587 restart_scan);
2589 if (event || apc_routine)
2591 FIXME( "Unsupported yet option\n" );
2592 return STATUS_NOT_IMPLEMENTED;
2594 switch (info_class)
2596 case FileDirectoryInformation:
2597 case FileBothDirectoryInformation:
2598 case FileFullDirectoryInformation:
2599 case FileIdBothDirectoryInformation:
2600 case FileIdFullDirectoryInformation:
2601 case FileIdGlobalTxDirectoryInformation:
2602 case FileNamesInformation:
2603 if (length < dir_info_align( dir_info_size( info_class, 1 ))) return STATUS_INFO_LENGTH_MISMATCH;
2604 break;
2605 case FileObjectIdInformation:
2606 if (length != sizeof(FILE_OBJECTID_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2607 return STATUS_INVALID_INFO_CLASS;
2608 case FileQuotaInformation:
2609 if (length != sizeof(FILE_QUOTA_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2610 return STATUS_INVALID_INFO_CLASS;
2611 case FileReparsePointInformation:
2612 if (length != sizeof(FILE_REPARSE_POINT_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2613 return STATUS_INVALID_INFO_CLASS;
2614 default:
2615 return STATUS_INVALID_INFO_CLASS;
2617 if (!buffer) return STATUS_ACCESS_VIOLATION;
2619 if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, &type, NULL )))
2620 return status;
2622 if (type != FD_TYPE_DIR)
2624 if (needs_close) close( fd );
2625 return STATUS_INVALID_PARAMETER;
2628 io->Information = 0;
2630 mutex_lock( &dir_mutex );
2632 cwd = open( ".", O_RDONLY );
2633 if (fchdir( fd ) != -1)
2635 if (!(status = get_cached_dir_data( handle, &data, fd, mask )))
2637 union file_directory_info *last_info = NULL;
2639 if (restart_scan) data->pos = 0;
2641 while (!status && data->pos < data->count)
2643 status = get_dir_data_entry( data, buffer, io, length, info_class, &last_info );
2644 if (!status || status == STATUS_BUFFER_OVERFLOW) data->pos++;
2645 if (single_entry && last_info) break;
2648 if (!last_info) status = STATUS_NO_MORE_FILES;
2649 else if (status == STATUS_MORE_ENTRIES) status = STATUS_SUCCESS;
2651 io->u.Status = status;
2653 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
2655 else status = errno_to_status( errno );
2657 mutex_unlock( &dir_mutex );
2659 if (needs_close) close( fd );
2660 if (cwd != -1) close( cwd );
2661 TRACE( "=> %x (%ld)\n", status, io->Information );
2662 return status;
2666 /***********************************************************************
2667 * find_file_in_dir
2669 * Find a file in a directory the hard way, by doing a case-insensitive search.
2670 * The file found is appended to unix_name at pos.
2671 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2673 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
2674 BOOLEAN check_case )
2676 WCHAR buffer[MAX_DIR_ENTRY_LEN];
2677 BOOLEAN is_name_8_dot_3;
2678 DIR *dir;
2679 struct dirent *de;
2680 struct stat st;
2681 int ret;
2683 /* try a shortcut for this directory */
2685 unix_name[pos++] = '/';
2686 ret = ntdll_wcstoumbs( name, length, unix_name + pos, MAX_DIR_ENTRY_LEN + 1, TRUE );
2687 if (ret >= 0 && ret <= MAX_DIR_ENTRY_LEN)
2689 unix_name[pos + ret] = 0;
2690 if (!stat( unix_name, &st )) return STATUS_SUCCESS;
2692 if (check_case) goto not_found; /* we want an exact match */
2694 if (pos > 1) unix_name[pos - 1] = 0;
2695 else unix_name[1] = 0; /* keep the initial slash */
2697 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2699 is_name_8_dot_3 = is_legal_8dot3_name( name, length );
2700 #ifndef VFAT_IOCTL_READDIR_BOTH
2701 is_name_8_dot_3 = is_name_8_dot_3 && length >= 8 && name[4] == '~';
2702 #endif
2704 if (!is_name_8_dot_3 && !get_dir_case_sensitivity( unix_name )) goto not_found;
2706 /* now look for it through the directory */
2708 #ifdef VFAT_IOCTL_READDIR_BOTH
2709 if (is_name_8_dot_3)
2711 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
2712 if (fd != -1)
2714 KERNEL_DIRENT kde[2];
2716 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) != -1)
2718 unix_name[pos - 1] = '/';
2719 while (kde[0].d_reclen)
2721 if (kde[1].d_name[0])
2723 ret = ntdll_umbstowcs( kde[1].d_name, strlen(kde[1].d_name),
2724 buffer, MAX_DIR_ENTRY_LEN );
2725 if (ret == length && !wcsnicmp( buffer, name, ret ))
2727 strcpy( unix_name + pos, kde[1].d_name );
2728 close( fd );
2729 return STATUS_SUCCESS;
2732 ret = ntdll_umbstowcs( kde[0].d_name, strlen(kde[0].d_name),
2733 buffer, MAX_DIR_ENTRY_LEN );
2734 if (ret == length && !wcsnicmp( buffer, name, ret ))
2736 strcpy( unix_name + pos,
2737 kde[1].d_name[0] ? kde[1].d_name : kde[0].d_name );
2738 close( fd );
2739 return STATUS_SUCCESS;
2741 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) == -1)
2743 close( fd );
2744 goto not_found;
2747 /* if that did not work, restore previous state of unix_name */
2748 unix_name[pos - 1] = 0;
2750 close( fd );
2752 /* fall through to normal handling */
2754 #endif /* VFAT_IOCTL_READDIR_BOTH */
2756 if (!(dir = opendir( unix_name ))) return errno_to_status( errno );
2758 unix_name[pos - 1] = '/';
2759 while ((de = readdir( dir )))
2761 ret = ntdll_umbstowcs( de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
2762 if (ret == length && !wcsnicmp( buffer, name, ret ))
2764 strcpy( unix_name + pos, de->d_name );
2765 closedir( dir );
2766 return STATUS_SUCCESS;
2769 if (!is_name_8_dot_3) continue;
2771 if (!is_legal_8dot3_name( buffer, ret ))
2773 WCHAR short_nameW[12];
2774 ret = hash_short_file_name( buffer, ret, short_nameW );
2775 if (ret == length && !wcsnicmp( short_nameW, name, length ))
2777 strcpy( unix_name + pos, de->d_name );
2778 closedir( dir );
2779 return STATUS_SUCCESS;
2783 closedir( dir );
2785 not_found:
2786 unix_name[pos - 1] = 0;
2787 return STATUS_OBJECT_NAME_NOT_FOUND;
2791 #ifndef _WIN64
2793 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2794 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2795 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};
2796 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2797 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2798 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2799 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
2800 static const WCHAR syswow64W[] = {'s','y','s','w','o','w','6','4',0};
2801 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
2802 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2803 static const WCHAR syswow64_regeditW[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
2804 static const WCHAR windirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\',0};
2805 static const WCHAR syswow64dirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','w','o','w','6','4','\\'};
2807 static const WCHAR * const no_redirect[] =
2809 catrootW,
2810 catroot2W,
2811 driversstoreW,
2812 driversetcW,
2813 logfilesW,
2814 spoolW
2817 static struct file_identity windir, sysdir;
2819 static inline ULONG starts_with_path( const WCHAR *name, ULONG name_len, const WCHAR *prefix )
2821 ULONG len = wcslen( prefix );
2823 if (name_len < len) return 0;
2824 if (wcsnicmp( name, prefix, len )) return 0;
2825 if (name_len > len && name[len] != '\\') return 0;
2826 return len;
2829 static BOOL replace_path( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *str, ULONG prefix_len,
2830 const WCHAR *match, const WCHAR *replace )
2832 const WCHAR *name = attr->ObjectName->Buffer;
2833 ULONG match_len, replace_len, len = attr->ObjectName->Length / sizeof(WCHAR);
2834 WCHAR *p;
2836 if (!starts_with_path( name + prefix_len, len - prefix_len, match )) return FALSE;
2838 match_len = wcslen( match );
2839 replace_len = wcslen( replace );
2840 str->Length = (len + replace_len - match_len) * sizeof(WCHAR);
2841 str->MaximumLength = str->Length + sizeof(WCHAR);
2842 if (!(p = str->Buffer = malloc( str->MaximumLength ))) return FALSE;
2844 memcpy( p, name, prefix_len * sizeof(WCHAR) );
2845 p += prefix_len;
2846 memcpy( p, replace, replace_len * sizeof(WCHAR) );
2847 p += replace_len;
2848 name += prefix_len + match_len;
2849 len -= prefix_len + match_len;
2850 memcpy( p, name, len * sizeof(WCHAR) );
2851 p[len] = 0;
2852 attr->ObjectName = str;
2853 return TRUE;
2856 /***********************************************************************
2857 * init_redirects
2859 static void init_redirects(void)
2861 static const char system_dir[] = "/dosdevices/c:/windows/system32";
2862 char *dir;
2863 struct stat st;
2865 if (!(dir = malloc( strlen(config_dir) + sizeof(system_dir) ))) return;
2866 strcpy( dir, config_dir );
2867 strcat( dir, system_dir );
2868 if (!stat( dir, &st ))
2870 sysdir.dev = st.st_dev;
2871 sysdir.ino = st.st_ino;
2873 *strrchr( dir, '/' ) = 0;
2874 if (!stat( dir, &st ))
2876 windir.dev = st.st_dev;
2877 windir.ino = st.st_ino;
2879 else ERR( "%s: %s\n", dir, strerror(errno) );
2880 free( dir );
2884 /***********************************************************************
2885 * get_redirect
2887 BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir )
2889 const WCHAR *name = attr->ObjectName->Buffer;
2890 unsigned int i, prefix_len = 0, len = attr->ObjectName->Length / sizeof(WCHAR);
2892 redir->Buffer = NULL;
2893 if (!NtCurrentTeb64()) return FALSE;
2894 if (!len) return FALSE;
2896 if (!attr->RootDirectory)
2898 prefix_len = wcslen( windirW );
2899 if (len < prefix_len || wcsnicmp( name, windirW, prefix_len )) return FALSE;
2901 else
2903 int fd, needs_close;
2904 struct stat st;
2906 if (server_get_unix_fd( attr->RootDirectory, 0, &fd, &needs_close, NULL, NULL )) return FALSE;
2907 fstat( fd, &st );
2908 if (needs_close) close( fd );
2909 if (!is_same_file( &windir, &st ))
2911 if (!is_same_file( &sysdir, &st )) return FALSE;
2912 if (NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE;
2913 if (name[0] == '\\') return FALSE;
2915 /* only check for paths that should NOT be redirected */
2916 for (i = 0; i < ARRAY_SIZE( no_redirect ); i++)
2917 if (starts_with_path( name, len, no_redirect[i] + 9 /* "system32\\" */)) return FALSE;
2919 /* redirect everything else */
2920 redir->Length = sizeof(syswow64dirW) + len * sizeof(WCHAR);
2921 redir->MaximumLength = redir->Length + sizeof(WCHAR);
2922 if (!(redir->Buffer = malloc( redir->MaximumLength ))) return FALSE;
2923 memcpy( redir->Buffer, syswow64dirW, sizeof(syswow64dirW) );
2924 memcpy( redir->Buffer + ARRAY_SIZE(syswow64dirW), name, len * sizeof(WCHAR) );
2925 redir->Buffer[redir->Length / sizeof(WCHAR)] = 0;
2926 attr->RootDirectory = 0;
2927 attr->ObjectName = redir;
2928 return TRUE;
2932 /* sysnative is redirected even when redirection is disabled */
2934 if (replace_path( attr, redir, prefix_len, sysnativeW, system32W )) return TRUE;
2936 if (NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE;
2938 for (i = 0; i < ARRAY_SIZE( no_redirect ); i++)
2939 if (starts_with_path( name + prefix_len, len - prefix_len, no_redirect[i] )) return FALSE;
2941 if (replace_path( attr, redir, prefix_len, system32W, syswow64W )) return TRUE;
2942 if (replace_path( attr, redir, prefix_len, regeditW, syswow64_regeditW )) return TRUE;
2943 return FALSE;
2946 #else /* _WIN64 */
2948 /* there are no redirects on 64-bit */
2949 BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir )
2951 redir->Buffer = NULL;
2952 return FALSE;
2955 #endif
2958 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
2960 /***********************************************************************
2961 * init_files
2963 void init_files(void)
2965 HANDLE key;
2967 #ifndef _WIN64
2968 if (is_wow64) init_redirects();
2969 #endif
2970 /* a couple of directories that we don't want to return in directory searches */
2971 ignore_file( config_dir );
2972 ignore_file( "/dev" );
2973 ignore_file( "/proc" );
2974 #ifdef linux
2975 ignore_file( "/sys" );
2976 #endif
2977 /* retrieve initial umask */
2978 start_umask = umask( 0777 );
2979 umask( start_umask );
2981 if (!open_hkcu_key( "Software\\Wine", &key ))
2983 static WCHAR showdotfilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
2984 char tmp[80];
2985 DWORD dummy;
2986 UNICODE_STRING nameW;
2988 init_unicode_string( &nameW, showdotfilesW );
2989 if (!NtQueryValueKey( key, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
2991 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
2992 show_dot_files = IS_OPTION_TRUE( str[0] );
2994 NtClose( key );
2999 /******************************************************************************
3000 * get_dos_device
3002 * Get the Unix path of a DOS device.
3004 static NTSTATUS get_dos_device( char **unix_name, int start_pos )
3006 struct stat st;
3007 char *new_name, *dev = *unix_name + start_pos;
3009 /* special case for drive devices */
3010 if (dev[0] && dev[1] == ':' && !dev[2]) strcpy( dev + 1, "::" );
3012 if (strchr( dev, '/' )) goto failed;
3014 for (;;)
3016 if (!stat( *unix_name, &st ))
3018 TRACE( "-> %s\n", debugstr_a(*unix_name));
3019 return STATUS_SUCCESS;
3021 if (!dev) break;
3023 /* now try some defaults for it */
3024 if (!strcmp( dev, "aux" ))
3026 strcpy( dev, "com1" );
3027 continue;
3029 if (!strcmp( dev, "prn" ))
3031 strcpy( dev, "lpt1" );
3032 continue;
3035 new_name = NULL;
3036 if (dev[1] == ':' && dev[2] == ':') /* drive device */
3038 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
3039 new_name = get_default_drive_device( *unix_name );
3041 free( *unix_name );
3042 *unix_name = new_name;
3043 if (!new_name) return STATUS_BAD_DEVICE_TYPE;
3044 dev = NULL; /* last try */
3046 failed:
3047 free( *unix_name );
3048 *unix_name = NULL;
3049 return STATUS_BAD_DEVICE_TYPE;
3053 /* return the length of the DOS namespace prefix if any */
3054 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
3056 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
3057 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
3059 if (name->Length >= sizeof(nt_prefixW) &&
3060 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
3061 return ARRAY_SIZE( nt_prefixW );
3063 if (name->Length >= sizeof(dosdev_prefixW) &&
3064 !wcsnicmp( name->Buffer, dosdev_prefixW, ARRAY_SIZE( dosdev_prefixW )))
3065 return ARRAY_SIZE( dosdev_prefixW );
3067 return 0;
3071 /***********************************************************************
3072 * remove_last_componentA
3074 * Remove the last component of the path. Helper for find_drive_rootA.
3076 static inline unsigned int remove_last_componentA( const char *path, unsigned int len )
3078 int level = 0;
3080 while (level < 1)
3082 /* find start of the last path component */
3083 unsigned int prev = len;
3084 if (prev <= 1) break; /* reached root */
3085 while (prev > 1 && path[prev - 1] != '/') prev--;
3086 /* does removing it take us up a level? */
3087 if (len - prev != 1 || path[prev] != '.') /* not '.' */
3089 if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.') /* is it '..'? */
3090 level--;
3091 else
3092 level++;
3094 /* strip off trailing slashes */
3095 while (prev > 1 && path[prev - 1] == '/') prev--;
3096 len = prev;
3098 return len;
3102 /***********************************************************************
3103 * find_drive_rootA
3105 * Find a drive for which the root matches the beginning of the given path.
3106 * This can be used to translate a Unix path into a drive + DOS path.
3107 * Return value is the drive, or -1 on error. On success, ppath is modified
3108 * to point to the beginning of the DOS path.
3110 static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_ret )
3112 /* Starting with the full path, check if the device and inode match any of
3113 * the wine 'drives'. If not then remove the last path component and try
3114 * again. If the last component was a '..' then skip a normal component
3115 * since it's a directory that's ascended back out of.
3117 int drive;
3118 char *buffer;
3119 const char *path = *ppath;
3120 struct stat st;
3121 struct file_identity info[MAX_DOS_DRIVES];
3123 /* get device and inode of all drives */
3124 if (!get_drives_info( info )) return STATUS_OBJECT_PATH_NOT_FOUND;
3126 /* strip off trailing slashes */
3127 while (len > 1 && path[len - 1] == '/') len--;
3129 /* make a copy of the path */
3130 if (!(buffer = malloc( len + 1 ))) return STATUS_NO_MEMORY;
3131 memcpy( buffer, path, len );
3132 buffer[len] = 0;
3134 for (;;)
3136 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
3138 /* Find the drive */
3139 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
3141 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
3143 if (len == 1) len = 0; /* preserve root slash in returned path */
3144 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
3145 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
3146 *ppath += len;
3147 *drive_ret = drive;
3148 free( buffer );
3149 return STATUS_SUCCESS;
3153 if (len <= 1) break; /* reached root */
3154 len = remove_last_componentA( buffer, len );
3155 buffer[len] = 0;
3157 free( buffer );
3158 return STATUS_OBJECT_PATH_NOT_FOUND;
3162 /******************************************************************************
3163 * find_file_id
3165 * Recursively search directories from the dir queue for a given inode.
3167 static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev )
3169 unsigned int pos;
3170 DIR *dir;
3171 struct dirent *de;
3172 NTSTATUS status;
3173 struct stat st;
3174 char *name = *unix_name;
3176 while (!(status = next_dir_in_queue( name )))
3178 if (!(dir = opendir( name ))) continue;
3179 TRACE( "searching %s for %s\n", debugstr_a(name), wine_dbgstr_longlong(file_id) );
3180 pos = strlen( name );
3181 if (pos + MAX_DIR_ENTRY_LEN >= *len / sizeof(WCHAR))
3183 if (!(name = realloc( name, *len * 2 )))
3185 closedir( dir );
3186 return STATUS_NO_MEMORY;
3188 *len *= 2;
3189 *unix_name = name;
3191 name[pos++] = '/';
3192 while ((de = readdir( dir )))
3194 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
3195 strcpy( name + pos, de->d_name );
3196 if (lstat( name, &st ) == -1) continue;
3197 if (st.st_dev != dev) continue;
3198 if (st.st_ino == file_id)
3200 closedir( dir );
3201 return STATUS_SUCCESS;
3203 if (!S_ISDIR( st.st_mode )) continue;
3204 if ((status = add_dir_to_queue( name )) != STATUS_SUCCESS)
3206 closedir( dir );
3207 return status;
3210 closedir( dir );
3212 return status;
3216 /******************************************************************************
3217 * file_id_to_unix_file_name
3219 * Lookup a file from its file id instead of its name.
3221 static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **unix_name_ret,
3222 UNICODE_STRING *nt_name )
3224 enum server_fd_type type;
3225 int old_cwd, root_fd, needs_close;
3226 char *unix_name;
3227 ULONG len;
3228 NTSTATUS status;
3229 ULONGLONG file_id;
3230 struct stat st, root_st;
3232 nt_name->Buffer = NULL;
3233 if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
3234 if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
3235 memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
3237 len = 2 * MAX_DIR_ENTRY_LEN + 4;
3238 if (!(unix_name = malloc( len ))) return STATUS_NO_MEMORY;
3239 strcpy( unix_name, "." );
3241 if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3242 goto done;
3244 if (type != FD_TYPE_DIR)
3246 status = STATUS_OBJECT_TYPE_MISMATCH;
3247 goto done;
3250 fstat( root_fd, &root_st );
3251 if (root_st.st_ino == file_id) /* shortcut for "." */
3253 status = STATUS_SUCCESS;
3254 goto done;
3257 mutex_lock( &dir_mutex );
3258 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3260 /* shortcut for ".." */
3261 if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
3263 strcpy( unix_name, ".." );
3264 status = STATUS_SUCCESS;
3266 else
3268 status = add_dir_to_queue( "." );
3269 if (!status)
3270 status = find_file_id( &unix_name, &len, file_id, root_st.st_dev );
3271 if (!status) /* get rid of "./" prefix */
3272 memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 );
3273 flush_dir_queue();
3275 if (fchdir( old_cwd ) == -1) chdir( "/" );
3277 else status = errno_to_status( errno );
3278 mutex_unlock( &dir_mutex );
3279 if (old_cwd != -1) close( old_cwd );
3281 done:
3282 if (status == STATUS_SUCCESS)
3284 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name) );
3285 *unix_name_ret = unix_name;
3287 nt_name->MaximumLength = (strlen(unix_name) + 1) * sizeof(WCHAR);
3288 if ((nt_name->Buffer = malloc( nt_name->MaximumLength )))
3290 DWORD i, len = ntdll_umbstowcs( unix_name, strlen(unix_name), nt_name->Buffer, strlen(unix_name) );
3291 nt_name->Buffer[len] = 0;
3292 nt_name->Length = len * sizeof(WCHAR);
3293 for (i = 0; i < len; i++) if (nt_name->Buffer[i] == '/') nt_name->Buffer[i] = '\\';
3296 else
3298 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
3299 free( unix_name );
3301 if (needs_close) close( root_fd );
3302 return status;
3306 /******************************************************************************
3307 * lookup_unix_name
3309 * Helper for nt_to_unix_file_name
3311 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
3312 UINT disposition, BOOL is_unix )
3314 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, '/', 0 };
3315 NTSTATUS status;
3316 int ret;
3317 struct stat st;
3318 char *unix_name = *buffer;
3319 const WCHAR *ptr, *end;
3321 /* check syntax of individual components */
3323 for (ptr = name, end = name + name_len; ptr < end; ptr++)
3325 if (*ptr == '\\') return STATUS_OBJECT_NAME_INVALID; /* duplicate backslash */
3326 if (*ptr == '.')
3328 if (ptr + 1 == end) return STATUS_OBJECT_NAME_INVALID; /* "." element */
3329 if (ptr[1] == '\\') return STATUS_OBJECT_NAME_INVALID; /* "." element */
3330 if (ptr[1] == '.')
3332 if (ptr + 2 == end) return STATUS_OBJECT_NAME_INVALID; /* ".." element */
3333 if (ptr[2] == '\\') return STATUS_OBJECT_NAME_INVALID; /* ".." element */
3336 /* check for invalid characters (all chars except 0 are valid for unix) */
3337 for ( ; ptr < end && *ptr != '\\'; ptr++)
3339 if (!*ptr) return STATUS_OBJECT_NAME_INVALID;
3340 if (is_unix) continue;
3341 if (*ptr < 32 || wcschr( invalid_charsW, *ptr )) return STATUS_OBJECT_NAME_INVALID;
3345 /* try a shortcut first */
3347 unix_name[pos] = '/';
3348 ret = ntdll_wcstoumbs( name, name_len, unix_name + pos + 1, unix_len - pos - 1, TRUE );
3349 if (ret >= 0 && ret < unix_len - pos - 1)
3351 char *p;
3352 unix_name[pos + 1 + ret] = 0;
3353 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
3354 if (!stat( unix_name, &st ))
3356 if (disposition == FILE_CREATE) return STATUS_OBJECT_NAME_COLLISION;
3357 return STATUS_SUCCESS;
3361 if (!name_len) /* empty name -> drive root doesn't exist */
3362 return STATUS_OBJECT_PATH_NOT_FOUND;
3363 if (is_unix && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
3364 return STATUS_OBJECT_NAME_NOT_FOUND;
3366 /* now do it component by component */
3368 while (name_len)
3370 const WCHAR *end, *next;
3372 end = name;
3373 while (end < name + name_len && *end != '\\') end++;
3374 next = end;
3375 if (next < name + name_len) next++;
3376 name_len -= next - name;
3378 /* grow the buffer if needed */
3380 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 3)
3382 char *new_name;
3383 unix_len += 2 * MAX_DIR_ENTRY_LEN;
3384 if (!(new_name = realloc( unix_name, unix_len ))) return STATUS_NO_MEMORY;
3385 unix_name = *buffer = new_name;
3388 status = find_file_in_dir( unix_name, pos, name, end - name, is_unix );
3390 /* if this is the last element, not finding it is not necessarily fatal */
3391 if (!name_len)
3393 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
3395 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3397 ret = ntdll_wcstoumbs( name, end - name, unix_name + pos + 1, MAX_DIR_ENTRY_LEN + 1, TRUE );
3398 if (ret > 0 && ret <= MAX_DIR_ENTRY_LEN)
3400 unix_name[pos] = '/';
3401 pos += ret + 1;
3402 if (end < next) unix_name[pos++] = '/';
3403 unix_name[pos] = 0;
3404 status = STATUS_NO_SUCH_FILE;
3405 break;
3409 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
3411 status = STATUS_OBJECT_NAME_COLLISION;
3413 if (end < next) strcat( unix_name, "/" );
3415 else if (status == STATUS_OBJECT_NAME_NOT_FOUND) status = STATUS_OBJECT_PATH_NOT_FOUND;
3417 if (status != STATUS_SUCCESS) break;
3419 pos += strlen( unix_name + pos );
3420 name = next;
3423 return status;
3427 /******************************************************************************
3428 * nt_to_unix_file_name_no_root
3430 static NTSTATUS nt_to_unix_file_name_no_root( const UNICODE_STRING *nameW, char **unix_name_ret,
3431 UINT disposition )
3433 static const WCHAR unixW[] = {'u','n','i','x'};
3434 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
3436 NTSTATUS status = STATUS_SUCCESS;
3437 const WCHAR *name;
3438 struct stat st;
3439 char *unix_name;
3440 int pos, ret, name_len, unix_len, prefix_len;
3441 WCHAR prefix[MAX_DIR_ENTRY_LEN + 1];
3442 BOOLEAN is_unix = FALSE;
3444 name = nameW->Buffer;
3445 name_len = nameW->Length / sizeof(WCHAR);
3447 if (!name_len || name[0] != '\\') return STATUS_OBJECT_PATH_SYNTAX_BAD;
3449 if (!(pos = get_dos_prefix_len( nameW )))
3450 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
3452 name += pos;
3453 name_len -= pos;
3455 if (!name_len) return STATUS_OBJECT_NAME_INVALID;
3457 /* check for sub-directory */
3458 for (pos = 0; pos < name_len && pos <= MAX_DIR_ENTRY_LEN; pos++)
3460 if (name[pos] == '\\') break;
3461 if (name[pos] < 32 || wcschr( invalid_charsW, name[pos] ))
3462 return STATUS_OBJECT_NAME_INVALID;
3463 prefix[pos] = (name[pos] >= 'A' && name[pos] <= 'Z') ? name[pos] + 'a' - 'A' : name[pos];
3465 if (pos > MAX_DIR_ENTRY_LEN) return STATUS_OBJECT_NAME_INVALID;
3467 if (pos >= 4 && !memcmp( prefix, unixW, sizeof(unixW) ))
3469 /* allow slash for unix namespace */
3470 if (pos > 4 && prefix[4] == '/') pos = 4;
3471 is_unix = pos == 4;
3473 prefix_len = pos;
3474 prefix[prefix_len] = 0;
3476 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3477 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
3478 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3479 strcpy( unix_name, config_dir );
3480 strcat( unix_name, "/dosdevices/" );
3481 pos = strlen(unix_name);
3483 ret = ntdll_wcstoumbs( prefix, prefix_len, unix_name + pos, unix_len - pos - 1, TRUE );
3484 if (ret <= 0)
3486 free( unix_name );
3487 return STATUS_OBJECT_NAME_INVALID;
3490 if (prefix_len == name_len) /* no subdir, plain DOS device */
3492 unix_name[pos + ret] = 0;
3493 *unix_name_ret = unix_name;
3494 return get_dos_device( unix_name_ret, pos );
3496 pos += ret;
3498 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3500 if (wcschr( prefix, '/' ))
3502 free( unix_name );
3503 return STATUS_OBJECT_PATH_NOT_FOUND;
3506 if (prefix_len != 2 || prefix[1] != ':')
3508 unix_name[pos] = 0;
3509 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
3511 if (!is_unix)
3513 free( unix_name );
3514 return STATUS_BAD_DEVICE_TYPE;
3516 pos = 0; /* fall back to unix root */
3520 prefix_len++; /* skip initial backslash */
3521 if (name_len > prefix_len && name[prefix_len] == '\\') prefix_len++; /* allow a second backslash */
3522 name += prefix_len;
3523 name_len -= prefix_len;
3525 status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, is_unix );
3526 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3528 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
3529 *unix_name_ret = unix_name;
3531 else
3533 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3534 free( unix_name );
3536 return status;
3540 /******************************************************************************
3541 * nt_to_unix_file_name
3543 * Convert a file name from NT namespace to Unix namespace.
3545 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3546 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3547 * returned, but the unix name is still filled in properly.
3549 NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition )
3551 enum server_fd_type type;
3552 int old_cwd, root_fd, needs_close;
3553 const WCHAR *name;
3554 char *unix_name;
3555 int name_len, unix_len;
3556 NTSTATUS status;
3558 if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
3559 return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition );
3561 name = attr->ObjectName->Buffer;
3562 name_len = attr->ObjectName->Length / sizeof(WCHAR);
3564 if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER;
3566 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3567 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3568 unix_name[0] = '.';
3570 if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3572 if (type != FD_TYPE_DIR)
3574 if (needs_close) close( root_fd );
3575 status = STATUS_BAD_DEVICE_TYPE;
3577 else
3579 mutex_lock( &dir_mutex );
3580 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3582 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1, disposition, FALSE );
3583 if (fchdir( old_cwd ) == -1) chdir( "/" );
3585 else status = errno_to_status( errno );
3586 mutex_unlock( &dir_mutex );
3587 if (old_cwd != -1) close( old_cwd );
3588 if (needs_close) close( root_fd );
3591 else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
3593 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3595 TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
3596 *name_ret = unix_name;
3598 else
3600 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3601 free( unix_name );
3603 return status;
3607 /******************************************************************************
3608 * wine_nt_to_unix_file_name
3610 * Convert a file name from NT namespace to Unix namespace.
3612 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3613 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3614 * returned, but the unix name is still filled in properly.
3616 NTSTATUS WINAPI wine_nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char *nameA, ULONG *size,
3617 UINT disposition )
3619 char *buffer = NULL;
3620 NTSTATUS status;
3621 UNICODE_STRING redir;
3622 OBJECT_ATTRIBUTES new_attr = *attr;
3624 get_redirect( &new_attr, &redir );
3625 status = nt_to_unix_file_name( &new_attr, &buffer, disposition );
3627 if (buffer)
3629 struct stat st1, st2;
3630 char *name = buffer;
3632 /* remove dosdevices prefix for z: drive if it points to the Unix root */
3633 if (!strncmp( buffer, config_dir, strlen(config_dir) ) &&
3634 !strncmp( buffer + strlen(config_dir), "/dosdevices/z:/", 15 ))
3636 char *p = buffer + strlen(config_dir) + 14;
3637 *p = 0;
3638 if (!stat( buffer, &st1 ) && !stat( "/", &st2 ) &&
3639 st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
3640 name = p;
3641 *p = '/';
3644 if (*size > strlen(name)) strcpy( nameA, name );
3645 else status = STATUS_BUFFER_TOO_SMALL;
3646 *size = strlen(name) + 1;
3647 free( buffer );
3649 free( redir.Buffer );
3650 return status;
3654 /******************************************************************
3655 * collapse_path
3657 * Get rid of . and .. components in the path.
3659 static void collapse_path( WCHAR *path )
3661 WCHAR *p, *start, *next;
3663 /* convert every / into a \ */
3664 for (p = path; *p; p++) if (*p == '/') *p = '\\';
3666 p = path + 4;
3667 while (*p && *p != '\\') p++;
3668 start = p + 1;
3670 /* collapse duplicate backslashes */
3671 next = start;
3672 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
3673 *next = 0;
3675 p = start;
3676 while (*p)
3678 if (*p == '.')
3680 switch(p[1])
3682 case '\\': /* .\ component */
3683 next = p + 2;
3684 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3685 continue;
3686 case 0: /* final . */
3687 if (p > start) p--;
3688 *p = 0;
3689 continue;
3690 case '.':
3691 if (p[2] == '\\') /* ..\ component */
3693 next = p + 3;
3694 if (p > start)
3696 p--;
3697 while (p > start && p[-1] != '\\') p--;
3699 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3700 continue;
3702 else if (!p[2]) /* final .. */
3704 if (p > start)
3706 p--;
3707 while (p > start && p[-1] != '\\') p--;
3708 if (p > start) p--;
3710 *p = 0;
3711 continue;
3713 break;
3716 /* skip to the next component */
3717 while (*p && *p != '\\') p++;
3718 if (*p == '\\')
3720 /* remove last dot in previous dir name */
3721 if (p > start && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
3722 else p++;
3726 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3727 while (p > start && (p[-1] == ' ' || p[-1] == '.')) p--;
3728 *p = 0;
3732 /******************************************************************
3733 * unix_to_nt_file_name
3735 NTSTATUS unix_to_nt_file_name( const char *name, WCHAR **nt )
3737 static const WCHAR unix_prefixW[] = {'\\','?','?','\\','u','n','i','x',0};
3738 WCHAR dos_prefixW[] = {'\\','?','?','\\','A',':','\\',0};
3739 const WCHAR *prefix = unix_prefixW;
3740 unsigned int lenW, lenA = strlen(name);
3741 const char *path = name;
3742 NTSTATUS status;
3743 WCHAR *buffer;
3744 int drive;
3746 status = find_drive_rootA( &path, lenA, &drive );
3747 lenA -= path - name;
3749 if (status == STATUS_SUCCESS)
3751 while (lenA && path[0] == '/') { lenA--; path++; }
3752 dos_prefixW[4] += drive;
3753 prefix = dos_prefixW;
3755 else if (status != STATUS_OBJECT_PATH_NOT_FOUND) return status;
3757 lenW = wcslen( prefix );
3758 if (!(buffer = malloc( (lenA + lenW + 1) * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
3759 memcpy( buffer, prefix, lenW * sizeof(WCHAR) );
3760 lenW += ntdll_umbstowcs( path, lenA, buffer + lenW, lenA );
3761 buffer[lenW] = 0;
3762 collapse_path( buffer );
3763 *nt = buffer;
3764 return STATUS_SUCCESS;
3768 /******************************************************************
3769 * wine_unix_to_nt_file_name
3771 NTSTATUS WINAPI wine_unix_to_nt_file_name( const char *name, WCHAR *buffer, ULONG *size )
3773 WCHAR *nt_name = NULL;
3774 NTSTATUS status;
3776 if (name[0] != '/') return STATUS_INVALID_PARAMETER; /* relative paths are not supported */
3778 status = unix_to_nt_file_name( name, &nt_name );
3779 if (nt_name)
3781 if (*size > wcslen(nt_name)) wcscpy( buffer, nt_name );
3782 else status = STATUS_BUFFER_TOO_SMALL;
3783 *size = wcslen(nt_name) + 1;
3784 free( nt_name );
3786 return status;
3790 /***********************************************************************
3791 * get_full_path
3793 * Simplified version of RtlGetFullPathName_U.
3795 NTSTATUS get_full_path( const WCHAR *name, const WCHAR *curdir, WCHAR **path )
3797 static const WCHAR uncW[] = {'\\','?','?','\\','U','N','C','\\',0};
3798 static const WCHAR devW[] = {'\\','?','?','\\',0};
3799 static const WCHAR unixW[] = {'u','n','i','x'};
3800 WCHAR *ret, root[] = {'\\','?','?','\\','C',':','\\',0};
3801 NTSTATUS status = STATUS_SUCCESS;
3802 const WCHAR *prefix;
3804 if (IS_SEPARATOR(name[0]) && IS_SEPARATOR(name[1])) /* \\ prefix */
3806 if ((name[2] == '.' || name[2] == '?') && IS_SEPARATOR(name[3])) /* \\?\ device */
3808 name += 4;
3809 if (!wcsnicmp( name, unixW, 4 ) && IS_SEPARATOR(name[4])) /* \\?\unix special name */
3811 char *unix_name;
3812 name += 4;
3813 unix_name = malloc( wcslen(name) * 3 + 1 );
3814 ntdll_wcstoumbs( name, wcslen(name) + 1, unix_name, wcslen(name) * 3 + 1, FALSE );
3815 status = unix_to_nt_file_name( unix_name, path );
3816 free( unix_name );
3817 return status;
3819 prefix = devW;
3821 else prefix = uncW; /* UNC path */
3823 else if (IS_SEPARATOR(name[0])) /* absolute path */
3825 root[4] = curdir[4];
3826 prefix = root;
3828 else if (name[0] && name[1] == ':') /* drive letter */
3830 root[4] = towupper(name[0]);
3831 name += 2;
3832 prefix = root;
3834 else prefix = curdir; /* relative path */
3836 ret = malloc( (wcslen(prefix) + wcslen(name) + 1) * sizeof(WCHAR) );
3837 wcscpy( ret, prefix );
3838 wcscat( ret, name );
3839 collapse_path( ret );
3840 *path = ret;
3841 return STATUS_SUCCESS;
3845 /***********************************************************************
3846 * unmount_device
3848 * Unmount the specified device.
3850 static NTSTATUS unmount_device( HANDLE handle )
3852 NTSTATUS status;
3853 int unix_fd, needs_close;
3855 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
3857 struct stat st;
3858 char *mount_point = NULL;
3860 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
3861 status = STATUS_INVALID_PARAMETER;
3862 else
3864 if ((mount_point = get_device_mount_point( st.st_rdev )))
3866 #ifdef __APPLE__
3867 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
3868 #else
3869 static const char umount[] = "umount >/dev/null 2>&1 ";
3870 #endif
3871 char *cmd = malloc( strlen(mount_point)+sizeof(umount));
3872 if (cmd)
3874 strcpy( cmd, umount );
3875 strcat( cmd, mount_point );
3876 system( cmd );
3877 free( cmd );
3878 #ifdef linux
3879 /* umount will fail to release the loop device since we still have
3880 a handle to it, so we release it here */
3881 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3882 #endif
3884 free( mount_point );
3887 if (needs_close) close( unix_fd );
3889 return status;
3893 /******************************************************************************
3894 * open_unix_file
3896 * Helper for NtCreateFile that takes a Unix path.
3898 NTSTATUS open_unix_file( HANDLE *handle, const char *unix_name, ACCESS_MASK access,
3899 OBJECT_ATTRIBUTES *attr, ULONG attributes, ULONG sharing, ULONG disposition,
3900 ULONG options, void *ea_buffer, ULONG ea_length )
3902 struct object_attributes *objattr;
3903 NTSTATUS status;
3904 data_size_t len;
3906 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3908 SERVER_START_REQ( create_file )
3910 req->access = access;
3911 req->sharing = sharing;
3912 req->create = disposition;
3913 req->options = options;
3914 req->attrs = attributes;
3915 wine_server_add_data( req, objattr, len );
3916 wine_server_add_data( req, unix_name, strlen(unix_name) );
3917 status = wine_server_call( req );
3918 *handle = wine_server_ptr_handle( reply->handle );
3920 SERVER_END_REQ;
3921 free( objattr );
3922 return status;
3926 /******************************************************************************
3927 * NtCreateFile (NTDLL.@)
3929 NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
3930 IO_STATUS_BLOCK *io, LARGE_INTEGER *alloc_size,
3931 ULONG attributes, ULONG sharing, ULONG disposition,
3932 ULONG options, void *ea_buffer, ULONG ea_length )
3934 OBJECT_ATTRIBUTES new_attr;
3935 UNICODE_STRING nt_name;
3936 char *unix_name;
3937 BOOL created = FALSE;
3938 NTSTATUS status;
3940 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
3941 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
3942 handle, access, debugstr_us(attr->ObjectName), attr->Attributes,
3943 attr->RootDirectory, attr->SecurityDescriptor, io, alloc_size,
3944 attributes, sharing, disposition, options, ea_buffer, ea_length );
3946 *handle = 0;
3947 if (!attr || !attr->ObjectName) return STATUS_INVALID_PARAMETER;
3949 if (alloc_size) FIXME( "alloc_size not supported\n" );
3951 new_attr = *attr;
3952 if (options & FILE_OPEN_BY_FILE_ID)
3954 status = file_id_to_unix_file_name( &new_attr, &unix_name, &nt_name );
3955 if (!status) new_attr.ObjectName = &nt_name;
3957 else
3959 get_redirect( &new_attr, &nt_name );
3960 status = nt_to_unix_file_name( &new_attr, &unix_name, disposition );
3963 if (status == STATUS_BAD_DEVICE_TYPE)
3965 status = server_open_file_object( handle, access, &new_attr, sharing, options );
3966 if (status == STATUS_SUCCESS) io->Information = FILE_OPENED;
3967 free( nt_name.Buffer );
3968 return io->u.Status = status;
3971 if (status == STATUS_NO_SUCH_FILE && disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3973 created = TRUE;
3974 status = STATUS_SUCCESS;
3977 if (status == STATUS_SUCCESS)
3979 status = open_unix_file( handle, unix_name, access, &new_attr, attributes,
3980 sharing, disposition, options, ea_buffer, ea_length );
3981 free( unix_name );
3983 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
3985 if (status == STATUS_SUCCESS)
3987 if (created) io->Information = FILE_CREATED;
3988 else switch(disposition)
3990 case FILE_SUPERSEDE:
3991 io->Information = FILE_SUPERSEDED;
3992 break;
3993 case FILE_CREATE:
3994 io->Information = FILE_CREATED;
3995 break;
3996 case FILE_OPEN:
3997 case FILE_OPEN_IF:
3998 io->Information = FILE_OPENED;
3999 break;
4000 case FILE_OVERWRITE:
4001 case FILE_OVERWRITE_IF:
4002 io->Information = FILE_OVERWRITTEN;
4003 break;
4006 if (io->Information == FILE_CREATED && (attributes & XATTR_ATTRIBS_MASK))
4008 int fd, needs_close;
4010 /* set any DOS extended attributes */
4011 if (!server_get_unix_fd( *handle, 0, &fd, &needs_close, NULL, NULL ))
4013 if (fd_set_dos_attrib( fd, attributes ) == -1 && errno != ENOTSUP)
4014 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)",
4015 errno, strerror( errno ) );
4016 if (needs_close) close( fd );
4020 else if (status == STATUS_TOO_MANY_OPENED_FILES)
4022 static int once;
4023 if (!once++) ERR_(winediag)( "Too many open files, ulimit -n probably needs to be increased\n" );
4026 free( nt_name.Buffer );
4027 return io->u.Status = status;
4031 /******************************************************************************
4032 * NtOpenFile (NTDLL.@)
4034 NTSTATUS WINAPI NtOpenFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
4035 IO_STATUS_BLOCK *io, ULONG sharing, ULONG options )
4037 return NtCreateFile( handle, access, attr, io, NULL, 0, sharing, FILE_OPEN, options, NULL, 0 );
4041 /******************************************************************************
4042 * NtCreateMailslotFile (NTDLL.@)
4044 NTSTATUS WINAPI NtCreateMailslotFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
4045 IO_STATUS_BLOCK *io, ULONG options, ULONG quota, ULONG msg_size,
4046 LARGE_INTEGER *timeout )
4048 NTSTATUS status;
4049 data_size_t len;
4050 struct object_attributes *objattr;
4052 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
4053 handle, access, attr, io, options, quota, msg_size, timeout );
4055 *handle = 0;
4056 if (!attr) return STATUS_INVALID_PARAMETER;
4057 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
4059 SERVER_START_REQ( create_mailslot )
4061 req->access = access;
4062 req->max_msgsize = msg_size;
4063 req->read_timeout = timeout ? timeout->QuadPart : -1;
4064 wine_server_add_data( req, objattr, len );
4065 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
4067 SERVER_END_REQ;
4069 free( objattr );
4070 return status;
4074 /******************************************************************
4075 * NtCreateNamedPipeFile (NTDLL.@)
4077 NTSTATUS WINAPI NtCreateNamedPipeFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
4078 IO_STATUS_BLOCK *io, ULONG sharing, ULONG dispo, ULONG options,
4079 ULONG pipe_type, ULONG read_mode, ULONG completion_mode,
4080 ULONG max_inst, ULONG inbound_quota, ULONG outbound_quota,
4081 LARGE_INTEGER *timeout )
4083 NTSTATUS status;
4084 data_size_t len;
4085 struct object_attributes *objattr;
4087 *handle = 0;
4088 if (!attr) return STATUS_INVALID_PARAMETER;
4090 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
4091 handle, access, debugstr_us(attr->ObjectName), io, sharing, dispo,
4092 options, pipe_type, read_mode, completion_mode, max_inst, inbound_quota,
4093 outbound_quota, timeout );
4095 /* assume we only get relative timeout */
4096 if (timeout->QuadPart > 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout->QuadPart) );
4098 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
4100 SERVER_START_REQ( create_named_pipe )
4102 req->access = access;
4103 req->options = options;
4104 req->sharing = sharing;
4105 req->flags =
4106 (pipe_type ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0) |
4107 (read_mode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0) |
4108 (completion_mode ? NAMED_PIPE_NONBLOCKING_MODE : 0);
4109 req->maxinstances = max_inst;
4110 req->outsize = outbound_quota;
4111 req->insize = inbound_quota;
4112 req->timeout = timeout->QuadPart;
4113 wine_server_add_data( req, objattr, len );
4114 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
4116 SERVER_END_REQ;
4118 free( objattr );
4119 return status;
4123 /******************************************************************
4124 * NtDeleteFile (NTDLL.@)
4126 NTSTATUS WINAPI NtDeleteFile( OBJECT_ATTRIBUTES *attr )
4128 HANDLE handle;
4129 NTSTATUS status;
4130 char *unix_name;
4131 UNICODE_STRING nt_name;
4132 OBJECT_ATTRIBUTES new_attr = *attr;
4134 get_redirect( &new_attr, &nt_name );
4135 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
4137 if (!(status = open_unix_file( &handle, unix_name, GENERIC_READ | GENERIC_WRITE | DELETE, &new_attr,
4138 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN,
4139 FILE_DELETE_ON_CLOSE, NULL, 0 )))
4140 NtClose( handle );
4141 free( unix_name );
4143 free( nt_name.Buffer );
4144 return status;
4148 /******************************************************************************
4149 * NtQueryFullAttributesFile (NTDLL.@)
4151 NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr,
4152 FILE_NETWORK_OPEN_INFORMATION *info )
4154 char *unix_name;
4155 NTSTATUS status;
4156 UNICODE_STRING redir;
4157 OBJECT_ATTRIBUTES new_attr = *attr;
4159 get_redirect( &new_attr, &redir );
4160 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
4162 ULONG attributes;
4163 struct stat st;
4165 if (get_file_info( unix_name, &st, &attributes ) == -1)
4166 status = errno_to_status( errno );
4167 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4168 status = STATUS_INVALID_INFO_CLASS;
4169 else
4171 FILE_BASIC_INFORMATION basic;
4172 FILE_STANDARD_INFORMATION std;
4174 fill_file_info( &st, attributes, &basic, FileBasicInformation );
4175 fill_file_info( &st, attributes, &std, FileStandardInformation );
4177 info->CreationTime = basic.CreationTime;
4178 info->LastAccessTime = basic.LastAccessTime;
4179 info->LastWriteTime = basic.LastWriteTime;
4180 info->ChangeTime = basic.ChangeTime;
4181 info->AllocationSize = std.AllocationSize;
4182 info->EndOfFile = std.EndOfFile;
4183 info->FileAttributes = basic.FileAttributes;
4184 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
4186 free( unix_name );
4188 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
4189 free( redir.Buffer );
4190 return status;
4194 /******************************************************************************
4195 * NtQueryAttributesFile (NTDLL.@)
4197 NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC_INFORMATION *info )
4199 char *unix_name;
4200 NTSTATUS status;
4201 UNICODE_STRING redir;
4202 OBJECT_ATTRIBUTES new_attr = *attr;
4204 get_redirect( &new_attr, &redir );
4205 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
4207 ULONG attributes;
4208 struct stat st;
4210 if (get_file_info( unix_name, &st, &attributes ) == -1)
4211 status = errno_to_status( errno );
4212 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4213 status = STATUS_INVALID_INFO_CLASS;
4214 else
4216 status = fill_file_info( &st, attributes, info, FileBasicInformation );
4217 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
4219 free( unix_name );
4221 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
4222 free( redir.Buffer );
4223 return status;
4227 /******************************************************************************
4228 * NtQueryInformationFile (NTDLL.@)
4230 NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4231 void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
4233 static const size_t info_sizes[] =
4236 sizeof(FILE_DIRECTORY_INFORMATION), /* FileDirectoryInformation */
4237 sizeof(FILE_FULL_DIRECTORY_INFORMATION), /* FileFullDirectoryInformation */
4238 sizeof(FILE_BOTH_DIRECTORY_INFORMATION), /* FileBothDirectoryInformation */
4239 sizeof(FILE_BASIC_INFORMATION), /* FileBasicInformation */
4240 sizeof(FILE_STANDARD_INFORMATION), /* FileStandardInformation */
4241 sizeof(FILE_INTERNAL_INFORMATION), /* FileInternalInformation */
4242 sizeof(FILE_EA_INFORMATION), /* FileEaInformation */
4243 0, /* FileAccessInformation */
4244 sizeof(FILE_NAME_INFORMATION), /* FileNameInformation */
4245 sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
4246 0, /* FileLinkInformation */
4247 sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR), /* FileNamesInformation */
4248 sizeof(FILE_DISPOSITION_INFORMATION), /* FileDispositionInformation */
4249 sizeof(FILE_POSITION_INFORMATION), /* FilePositionInformation */
4250 sizeof(FILE_FULL_EA_INFORMATION), /* FileFullEaInformation */
4251 0, /* FileModeInformation */
4252 sizeof(FILE_ALIGNMENT_INFORMATION), /* FileAlignmentInformation */
4253 sizeof(FILE_ALL_INFORMATION), /* FileAllInformation */
4254 sizeof(FILE_ALLOCATION_INFORMATION), /* FileAllocationInformation */
4255 sizeof(FILE_END_OF_FILE_INFORMATION), /* FileEndOfFileInformation */
4256 0, /* FileAlternateNameInformation */
4257 sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
4258 sizeof(FILE_PIPE_INFORMATION), /* FilePipeInformation */
4259 sizeof(FILE_PIPE_LOCAL_INFORMATION), /* FilePipeLocalInformation */
4260 0, /* FilePipeRemoteInformation */
4261 sizeof(FILE_MAILSLOT_QUERY_INFORMATION), /* FileMailslotQueryInformation */
4262 0, /* FileMailslotSetInformation */
4263 0, /* FileCompressionInformation */
4264 0, /* FileObjectIdInformation */
4265 0, /* FileCompletionInformation */
4266 0, /* FileMoveClusterInformation */
4267 0, /* FileQuotaInformation */
4268 0, /* FileReparsePointInformation */
4269 sizeof(FILE_NETWORK_OPEN_INFORMATION), /* FileNetworkOpenInformation */
4270 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), /* FileAttributeTagInformation */
4271 0, /* FileTrackingInformation */
4272 0, /* FileIdBothDirectoryInformation */
4273 0, /* FileIdFullDirectoryInformation */
4274 0, /* FileValidDataLengthInformation */
4275 0, /* FileShortNameInformation */
4276 0, /* FileIoCompletionNotificationInformation, */
4277 0, /* FileIoStatusBlockRangeInformation */
4278 0, /* FileIoPriorityHintInformation */
4279 0, /* FileSfioReserveInformation */
4280 0, /* FileSfioVolumeInformation */
4281 0, /* FileHardLinkInformation */
4282 0, /* FileProcessIdsUsingFileInformation */
4283 0, /* FileNormalizedNameInformation */
4284 0, /* FileNetworkPhysicalNameInformation */
4285 0, /* FileIdGlobalTxDirectoryInformation */
4286 0, /* FileIsRemoteDeviceInformation */
4287 0, /* FileAttributeCacheInformation */
4288 0, /* FileNumaNodeInformation */
4289 0, /* FileStandardLinkInformation */
4290 0, /* FileRemoteProtocolInformation */
4291 0, /* FileRenameInformationBypassAccessCheck */
4292 0, /* FileLinkInformationBypassAccessCheck */
4293 0, /* FileVolumeNameInformation */
4294 sizeof(FILE_ID_INFORMATION), /* FileIdInformation */
4295 0, /* FileIdExtdDirectoryInformation */
4296 0, /* FileReplaceCompletionInformation */
4297 0, /* FileHardLinkFullIdInformation */
4298 0, /* FileIdExtdBothDirectoryInformation */
4301 struct stat st;
4302 int fd, needs_close = FALSE;
4303 ULONG attr;
4304 unsigned int options;
4305 NTSTATUS status;
4307 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class);
4309 io->Information = 0;
4311 if (class <= 0 || class >= FileMaximumInformation)
4312 return io->u.Status = STATUS_INVALID_INFO_CLASS;
4313 if (!info_sizes[class])
4314 return server_get_file_info( handle, io, ptr, len, class );
4315 if (len < info_sizes[class])
4316 return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4318 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options )))
4320 if (status != STATUS_BAD_DEVICE_TYPE) return io->u.Status = status;
4321 return server_get_file_info( handle, io, ptr, len, class );
4324 switch (class)
4326 case FileBasicInformation:
4327 if (fd_get_file_info( fd, options, &st, &attr ) == -1)
4328 status = errno_to_status( errno );
4329 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4330 status = STATUS_INVALID_INFO_CLASS;
4331 else
4332 fill_file_info( &st, attr, ptr, class );
4333 break;
4334 case FileStandardInformation:
4336 FILE_STANDARD_INFORMATION *info = ptr;
4338 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4339 else
4341 fill_file_info( &st, attr, info, class );
4342 info->DeletePending = FALSE; /* FIXME */
4345 break;
4346 case FilePositionInformation:
4348 FILE_POSITION_INFORMATION *info = ptr;
4349 off_t res = lseek( fd, 0, SEEK_CUR );
4350 if (res == (off_t)-1) status = errno_to_status( errno );
4351 else info->CurrentByteOffset.QuadPart = res;
4353 break;
4354 case FileInternalInformation:
4355 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4356 else fill_file_info( &st, attr, ptr, class );
4357 break;
4358 case FileEaInformation:
4360 FILE_EA_INFORMATION *info = ptr;
4361 info->EaSize = 0;
4363 break;
4364 case FileEndOfFileInformation:
4365 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4366 else fill_file_info( &st, attr, ptr, class );
4367 break;
4368 case FileAllInformation:
4370 FILE_ALL_INFORMATION *info = ptr;
4371 char *unix_name;
4373 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4374 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4375 status = STATUS_INVALID_INFO_CLASS;
4376 else if (!(status = server_get_unix_name( handle, &unix_name )))
4378 LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
4380 fill_file_info( &st, attr, info, FileAllInformation );
4381 info->StandardInformation.DeletePending = FALSE; /* FIXME */
4382 info->EaInformation.EaSize = 0;
4383 info->AccessInformation.AccessFlags = 0; /* FIXME */
4384 info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
4385 info->ModeInformation.Mode = 0; /* FIXME */
4386 info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
4388 status = fill_name_info( unix_name, &info->NameInformation, &name_len );
4389 free( unix_name );
4390 io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len;
4393 break;
4394 case FileMailslotQueryInformation:
4396 FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;
4398 SERVER_START_REQ( set_mailslot_info )
4400 req->handle = wine_server_obj_handle( handle );
4401 req->flags = 0;
4402 status = wine_server_call( req );
4403 if (status == STATUS_SUCCESS)
4405 info->MaximumMessageSize = reply->max_msgsize;
4406 info->MailslotQuota = 0;
4407 info->NextMessageSize = 0;
4408 info->MessagesAvailable = 0;
4409 info->ReadTimeout.QuadPart = reply->read_timeout;
4412 SERVER_END_REQ;
4413 if (!status)
4415 char *tmpbuf;
4416 ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
4417 if (size > 0x10000) size = 0x10000;
4418 if ((tmpbuf = malloc( size )))
4420 if (!server_get_unix_fd( handle, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
4422 int res = recv( fd, tmpbuf, size, MSG_PEEK );
4423 info->MessagesAvailable = (res > 0);
4424 info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
4425 if (needs_close) close( fd );
4427 free( tmpbuf );
4431 break;
4432 case FileNameInformation:
4434 FILE_NAME_INFORMATION *info = ptr;
4435 char *unix_name;
4437 if (!(status = server_get_unix_name( handle, &unix_name )))
4439 LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
4440 status = fill_name_info( unix_name, info, &name_len );
4441 free( unix_name );
4442 io->Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + name_len;
4445 break;
4446 case FileNetworkOpenInformation:
4448 FILE_NETWORK_OPEN_INFORMATION *info = ptr;
4449 char *unix_name;
4451 if (!(status = server_get_unix_name( handle, &unix_name )))
4453 ULONG attributes;
4454 struct stat st;
4456 if (get_file_info( unix_name, &st, &attributes ) == -1)
4457 status = errno_to_status( errno );
4458 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4459 status = STATUS_INVALID_INFO_CLASS;
4460 else
4462 FILE_BASIC_INFORMATION basic;
4463 FILE_STANDARD_INFORMATION std;
4465 fill_file_info( &st, attributes, &basic, FileBasicInformation );
4466 fill_file_info( &st, attributes, &std, FileStandardInformation );
4468 info->CreationTime = basic.CreationTime;
4469 info->LastAccessTime = basic.LastAccessTime;
4470 info->LastWriteTime = basic.LastWriteTime;
4471 info->ChangeTime = basic.ChangeTime;
4472 info->AllocationSize = std.AllocationSize;
4473 info->EndOfFile = std.EndOfFile;
4474 info->FileAttributes = basic.FileAttributes;
4476 free( unix_name );
4479 break;
4480 case FileIdInformation:
4481 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4482 else
4484 struct mountmgr_unix_drive drive;
4485 FILE_ID_INFORMATION *info = ptr;
4487 info->VolumeSerialNumber = 0;
4488 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) ))
4489 info->VolumeSerialNumber = drive.serial;
4490 memset( &info->FileId, 0, sizeof(info->FileId) );
4491 *(ULONGLONG *)&info->FileId = st.st_ino;
4493 break;
4494 case FileAttributeTagInformation:
4495 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4496 else
4498 FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
4499 info->FileAttributes = attr;
4500 info->ReparseTag = 0; /* FIXME */
4501 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
4502 info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
4504 break;
4505 default:
4506 FIXME("Unsupported class (%d)\n", class);
4507 status = STATUS_NOT_IMPLEMENTED;
4508 break;
4510 if (needs_close) close( fd );
4511 if (status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
4512 return io->u.Status = status;
4516 /******************************************************************************
4517 * NtSetInformationFile (NTDLL.@)
4519 NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4520 void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
4522 int fd, needs_close;
4523 NTSTATUS status = STATUS_SUCCESS;
4525 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class );
4527 switch (class)
4529 case FileBasicInformation:
4530 if (len >= sizeof(FILE_BASIC_INFORMATION))
4532 const FILE_BASIC_INFORMATION *info = ptr;
4533 LARGE_INTEGER mtime, atime;
4535 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4536 return io->u.Status = status;
4538 mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart;
4539 atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
4541 if (atime.QuadPart || mtime.QuadPart)
4542 status = set_file_times( fd, &mtime, &atime );
4544 if (status == STATUS_SUCCESS && info->FileAttributes)
4545 status = fd_set_file_info( fd, info->FileAttributes );
4547 if (needs_close) close( fd );
4549 else status = STATUS_INVALID_PARAMETER_3;
4550 break;
4552 case FilePositionInformation:
4553 if (len >= sizeof(FILE_POSITION_INFORMATION))
4555 const FILE_POSITION_INFORMATION *info = ptr;
4557 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4558 return io->u.Status = status;
4560 if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
4561 status = errno_to_status( errno );
4563 if (needs_close) close( fd );
4565 else status = STATUS_INVALID_PARAMETER_3;
4566 break;
4568 case FileEndOfFileInformation:
4569 if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
4571 const FILE_END_OF_FILE_INFORMATION *info = ptr;
4573 SERVER_START_REQ( set_fd_eof_info )
4575 req->handle = wine_server_obj_handle( handle );
4576 req->eof = info->EndOfFile.QuadPart;
4577 status = wine_server_call( req );
4579 SERVER_END_REQ;
4581 else status = STATUS_INVALID_PARAMETER_3;
4582 break;
4584 case FilePipeInformation:
4585 if (len >= sizeof(FILE_PIPE_INFORMATION))
4587 FILE_PIPE_INFORMATION *info = ptr;
4589 if ((info->CompletionMode | info->ReadMode) & ~1)
4591 status = STATUS_INVALID_PARAMETER;
4592 break;
4595 SERVER_START_REQ( set_named_pipe_info )
4597 req->handle = wine_server_obj_handle( handle );
4598 req->flags = (info->CompletionMode ? NAMED_PIPE_NONBLOCKING_MODE : 0) |
4599 (info->ReadMode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0);
4600 status = wine_server_call( req );
4602 SERVER_END_REQ;
4604 else status = STATUS_INVALID_PARAMETER_3;
4605 break;
4607 case FileMailslotSetInformation:
4609 FILE_MAILSLOT_SET_INFORMATION *info = ptr;
4611 SERVER_START_REQ( set_mailslot_info )
4613 req->handle = wine_server_obj_handle( handle );
4614 req->flags = MAILSLOT_SET_READ_TIMEOUT;
4615 req->read_timeout = info->ReadTimeout.QuadPart;
4616 status = wine_server_call( req );
4618 SERVER_END_REQ;
4620 break;
4622 case FileCompletionInformation:
4623 if (len >= sizeof(FILE_COMPLETION_INFORMATION))
4625 FILE_COMPLETION_INFORMATION *info = ptr;
4627 SERVER_START_REQ( set_completion_info )
4629 req->handle = wine_server_obj_handle( handle );
4630 req->chandle = wine_server_obj_handle( info->CompletionPort );
4631 req->ckey = info->CompletionKey;
4632 status = wine_server_call( req );
4634 SERVER_END_REQ;
4636 else status = STATUS_INVALID_PARAMETER_3;
4637 break;
4639 case FileIoCompletionNotificationInformation:
4640 if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION))
4642 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr;
4644 if (info->Flags & FILE_SKIP_SET_USER_EVENT_ON_FAST_IO)
4645 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4647 SERVER_START_REQ( set_fd_completion_mode )
4649 req->handle = wine_server_obj_handle( handle );
4650 req->flags = info->Flags;
4651 status = wine_server_call( req );
4653 SERVER_END_REQ;
4655 else status = STATUS_INFO_LENGTH_MISMATCH;
4656 break;
4658 case FileIoPriorityHintInformation:
4659 if (len >= sizeof(FILE_IO_PRIORITY_HINT_INFO))
4661 FILE_IO_PRIORITY_HINT_INFO *info = ptr;
4662 if (info->PriorityHint < MaximumIoPriorityHintType)
4663 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info->PriorityHint );
4664 else
4665 status = STATUS_INVALID_PARAMETER;
4667 else status = STATUS_INFO_LENGTH_MISMATCH;
4668 break;
4670 case FileAllInformation:
4671 status = STATUS_INVALID_INFO_CLASS;
4672 break;
4674 case FileValidDataLengthInformation:
4675 if (len >= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION))
4677 struct stat st;
4678 const FILE_VALID_DATA_LENGTH_INFORMATION *info = ptr;
4680 if ((status = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL )))
4681 return io->u.Status = status;
4683 if (fstat( fd, &st ) == -1) status = errno_to_status( errno );
4684 else if (info->ValidDataLength.QuadPart <= 0 || (off_t)info->ValidDataLength.QuadPart > st.st_size)
4685 status = STATUS_INVALID_PARAMETER;
4686 else
4688 #ifdef HAVE_POSIX_FALLOCATE
4689 int err;
4690 if ((err = posix_fallocate( fd, 0, (off_t)info->ValidDataLength.QuadPart )) != 0)
4692 if (err == EOPNOTSUPP) WARN( "posix_fallocate not supported on this filesystem\n" );
4693 else status = errno_to_status( err );
4695 #else
4696 WARN( "setting valid data length not supported\n" );
4697 #endif
4699 if (needs_close) close( fd );
4701 else status = STATUS_INVALID_PARAMETER_3;
4702 break;
4704 case FileDispositionInformation:
4705 if (len >= sizeof(FILE_DISPOSITION_INFORMATION))
4707 FILE_DISPOSITION_INFORMATION *info = ptr;
4709 SERVER_START_REQ( set_fd_disp_info )
4711 req->handle = wine_server_obj_handle( handle );
4712 req->unlink = info->DoDeleteFile;
4713 status = wine_server_call( req );
4715 SERVER_END_REQ;
4717 else status = STATUS_INVALID_PARAMETER_3;
4718 break;
4720 case FileRenameInformation:
4721 if (len >= sizeof(FILE_RENAME_INFORMATION))
4723 FILE_RENAME_INFORMATION *info = ptr;
4724 UNICODE_STRING name_str, redir;
4725 OBJECT_ATTRIBUTES attr;
4726 char *unix_name;
4728 name_str.Buffer = info->FileName;
4729 name_str.Length = info->FileNameLength;
4730 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4731 InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL );
4732 get_redirect( &attr, &redir );
4734 status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF );
4735 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
4737 SERVER_START_REQ( set_fd_name_info )
4739 req->handle = wine_server_obj_handle( handle );
4740 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4741 req->namelen = attr.ObjectName->Length;
4742 req->link = FALSE;
4743 req->replace = info->ReplaceIfExists;
4744 wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length );
4745 wine_server_add_data( req, unix_name, strlen(unix_name) );
4746 status = wine_server_call( req );
4748 SERVER_END_REQ;
4750 free( unix_name );
4752 free( redir.Buffer );
4754 else status = STATUS_INVALID_PARAMETER_3;
4755 break;
4757 case FileLinkInformation:
4758 if (len >= sizeof(FILE_LINK_INFORMATION))
4760 FILE_LINK_INFORMATION *info = ptr;
4761 UNICODE_STRING name_str, redir;
4762 OBJECT_ATTRIBUTES attr;
4763 char *unix_name;
4765 name_str.Buffer = info->FileName;
4766 name_str.Length = info->FileNameLength;
4767 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4768 InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL );
4769 get_redirect( &attr, &redir );
4771 status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF );
4772 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
4774 SERVER_START_REQ( set_fd_name_info )
4776 req->handle = wine_server_obj_handle( handle );
4777 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4778 req->namelen = attr.ObjectName->Length;
4779 req->link = TRUE;
4780 req->replace = info->ReplaceIfExists;
4781 wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length );
4782 wine_server_add_data( req, unix_name, strlen(unix_name) );
4783 status = wine_server_call( req );
4785 SERVER_END_REQ;
4787 free( unix_name );
4789 free( redir.Buffer );
4791 else status = STATUS_INVALID_PARAMETER_3;
4792 break;
4794 default:
4795 FIXME("Unsupported class (%d)\n", class);
4796 status = STATUS_NOT_IMPLEMENTED;
4797 break;
4799 io->Information = 0;
4800 return io->u.Status = status;
4804 /***********************************************************************
4805 * Asynchronous file I/O *
4808 struct async_fileio_read
4810 struct async_fileio io;
4811 char *buffer;
4812 unsigned int already;
4813 unsigned int count;
4814 BOOL avail_mode;
4817 struct async_fileio_write
4819 struct async_fileio io;
4820 const char *buffer;
4821 unsigned int already;
4822 unsigned int count;
4825 struct async_fileio_read_changes
4827 struct async_fileio io;
4828 void *buffer;
4829 ULONG buffer_size;
4830 ULONG data_size;
4831 char data[1];
4834 struct async_irp
4836 struct async_fileio io;
4837 void *buffer; /* buffer for output */
4838 ULONG size; /* size of buffer */
4841 static struct async_fileio *fileio_freelist;
4843 void release_fileio( struct async_fileio *io )
4845 for (;;)
4847 struct async_fileio *next = fileio_freelist;
4848 io->next = next;
4849 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist, io, next ) == next) return;
4853 struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle )
4855 /* first free remaining previous fileinfos */
4856 struct async_fileio *io = InterlockedExchangePointer( (void **)&fileio_freelist, NULL );
4858 while (io)
4860 struct async_fileio *next = io->next;
4861 free( io );
4862 io = next;
4865 if ((io = malloc( size )))
4867 io->callback = callback;
4868 io->handle = handle;
4870 return io;
4873 /* callback for irp async I/O completion */
4874 static BOOL irp_completion( void *user, ULONG_PTR *info, NTSTATUS *status )
4876 struct async_irp *async = user;
4878 if (*status == STATUS_ALERTED)
4880 SERVER_START_REQ( get_async_result )
4882 req->user_arg = wine_server_client_ptr( async );
4883 wine_server_set_reply( req, async->buffer, async->size );
4884 *status = virtual_locked_server_call( req );
4886 SERVER_END_REQ;
4888 release_fileio( &async->io );
4889 return TRUE;
4892 static BOOL async_read_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
4894 struct async_fileio_read *fileio = user;
4895 int fd, needs_close, result;
4897 switch (*status)
4899 case STATUS_ALERTED: /* got some new data */
4900 /* check to see if the data is ready (non-blocking) */
4901 if ((*status = server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd,
4902 &needs_close, NULL, NULL )))
4903 break;
4905 result = virtual_locked_read(fd, &fileio->buffer[fileio->already], fileio->count-fileio->already);
4906 if (needs_close) close( fd );
4908 if (result < 0)
4910 if (errno == EAGAIN || errno == EINTR)
4911 return FALSE;
4913 /* check to see if the transfer is complete */
4914 *status = errno_to_status( errno );
4916 else if (result == 0)
4918 *status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN;
4920 else
4922 fileio->already += result;
4924 if (fileio->already < fileio->count && !fileio->avail_mode)
4925 return FALSE;
4927 *status = STATUS_SUCCESS;
4929 break;
4931 case STATUS_TIMEOUT:
4932 case STATUS_IO_TIMEOUT:
4933 if (fileio->already) *status = STATUS_SUCCESS;
4934 break;
4937 *info = fileio->already;
4938 release_fileio( &fileio->io );
4939 return TRUE;
4942 static BOOL async_write_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
4944 struct async_fileio_write *fileio = user;
4945 int result, fd, needs_close;
4946 enum server_fd_type type;
4948 switch (*status)
4950 case STATUS_ALERTED:
4951 /* write some data (non-blocking) */
4952 if ((*status = server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd,
4953 &needs_close, &type, NULL )))
4954 break;
4956 if (!fileio->count && type == FD_TYPE_MAILSLOT)
4957 result = send( fd, fileio->buffer, 0, 0 );
4958 else
4959 result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already );
4961 if (needs_close) close( fd );
4963 if (result < 0)
4965 if (errno == EAGAIN || errno == EINTR) return FALSE;
4966 *status = errno_to_status( errno );
4968 else
4970 fileio->already += result;
4971 if (fileio->already < fileio->count) return FALSE;
4972 *status = STATUS_SUCCESS;
4974 break;
4976 case STATUS_TIMEOUT:
4977 case STATUS_IO_TIMEOUT:
4978 if (fileio->already) *status = STATUS_SUCCESS;
4979 break;
4982 *info = fileio->already;
4983 release_fileio( &fileio->io );
4984 return TRUE;
4987 /* do a read call through the server */
4988 static NTSTATUS server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
4989 IO_STATUS_BLOCK *io, void *buffer, ULONG size,
4990 LARGE_INTEGER *offset, ULONG *key )
4992 struct async_irp *async;
4993 NTSTATUS status;
4994 HANDLE wait_handle;
4995 ULONG options;
4997 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4998 return STATUS_NO_MEMORY;
5000 async->buffer = buffer;
5001 async->size = size;
5003 SERVER_START_REQ( read )
5005 req->async = server_async( handle, &async->io, event, apc, apc_context, iosb_client_ptr(io) );
5006 req->pos = offset ? offset->QuadPart : 0;
5007 wine_server_set_reply( req, buffer, size );
5008 status = virtual_locked_server_call( req );
5009 wait_handle = wine_server_ptr_handle( reply->wait );
5010 options = reply->options;
5011 if (wait_handle && status != STATUS_PENDING)
5013 io->u.Status = status;
5014 io->Information = wine_server_reply_size( reply );
5017 SERVER_END_REQ;
5019 if (status != STATUS_PENDING) free( async );
5021 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
5022 return status;
5025 /* do a write call through the server */
5026 static NTSTATUS server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5027 IO_STATUS_BLOCK *io, const void *buffer, ULONG size,
5028 LARGE_INTEGER *offset, ULONG *key )
5030 struct async_irp *async;
5031 NTSTATUS status;
5032 HANDLE wait_handle;
5033 ULONG options;
5035 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5036 return STATUS_NO_MEMORY;
5038 async->buffer = NULL;
5039 async->size = 0;
5041 SERVER_START_REQ( write )
5043 req->async = server_async( handle, &async->io, event, apc, apc_context, iosb_client_ptr(io) );
5044 req->pos = offset ? offset->QuadPart : 0;
5045 wine_server_add_data( req, buffer, size );
5046 status = wine_server_call( req );
5047 wait_handle = wine_server_ptr_handle( reply->wait );
5048 options = reply->options;
5049 if (wait_handle && status != STATUS_PENDING)
5051 io->u.Status = status;
5052 io->Information = reply->size;
5055 SERVER_END_REQ;
5057 if (status != STATUS_PENDING) free( async );
5059 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
5060 return status;
5063 /* do an ioctl call through the server */
5064 static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
5065 PIO_APC_ROUTINE apc, PVOID apc_context,
5066 IO_STATUS_BLOCK *io, ULONG code,
5067 const void *in_buffer, ULONG in_size,
5068 PVOID out_buffer, ULONG out_size )
5070 struct async_irp *async;
5071 NTSTATUS status;
5072 HANDLE wait_handle;
5073 ULONG options;
5075 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5076 return STATUS_NO_MEMORY;
5077 async->buffer = out_buffer;
5078 async->size = out_size;
5080 SERVER_START_REQ( ioctl )
5082 req->code = code;
5083 req->async = server_async( handle, &async->io, event, apc, apc_context, iosb_client_ptr(io) );
5084 wine_server_add_data( req, in_buffer, in_size );
5085 if ((code & 3) != METHOD_BUFFERED) wine_server_add_data( req, out_buffer, out_size );
5086 wine_server_set_reply( req, out_buffer, out_size );
5087 status = virtual_locked_server_call( req );
5088 wait_handle = wine_server_ptr_handle( reply->wait );
5089 options = reply->options;
5090 if (wait_handle && status != STATUS_PENDING)
5092 io->u.Status = status;
5093 io->Information = wine_server_reply_size( reply );
5096 SERVER_END_REQ;
5098 if (status == STATUS_NOT_SUPPORTED)
5099 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
5100 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
5102 if (status != STATUS_PENDING) free( async );
5104 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
5105 return status;
5109 struct io_timeouts
5111 int interval; /* max interval between two bytes */
5112 int total; /* total timeout for the whole operation */
5113 int end_time; /* absolute time of end of operation */
5116 /* retrieve the I/O timeouts to use for a given handle */
5117 static NTSTATUS get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read,
5118 struct io_timeouts *timeouts )
5120 NTSTATUS status = STATUS_SUCCESS;
5122 timeouts->interval = timeouts->total = -1;
5124 switch(type)
5126 case FD_TYPE_SERIAL:
5128 /* GetCommTimeouts */
5129 SERIAL_TIMEOUTS st;
5130 IO_STATUS_BLOCK io = {0};
5132 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
5133 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
5134 if (status) break;
5136 if (is_read)
5138 if (st.ReadIntervalTimeout)
5139 timeouts->interval = st.ReadIntervalTimeout;
5141 if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant)
5143 timeouts->total = st.ReadTotalTimeoutConstant;
5144 if (st.ReadTotalTimeoutMultiplier != MAXDWORD)
5145 timeouts->total += count * st.ReadTotalTimeoutMultiplier;
5147 else if (st.ReadIntervalTimeout == MAXDWORD)
5148 timeouts->interval = timeouts->total = 0;
5150 else /* write */
5152 if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant)
5154 timeouts->total = st.WriteTotalTimeoutConstant;
5155 if (st.WriteTotalTimeoutMultiplier != MAXDWORD)
5156 timeouts->total += count * st.WriteTotalTimeoutMultiplier;
5159 break;
5161 case FD_TYPE_MAILSLOT:
5162 if (is_read)
5164 timeouts->interval = 0; /* return as soon as we got something */
5165 SERVER_START_REQ( set_mailslot_info )
5167 req->handle = wine_server_obj_handle( handle );
5168 req->flags = 0;
5169 if (!(status = wine_server_call( req )) &&
5170 reply->read_timeout != TIMEOUT_INFINITE)
5171 timeouts->total = reply->read_timeout / -10000;
5173 SERVER_END_REQ;
5175 break;
5176 case FD_TYPE_SOCKET:
5177 case FD_TYPE_CHAR:
5178 if (is_read) timeouts->interval = 0; /* return as soon as we got something */
5179 break;
5180 default:
5181 break;
5183 if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total;
5184 return STATUS_SUCCESS;
5188 /* retrieve the timeout for the next wait, in milliseconds */
5189 static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already )
5191 int ret = -1;
5193 if (timeouts->total != -1)
5195 ret = timeouts->end_time - NtGetTickCount();
5196 if (ret < 0) ret = 0;
5198 if (already && timeouts->interval != -1)
5200 if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval;
5202 return ret;
5206 /* retrieve the avail_mode flag for async reads */
5207 static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode )
5209 NTSTATUS status = STATUS_SUCCESS;
5211 switch(type)
5213 case FD_TYPE_SERIAL:
5215 /* GetCommTimeouts */
5216 SERIAL_TIMEOUTS st;
5217 IO_STATUS_BLOCK io = {0};
5219 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
5220 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
5221 if (status) break;
5222 *avail_mode = (!st.ReadTotalTimeoutMultiplier &&
5223 !st.ReadTotalTimeoutConstant &&
5224 st.ReadIntervalTimeout == MAXDWORD);
5225 break;
5227 case FD_TYPE_MAILSLOT:
5228 case FD_TYPE_SOCKET:
5229 case FD_TYPE_CHAR:
5230 *avail_mode = TRUE;
5231 break;
5232 default:
5233 *avail_mode = FALSE;
5234 break;
5236 return status;
5239 /* register an async I/O for a file read; helper for NtReadFile */
5240 static NTSTATUS register_async_file_read( HANDLE handle, HANDLE event,
5241 PIO_APC_ROUTINE apc, void *apc_user,
5242 client_ptr_t iosb, void *buffer,
5243 ULONG already, ULONG length, BOOL avail_mode )
5245 struct async_fileio_read *fileio;
5246 NTSTATUS status;
5248 if (!(fileio = (struct async_fileio_read *)alloc_fileio( sizeof(*fileio), async_read_proc, handle )))
5249 return STATUS_NO_MEMORY;
5251 fileio->already = already;
5252 fileio->count = length;
5253 fileio->buffer = buffer;
5254 fileio->avail_mode = avail_mode;
5256 SERVER_START_REQ( register_async )
5258 req->type = ASYNC_TYPE_READ;
5259 req->count = length;
5260 req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb );
5261 status = wine_server_call( req );
5263 SERVER_END_REQ;
5265 if (status != STATUS_PENDING) free( fileio );
5266 return status;
5269 void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async )
5271 SERVER_START_REQ( add_fd_completion )
5273 req->handle = wine_server_obj_handle( handle );
5274 req->cvalue = value;
5275 req->status = status;
5276 req->information = info;
5277 req->async = async;
5278 wine_server_call( req );
5280 SERVER_END_REQ;
5283 static NTSTATUS set_pending_write( HANDLE device )
5285 NTSTATUS status;
5287 SERVER_START_REQ( set_serial_info )
5289 req->handle = wine_server_obj_handle( device );
5290 req->flags = SERIALINFO_PENDING_WRITE;
5291 status = wine_server_call( req );
5293 SERVER_END_REQ;
5294 return status;
5298 /******************************************************************************
5299 * NtReadFile (NTDLL.@)
5301 NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5302 IO_STATUS_BLOCK *io, void *buffer, ULONG length,
5303 LARGE_INTEGER *offset, ULONG *key )
5305 int result, unix_handle, needs_close;
5306 unsigned int options;
5307 struct io_timeouts timeouts;
5308 NTSTATUS status, ret_status;
5309 ULONG total = 0;
5310 client_ptr_t iosb_ptr = iosb_client_ptr(io);
5311 enum server_fd_type type;
5312 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5313 BOOL send_completion = FALSE, async_read, timeout_init_done = FALSE;
5315 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5316 handle, event, apc, apc_user, io, buffer, length, offset, key );
5318 if (!io) return STATUS_ACCESS_VIOLATION;
5320 status = server_get_unix_fd( handle, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5321 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5323 if (!virtual_check_buffer_for_write( buffer, length )) return STATUS_ACCESS_VIOLATION;
5325 if (status == STATUS_BAD_DEVICE_TYPE)
5326 return server_read_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5328 async_read = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5330 if (type == FD_TYPE_FILE)
5332 if (async_read && (!offset || offset->QuadPart < 0))
5334 status = STATUS_INVALID_PARAMETER;
5335 goto done;
5338 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5340 /* async I/O doesn't make sense on regular files */
5341 while ((result = virtual_locked_pread( unix_handle, buffer, length, offset->QuadPart )) == -1)
5343 if (errno != EINTR)
5345 status = errno_to_status( errno );
5346 goto done;
5349 if (!async_read) /* update file pointer position */
5350 lseek( unix_handle, offset->QuadPart + result, SEEK_SET );
5352 total = result;
5353 status = (total || !length) ? STATUS_SUCCESS : STATUS_END_OF_FILE;
5354 goto done;
5357 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5359 if (async_read && (!offset || offset->QuadPart < 0))
5361 status = STATUS_INVALID_PARAMETER;
5362 goto done;
5365 else if (type == FD_TYPE_SOCKET)
5367 status = sock_read( handle, unix_handle, event, apc, apc_user, io, buffer, length );
5368 if (needs_close) close( unix_handle );
5369 return status;
5372 if (type == FD_TYPE_SERIAL && async_read && length)
5374 /* an asynchronous serial port read with a read interval timeout needs to
5375 skip the synchronous read to make sure that the server starts the read
5376 interval timer after the first read */
5377 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5378 if (timeouts.interval)
5380 status = register_async_file_read( handle, event, apc, apc_user, iosb_ptr,
5381 buffer, total, length, FALSE );
5382 goto err;
5386 for (;;)
5388 if ((result = virtual_locked_read( unix_handle, (char *)buffer + total, length - total )) >= 0)
5390 total += result;
5391 if (!result || total == length)
5393 if (total)
5395 status = STATUS_SUCCESS;
5396 goto done;
5398 switch (type)
5400 case FD_TYPE_FILE:
5401 case FD_TYPE_CHAR:
5402 case FD_TYPE_DEVICE:
5403 status = length ? STATUS_END_OF_FILE : STATUS_SUCCESS;
5404 goto done;
5405 case FD_TYPE_SERIAL:
5406 if (!length)
5408 status = STATUS_SUCCESS;
5409 goto done;
5411 break;
5412 default:
5413 status = STATUS_PIPE_BROKEN;
5414 goto err;
5417 else if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5419 else if (errno != EAGAIN)
5421 if (errno == EINTR) continue;
5422 if (!total) status = errno_to_status( errno );
5423 goto err;
5426 if (async_read)
5428 BOOL avail_mode;
5430 if ((status = get_io_avail_mode( handle, type, &avail_mode ))) goto err;
5431 if (total && avail_mode)
5433 status = STATUS_SUCCESS;
5434 goto done;
5436 status = register_async_file_read( handle, event, apc, apc_user, iosb_ptr,
5437 buffer, total, length, avail_mode );
5438 goto err;
5440 else /* synchronous read, wait for the fd to become ready */
5442 struct pollfd pfd;
5443 int ret, timeout;
5445 if (!timeout_init_done)
5447 timeout_init_done = TRUE;
5448 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5449 if (event) NtResetEvent( event, NULL );
5451 timeout = get_next_io_timeout( &timeouts, total );
5453 pfd.fd = unix_handle;
5454 pfd.events = POLLIN;
5456 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5458 if (total) /* return with what we got so far */
5459 status = STATUS_SUCCESS;
5460 else
5461 status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT;
5462 goto done;
5464 if (ret == -1 && errno != EINTR)
5466 status = errno_to_status( errno );
5467 goto done;
5469 /* will now restart the read */
5473 done:
5474 send_completion = cvalue != 0;
5476 err:
5477 if (needs_close) close( unix_handle );
5478 if (status == STATUS_SUCCESS || (status == STATUS_END_OF_FILE && (!async_read || type == FD_TYPE_FILE)))
5480 io->u.Status = status;
5481 io->Information = total;
5482 TRACE("= SUCCESS (%u)\n", total);
5483 if (event) NtSetEvent( event, NULL );
5484 if (apc && (!status || async_read)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5485 (ULONG_PTR)apc_user, iosb_ptr, 0 );
5487 else
5489 TRACE("= 0x%08x\n", status);
5490 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5493 ret_status = async_read && type == FD_TYPE_FILE && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE)
5494 ? STATUS_PENDING : status;
5496 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5497 return ret_status;
5501 /******************************************************************************
5502 * NtReadFileScatter (NTDLL.@)
5504 NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5505 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5506 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5508 int result, unix_handle, needs_close;
5509 unsigned int options;
5510 NTSTATUS status;
5511 ULONG pos = 0, total = 0;
5512 client_ptr_t iosb_ptr = iosb_client_ptr(io);
5513 enum server_fd_type type;
5514 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5515 BOOL send_completion = FALSE;
5517 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5518 file, event, apc, apc_user, io, segments, length, offset, key );
5520 if (!io) return STATUS_ACCESS_VIOLATION;
5522 status = server_get_unix_fd( file, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5523 if (status) return status;
5525 if ((type != FD_TYPE_FILE) ||
5526 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5527 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5529 status = STATUS_INVALID_PARAMETER;
5530 goto error;
5533 while (length)
5535 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5536 result = pread( unix_handle, (char *)segments->Buffer + pos,
5537 min( length - pos, page_size - pos ), offset->QuadPart + total );
5538 else
5539 result = read( unix_handle, (char *)segments->Buffer + pos, min( length - pos, page_size - pos ) );
5541 if (result == -1)
5543 if (errno == EINTR) continue;
5544 status = errno_to_status( errno );
5545 break;
5547 if (!result) break;
5548 total += result;
5549 length -= result;
5550 if ((pos += result) == page_size)
5552 pos = 0;
5553 segments++;
5557 if (total == 0) status = STATUS_END_OF_FILE;
5559 send_completion = cvalue != 0;
5561 if (needs_close) close( unix_handle );
5562 io->u.Status = status;
5563 io->Information = total;
5564 TRACE("= 0x%08x (%u)\n", status, total);
5565 if (event) NtSetEvent( event, NULL );
5566 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, (ULONG_PTR)apc_user, iosb_ptr, 0 );
5567 if (send_completion) add_completion( file, cvalue, status, total, TRUE );
5569 return STATUS_PENDING;
5571 error:
5572 if (needs_close) close( unix_handle );
5573 if (event) NtResetEvent( event, NULL );
5574 TRACE("= 0x%08x\n", status);
5575 return status;
5579 /******************************************************************************
5580 * NtWriteFile (NTDLL.@)
5582 NTSTATUS WINAPI NtWriteFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5583 IO_STATUS_BLOCK *io, const void *buffer, ULONG length,
5584 LARGE_INTEGER *offset, ULONG *key )
5586 int result, unix_handle, needs_close;
5587 unsigned int options;
5588 struct io_timeouts timeouts;
5589 NTSTATUS status, ret_status;
5590 ULONG total = 0;
5591 client_ptr_t iosb_ptr = iosb_client_ptr(io);
5592 enum server_fd_type type;
5593 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5594 BOOL send_completion = FALSE, async_write, append_write = FALSE, timeout_init_done = FALSE;
5595 LARGE_INTEGER offset_eof;
5597 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5598 handle, event, apc, apc_user, io, buffer, length, offset, key );
5600 if (!io) return STATUS_ACCESS_VIOLATION;
5602 status = server_get_unix_fd( handle, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5603 if (status == STATUS_ACCESS_DENIED)
5605 status = server_get_unix_fd( handle, FILE_APPEND_DATA, &unix_handle,
5606 &needs_close, &type, &options );
5607 append_write = TRUE;
5609 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5611 async_write = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5613 if (!virtual_check_buffer_for_read( buffer, length ))
5615 status = STATUS_INVALID_USER_BUFFER;
5616 goto done;
5619 if (status == STATUS_BAD_DEVICE_TYPE)
5620 return server_write_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5622 if (type == FD_TYPE_FILE)
5624 if (async_write &&
5625 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5627 status = STATUS_INVALID_PARAMETER;
5628 goto done;
5631 if (append_write)
5633 offset_eof.QuadPart = FILE_WRITE_TO_END_OF_FILE;
5634 offset = &offset_eof;
5637 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5639 off_t off = offset->QuadPart;
5641 if (offset->QuadPart == FILE_WRITE_TO_END_OF_FILE)
5643 struct stat st;
5645 if (fstat( unix_handle, &st ) == -1)
5647 status = errno_to_status( errno );
5648 goto done;
5650 off = st.st_size;
5652 else if (offset->QuadPart < 0)
5654 status = STATUS_INVALID_PARAMETER;
5655 goto done;
5658 /* async I/O doesn't make sense on regular files */
5659 while ((result = pwrite( unix_handle, buffer, length, off )) == -1)
5661 if (errno != EINTR)
5663 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5664 else status = errno_to_status( errno );
5665 goto done;
5669 if (!async_write) /* update file pointer position */
5670 lseek( unix_handle, off + result, SEEK_SET );
5672 total = result;
5673 status = STATUS_SUCCESS;
5674 goto done;
5677 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5679 if (async_write &&
5680 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5682 status = STATUS_INVALID_PARAMETER;
5683 goto done;
5686 else if (type == FD_TYPE_SOCKET)
5688 status = sock_write( handle, unix_handle, event, apc, apc_user, io, buffer, length );
5689 if (needs_close) close( unix_handle );
5690 return status;
5693 for (;;)
5695 /* zero-length writes on sockets may not work with plain write(2) */
5696 if (!length && type == FD_TYPE_MAILSLOT)
5697 result = send( unix_handle, buffer, 0, 0 );
5698 else
5699 result = write( unix_handle, (const char *)buffer + total, length - total );
5701 if (result >= 0)
5703 total += result;
5704 if (total == length)
5706 status = STATUS_SUCCESS;
5707 goto done;
5709 if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5711 else if (errno != EAGAIN)
5713 if (errno == EINTR) continue;
5714 if (!total)
5716 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5717 else status = errno_to_status( errno );
5719 goto err;
5722 if (async_write)
5724 struct async_fileio_write *fileio;
5726 fileio = (struct async_fileio_write *)alloc_fileio( sizeof(*fileio), async_write_proc, handle );
5727 if (!fileio)
5729 status = STATUS_NO_MEMORY;
5730 goto err;
5732 fileio->already = total;
5733 fileio->count = length;
5734 fileio->buffer = buffer;
5736 SERVER_START_REQ( register_async )
5738 req->type = ASYNC_TYPE_WRITE;
5739 req->count = length;
5740 req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb_ptr );
5741 status = wine_server_call( req );
5743 SERVER_END_REQ;
5745 if (status != STATUS_PENDING) free( fileio );
5746 goto err;
5748 else /* synchronous write, wait for the fd to become ready */
5750 struct pollfd pfd;
5751 int ret, timeout;
5753 if (!timeout_init_done)
5755 timeout_init_done = TRUE;
5756 if ((status = get_io_timeouts( handle, type, length, FALSE, &timeouts )))
5757 goto err;
5758 if (event) NtResetEvent( event, NULL );
5760 timeout = get_next_io_timeout( &timeouts, total );
5762 pfd.fd = unix_handle;
5763 pfd.events = POLLOUT;
5765 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5767 /* return with what we got so far */
5768 status = total ? STATUS_SUCCESS : STATUS_TIMEOUT;
5769 goto done;
5771 if (ret == -1 && errno != EINTR)
5773 status = errno_to_status( errno );
5774 goto done;
5776 /* will now restart the write */
5780 done:
5781 send_completion = cvalue != 0;
5783 err:
5784 if (needs_close) close( unix_handle );
5786 if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING))
5787 set_pending_write( handle );
5789 if (status == STATUS_SUCCESS)
5791 io->u.Status = status;
5792 io->Information = total;
5793 TRACE("= SUCCESS (%u)\n", total);
5794 if (event) NtSetEvent( event, NULL );
5795 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, (ULONG_PTR)apc_user, iosb_ptr, 0 );
5797 else
5799 TRACE("= 0x%08x\n", status);
5800 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5803 ret_status = async_write && type == FD_TYPE_FILE && status == STATUS_SUCCESS ? STATUS_PENDING : status;
5804 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5805 return ret_status;
5809 /******************************************************************************
5810 * NtWriteFileGather (NTDLL.@)
5812 NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5813 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5814 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5816 int result, unix_handle, needs_close;
5817 unsigned int options;
5818 NTSTATUS status;
5819 ULONG pos = 0, total = 0;
5820 client_ptr_t iosb_ptr = iosb_client_ptr(io);
5821 enum server_fd_type type;
5822 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5823 BOOL send_completion = FALSE;
5825 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5826 file, event, apc, apc_user, io, segments, length, offset, key );
5828 if (length % page_size) return STATUS_INVALID_PARAMETER;
5829 if (!io) return STATUS_ACCESS_VIOLATION;
5831 status = server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5832 if (status) return status;
5834 if ((type != FD_TYPE_FILE) ||
5835 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5836 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5838 status = STATUS_INVALID_PARAMETER;
5839 goto done;
5842 while (length)
5844 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5845 result = pwrite( unix_handle, (char *)segments->Buffer + pos,
5846 page_size - pos, offset->QuadPart + total );
5847 else
5848 result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos );
5850 if (result == -1)
5852 if (errno == EINTR) continue;
5853 if (errno == EFAULT)
5855 status = STATUS_INVALID_USER_BUFFER;
5856 goto done;
5858 status = errno_to_status( errno );
5859 break;
5861 if (!result)
5863 status = STATUS_DISK_FULL;
5864 break;
5866 total += result;
5867 length -= result;
5868 if ((pos += result) == page_size)
5870 pos = 0;
5871 segments++;
5875 send_completion = cvalue != 0;
5877 done:
5878 if (needs_close) close( unix_handle );
5879 if (status == STATUS_SUCCESS)
5881 io->u.Status = status;
5882 io->Information = total;
5883 TRACE("= SUCCESS (%u)\n", total);
5884 if (event) NtSetEvent( event, NULL );
5885 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, (ULONG_PTR)apc_user, iosb_ptr, 0 );
5887 else
5889 TRACE("= 0x%08x\n", status);
5890 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5892 if (send_completion) add_completion( file, cvalue, status, total, FALSE );
5893 return status;
5897 /******************************************************************************
5898 * NtDeviceIoControlFile (NTDLL.@)
5900 NTSTATUS WINAPI NtDeviceIoControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5901 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5902 void *out_buffer, ULONG out_size )
5904 ULONG device = (code >> 16);
5905 NTSTATUS status = STATUS_NOT_SUPPORTED;
5907 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5908 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5910 /* some broken applications call this frequently with INVALID_HANDLE_VALUE,
5911 * and run slowly if we make a server call every time */
5912 if (HandleToLong( handle ) == ~0)
5913 return STATUS_INVALID_HANDLE;
5915 switch (device)
5917 case FILE_DEVICE_BEEP:
5918 case FILE_DEVICE_NETWORK:
5919 status = sock_ioctl( handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5920 if (status != STATUS_NOT_SUPPORTED && status != STATUS_BAD_DEVICE_TYPE) return status;
5921 break;
5922 case FILE_DEVICE_DISK:
5923 case FILE_DEVICE_CD_ROM:
5924 case FILE_DEVICE_DVD:
5925 case FILE_DEVICE_CONTROLLER:
5926 case FILE_DEVICE_MASS_STORAGE:
5927 status = cdrom_DeviceIoControl( handle, event, apc, apc_context, io, code,
5928 in_buffer, in_size, out_buffer, out_size );
5929 break;
5930 case FILE_DEVICE_SERIAL_PORT:
5931 status = serial_DeviceIoControl( handle, event, apc, apc_context, io, code,
5932 in_buffer, in_size, out_buffer, out_size );
5933 break;
5934 case FILE_DEVICE_TAPE:
5935 status = tape_DeviceIoControl( handle, event, apc, apc_context, io, code,
5936 in_buffer, in_size, out_buffer, out_size );
5937 break;
5940 if (status == STATUS_NOT_SUPPORTED || status == STATUS_BAD_DEVICE_TYPE)
5941 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5942 in_buffer, in_size, out_buffer, out_size );
5944 if (status != STATUS_PENDING && !NT_ERROR(status)) io->u.Status = status;
5945 return status;
5949 /* Tell Valgrind to ignore any holes in structs we will be passing to the
5950 * server */
5951 static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer, ULONG in_size )
5953 #ifdef VALGRIND_MAKE_MEM_DEFINED
5954 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
5955 do { \
5956 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
5957 if ((size) >= FIELD_OFFSET(t, f2)) \
5958 VALGRIND_MAKE_MEM_DEFINED( \
5959 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
5960 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
5961 } while (0)
5963 switch (code)
5965 case FSCTL_PIPE_WAIT:
5966 IGNORE_STRUCT_HOLE(in_buffer, in_size, FILE_PIPE_WAIT_FOR_BUFFER, TimeoutSpecified, Name);
5967 break;
5969 #endif
5973 /******************************************************************************
5974 * NtFsControlFile (NTDLL.@)
5976 NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5977 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5978 void *out_buffer, ULONG out_size )
5980 NTSTATUS status;
5982 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5983 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5985 if (!io) return STATUS_INVALID_PARAMETER;
5987 ignore_server_ioctl_struct_holes( code, in_buffer, in_size );
5989 switch (code)
5991 case FSCTL_DISMOUNT_VOLUME:
5992 status = server_ioctl_file( handle, event, apc, apc_context, io, code,
5993 in_buffer, in_size, out_buffer, out_size );
5994 if (!status) status = unmount_device( handle );
5995 return status;
5997 case FSCTL_PIPE_IMPERSONATE:
5998 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
5999 return server_ioctl_file( handle, event, apc, apc_context, io, code,
6000 in_buffer, in_size, out_buffer, out_size );
6002 case FSCTL_IS_VOLUME_MOUNTED:
6003 case FSCTL_LOCK_VOLUME:
6004 case FSCTL_UNLOCK_VOLUME:
6005 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
6006 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
6007 status = STATUS_SUCCESS;
6008 break;
6010 case FSCTL_GET_RETRIEVAL_POINTERS:
6012 RETRIEVAL_POINTERS_BUFFER *buffer = (RETRIEVAL_POINTERS_BUFFER *)out_buffer;
6014 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
6016 if (out_size >= sizeof(RETRIEVAL_POINTERS_BUFFER))
6018 buffer->ExtentCount = 1;
6019 buffer->StartingVcn.QuadPart = 1;
6020 buffer->Extents[0].NextVcn.QuadPart = 0;
6021 buffer->Extents[0].Lcn.QuadPart = 0;
6022 io->Information = sizeof(RETRIEVAL_POINTERS_BUFFER);
6023 status = STATUS_SUCCESS;
6025 else
6027 io->Information = 0;
6028 status = STATUS_BUFFER_TOO_SMALL;
6030 break;
6033 case FSCTL_GET_OBJECT_ID:
6035 FILE_OBJECTID_BUFFER *info = out_buffer;
6036 int fd, needs_close;
6037 struct stat st;
6039 io->Information = 0;
6040 if (out_size >= sizeof(*info))
6042 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
6043 if (status) break;
6044 fstat( fd, &st );
6045 if (needs_close) close( fd );
6046 memset( info, 0, sizeof(*info) );
6047 memcpy( info->ObjectId, &st.st_dev, sizeof(st.st_dev) );
6048 memcpy( info->ObjectId + 8, &st.st_ino, sizeof(st.st_ino) );
6049 io->Information = sizeof(*info);
6051 else status = STATUS_BUFFER_TOO_SMALL;
6052 break;
6055 case FSCTL_SET_SPARSE:
6056 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
6057 io->Information = 0;
6058 status = STATUS_SUCCESS;
6059 break;
6060 default:
6061 return server_ioctl_file( handle, event, apc, apc_context, io, code,
6062 in_buffer, in_size, out_buffer, out_size );
6065 if (status != STATUS_PENDING) io->u.Status = status;
6066 return status;
6070 /******************************************************************************
6071 * NtFlushBuffersFile (NTDLL.@)
6073 NTSTATUS WINAPI NtFlushBuffersFile( HANDLE handle, IO_STATUS_BLOCK *io )
6075 NTSTATUS ret;
6076 HANDLE wait_handle;
6077 enum server_fd_type type;
6078 int fd, needs_close;
6080 if (!io || !virtual_check_buffer_for_write( io, sizeof(*io) )) return STATUS_ACCESS_VIOLATION;
6082 ret = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, &type, NULL );
6083 if (ret == STATUS_ACCESS_DENIED)
6084 ret = server_get_unix_fd( handle, FILE_APPEND_DATA, &fd, &needs_close, &type, NULL );
6086 if (!ret && (type == FD_TYPE_FILE || type == FD_TYPE_DIR || type == FD_TYPE_CHAR))
6088 if (fsync(fd)) ret = errno_to_status( errno );
6089 io->u.Status = ret;
6090 io->Information = 0;
6092 else if (!ret && type == FD_TYPE_SERIAL)
6094 ret = serial_FlushBuffersFile( fd );
6096 else if (ret != STATUS_ACCESS_DENIED)
6098 struct async_irp *async;
6100 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
6101 return STATUS_NO_MEMORY;
6102 async->buffer = NULL;
6103 async->size = 0;
6105 SERVER_START_REQ( flush )
6107 req->async = server_async( handle, &async->io, NULL, NULL, NULL, iosb_client_ptr(io) );
6108 ret = wine_server_call( req );
6109 wait_handle = wine_server_ptr_handle( reply->event );
6110 if (wait_handle && ret != STATUS_PENDING)
6112 io->u.Status = ret;
6113 io->Information = 0;
6116 SERVER_END_REQ;
6118 if (ret != STATUS_PENDING) free( async );
6120 if (wait_handle) ret = wait_async( wait_handle, FALSE );
6123 if (needs_close) close( fd );
6124 return ret;
6128 /**************************************************************************
6129 * NtCancelIoFile (NTDLL.@)
6131 NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status )
6133 NTSTATUS status;
6135 TRACE( "%p %p\n", handle, io_status );
6137 SERVER_START_REQ( cancel_async )
6139 req->handle = wine_server_obj_handle( handle );
6140 req->only_thread = TRUE;
6141 if (!(status = wine_server_call( req )))
6143 io_status->u.Status = status;
6144 io_status->Information = 0;
6147 SERVER_END_REQ;
6149 return status;
6153 /**************************************************************************
6154 * NtCancelIoFileEx (NTDLL.@)
6156 NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status )
6158 NTSTATUS status;
6160 TRACE( "%p %p %p\n", handle, io, io_status );
6162 SERVER_START_REQ( cancel_async )
6164 req->handle = wine_server_obj_handle( handle );
6165 req->iosb = wine_server_client_ptr( io );
6166 if (!(status = wine_server_call( req )))
6168 io_status->u.Status = status;
6169 io_status->Information = 0;
6172 SERVER_END_REQ;
6174 return status;
6178 /******************************************************************
6179 * NtLockFile (NTDLL.@)
6181 NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void* apc_user,
6182 IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
6183 LARGE_INTEGER *count, ULONG *key, BOOLEAN dont_wait, BOOLEAN exclusive )
6185 static int warn;
6186 NTSTATUS ret;
6187 HANDLE handle;
6188 BOOLEAN async;
6190 if (apc || io_status || key)
6192 FIXME("Unimplemented yet parameter\n");
6193 return STATUS_NOT_IMPLEMENTED;
6195 if (apc_user && !warn++) FIXME("I/O completion on lock not implemented yet\n");
6197 for (;;)
6199 SERVER_START_REQ( lock_file )
6201 req->handle = wine_server_obj_handle( file );
6202 req->offset = offset->QuadPart;
6203 req->count = count->QuadPart;
6204 req->shared = !exclusive;
6205 req->wait = !dont_wait;
6206 ret = wine_server_call( req );
6207 handle = wine_server_ptr_handle( reply->handle );
6208 async = reply->overlapped;
6210 SERVER_END_REQ;
6211 if (ret != STATUS_PENDING)
6213 if (!ret && event) NtSetEvent( event, NULL );
6214 return ret;
6216 if (async)
6218 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6219 if (handle) NtClose( handle );
6220 return STATUS_PENDING;
6222 if (handle)
6224 NtWaitForSingleObject( handle, FALSE, NULL );
6225 NtClose( handle );
6227 else /* Unix lock conflict, sleep a bit and retry */
6229 LARGE_INTEGER time;
6230 time.QuadPart = -100 * (ULONGLONG)10000;
6231 NtDelayExecution( FALSE, &time );
6237 /******************************************************************
6238 * NtUnlockFile (NTDLL.@)
6240 NTSTATUS WINAPI NtUnlockFile( HANDLE handle, IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
6241 LARGE_INTEGER *count, ULONG *key )
6243 NTSTATUS status;
6245 TRACE( "%p %x%08x %x%08x\n",
6246 handle, offset->u.HighPart, offset->u.LowPart, count->u.HighPart, count->u.LowPart );
6248 if (io_status || key)
6250 FIXME("Unimplemented yet parameter\n");
6251 return STATUS_NOT_IMPLEMENTED;
6254 SERVER_START_REQ( unlock_file )
6256 req->handle = wine_server_obj_handle( handle );
6257 req->offset = offset->QuadPart;
6258 req->count = count->QuadPart;
6259 status = wine_server_call( req );
6261 SERVER_END_REQ;
6262 return status;
6266 static BOOL read_changes_apc( void *user, ULONG_PTR *info, NTSTATUS *status )
6268 struct async_fileio_read_changes *fileio = user;
6269 int size = 0;
6271 if (*status == STATUS_ALERTED)
6273 SERVER_START_REQ( read_change )
6275 req->handle = wine_server_obj_handle( fileio->io.handle );
6276 wine_server_set_reply( req, fileio->data, fileio->data_size );
6277 *status = wine_server_call( req );
6278 size = wine_server_reply_size( reply );
6280 SERVER_END_REQ;
6282 if (*status == STATUS_SUCCESS && fileio->buffer)
6284 FILE_NOTIFY_INFORMATION *pfni = fileio->buffer;
6285 int i, left = fileio->buffer_size;
6286 DWORD *last_entry_offset = NULL;
6287 struct filesystem_event *event = (struct filesystem_event*)fileio->data;
6289 while (size && left >= sizeof(*pfni))
6291 DWORD len = (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR);
6293 /* convert to an NT style path */
6294 for (i = 0; i < event->len; i++)
6295 if (event->name[i] == '/') event->name[i] = '\\';
6297 pfni->Action = event->action;
6298 pfni->FileNameLength = ntdll_umbstowcs( event->name, event->len, pfni->FileName, len );
6299 last_entry_offset = &pfni->NextEntryOffset;
6301 if (pfni->FileNameLength == len) break;
6303 i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
6304 pfni->FileNameLength *= sizeof(WCHAR);
6305 pfni->NextEntryOffset = i;
6306 pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
6307 left -= i;
6309 i = (offsetof(struct filesystem_event, name[event->len])
6310 + sizeof(int)-1) / sizeof(int) * sizeof(int);
6311 event = (struct filesystem_event*)((char*)event + i);
6312 size -= i;
6315 if (size)
6317 *status = STATUS_NOTIFY_ENUM_DIR;
6318 size = 0;
6320 else
6322 if (last_entry_offset) *last_entry_offset = 0;
6323 size = fileio->buffer_size - left;
6326 else
6328 *status = STATUS_NOTIFY_ENUM_DIR;
6329 size = 0;
6333 *info = size;
6334 release_fileio( &fileio->io );
6335 return TRUE;
6338 #define FILE_NOTIFY_ALL ( \
6339 FILE_NOTIFY_CHANGE_FILE_NAME | \
6340 FILE_NOTIFY_CHANGE_DIR_NAME | \
6341 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
6342 FILE_NOTIFY_CHANGE_SIZE | \
6343 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6344 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6345 FILE_NOTIFY_CHANGE_CREATION | \
6346 FILE_NOTIFY_CHANGE_SECURITY )
6348 /******************************************************************************
6349 * NtNotifyChangeDirectoryFile (NTDLL.@)
6351 NTSTATUS WINAPI NtNotifyChangeDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
6352 void *apc_context, IO_STATUS_BLOCK *iosb, void *buffer,
6353 ULONG buffer_size, ULONG filter, BOOLEAN subtree )
6355 struct async_fileio_read_changes *fileio;
6356 NTSTATUS status;
6357 ULONG size = max( 4096, buffer_size );
6359 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6360 handle, event, apc, apc_context, iosb, buffer, buffer_size, filter, subtree );
6362 if (!iosb) return STATUS_ACCESS_VIOLATION;
6363 if (filter == 0 || (filter & ~FILE_NOTIFY_ALL)) return STATUS_INVALID_PARAMETER;
6365 fileio = (struct async_fileio_read_changes *)alloc_fileio(
6366 offsetof(struct async_fileio_read_changes, data[size]), read_changes_apc, handle );
6367 if (!fileio) return STATUS_NO_MEMORY;
6369 fileio->buffer = buffer;
6370 fileio->buffer_size = buffer_size;
6371 fileio->data_size = size;
6373 SERVER_START_REQ( read_directory_changes )
6375 req->filter = filter;
6376 req->want_data = (buffer != NULL);
6377 req->subtree = subtree;
6378 req->async = server_async( handle, &fileio->io, event, apc, apc_context, iosb_client_ptr(iosb) );
6379 status = wine_server_call( req );
6381 SERVER_END_REQ;
6383 if (status != STATUS_PENDING) free( fileio );
6384 return status;
6388 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6389 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6390 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION *info, const char *fstypename,
6391 unsigned int flags )
6393 if (!strcmp("cd9660", fstypename) || !strcmp("udf", fstypename))
6395 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6396 /* Don't assume read-only, let the mount options set it below */
6397 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6399 else if (!strcmp("nfs", fstypename) || !strcmp("nwfs", fstypename) ||
6400 !strcmp("smbfs", fstypename) || !strcmp("afpfs", fstypename))
6402 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6403 info->Characteristics |= FILE_REMOTE_DEVICE;
6405 else if (!strcmp("procfs", fstypename))
6406 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6407 else
6408 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6410 if (flags & MNT_RDONLY)
6411 info->Characteristics |= FILE_READ_ONLY_DEVICE;
6413 if (!(flags & MNT_LOCAL))
6415 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6416 info->Characteristics |= FILE_REMOTE_DEVICE;
6419 #endif
6421 static inline BOOL is_device_placeholder( int fd )
6423 static const char wine_placeholder[] = "Wine device placeholder";
6424 char buffer[sizeof(wine_placeholder)-1];
6426 if (pread( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1)
6427 return FALSE;
6428 return !memcmp( buffer, wine_placeholder, sizeof(wine_placeholder) - 1 );
6431 NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info )
6433 struct stat st;
6435 info->Characteristics = 0;
6436 if (fstat( fd, &st ) < 0) return errno_to_status( errno );
6437 if (S_ISCHR( st.st_mode ))
6439 info->DeviceType = FILE_DEVICE_UNKNOWN;
6440 #ifdef linux
6441 switch(major(st.st_rdev))
6443 case MEM_MAJOR:
6444 info->DeviceType = FILE_DEVICE_NULL;
6445 break;
6446 case TTY_MAJOR:
6447 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6448 break;
6449 case LP_MAJOR:
6450 info->DeviceType = FILE_DEVICE_PARALLEL_PORT;
6451 break;
6452 case SCSI_TAPE_MAJOR:
6453 info->DeviceType = FILE_DEVICE_TAPE;
6454 break;
6456 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6458 int d_type;
6459 if (ioctl(fd, FIODTYPE, &d_type) == 0)
6461 switch(d_type)
6463 case D_TAPE:
6464 info->DeviceType = FILE_DEVICE_TAPE;
6465 break;
6466 case D_DISK:
6467 info->DeviceType = FILE_DEVICE_DISK;
6468 break;
6469 case D_TTY:
6470 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6471 break;
6472 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6473 case D_MEM:
6474 info->DeviceType = FILE_DEVICE_NULL;
6475 break;
6476 #endif
6478 /* no special d_type for parallel ports */
6481 #endif
6483 else if (S_ISBLK( st.st_mode ))
6485 info->DeviceType = FILE_DEVICE_DISK;
6487 else if (S_ISFIFO( st.st_mode ) || S_ISSOCK( st.st_mode ))
6489 info->DeviceType = FILE_DEVICE_NAMED_PIPE;
6491 else if (is_device_placeholder( fd ))
6493 info->DeviceType = FILE_DEVICE_DISK;
6495 else /* regular file or directory */
6497 #if defined(linux) && defined(HAVE_FSTATFS)
6498 struct statfs stfs;
6500 /* check for floppy disk */
6501 if (major(st.st_dev) == FLOPPY_MAJOR)
6502 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6504 if (fstatfs( fd, &stfs ) < 0) stfs.f_type = 0;
6505 switch (stfs.f_type)
6507 case 0x9660: /* iso9660 */
6508 case 0x9fa1: /* supermount */
6509 case 0x15013346: /* udf */
6510 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6511 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6512 break;
6513 case 0x6969: /* nfs */
6514 case 0xff534d42: /* cifs */
6515 case 0xfe534d42: /* smb2 */
6516 case 0x517b: /* smbfs */
6517 case 0x564c: /* ncpfs */
6518 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6519 info->Characteristics |= FILE_REMOTE_DEVICE;
6520 break;
6521 case 0x1373: /* devfs */
6522 case 0x9fa0: /* procfs */
6523 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6524 break;
6525 case 0x01021994: /* tmpfs */
6526 case 0x28cd3d45: /* cramfs */
6527 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6528 * filesystems are rare on Windows, and some programs refuse to
6529 * recognize them as valid. */
6530 default:
6531 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6532 break;
6534 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6535 struct statfs stfs;
6537 if (fstatfs( fd, &stfs ) < 0)
6538 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6539 else
6540 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flags );
6541 #elif defined(__NetBSD__)
6542 struct statvfs stfs;
6544 if (fstatvfs( fd, &stfs) < 0)
6545 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6546 else
6547 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flag );
6548 #elif defined(sun)
6549 /* Use dkio to work out device types */
6551 # include <sys/dkio.h>
6552 # include <sys/vtoc.h>
6553 struct dk_cinfo dkinf;
6554 int retval = ioctl(fd, DKIOCINFO, &dkinf);
6555 if(retval==-1){
6556 WARN("Unable to get disk device type information - assuming a disk like device\n");
6557 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6559 switch (dkinf.dki_ctype)
6561 case DKC_CDROM:
6562 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6563 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6564 break;
6565 case DKC_NCRFLOPPY:
6566 case DKC_SMSFLOPPY:
6567 case DKC_INTEL82072:
6568 case DKC_INTEL82077:
6569 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6570 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6571 break;
6572 case DKC_MD:
6573 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6574 * filesystems are rare on Windows, and some programs refuse to
6575 * recognize them as valid. */
6576 default:
6577 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6580 #else
6581 static int warned;
6582 if (!warned++) FIXME( "device info not properly supported on this platform\n" );
6583 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6584 #endif
6585 info->Characteristics |= FILE_DEVICE_IS_MOUNTED;
6587 return STATUS_SUCCESS;
6591 /******************************************************************************
6592 * NtQueryVolumeInformationFile (NTDLL.@)
6594 NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
6595 void *buffer, ULONG length,
6596 FS_INFORMATION_CLASS info_class )
6598 int fd, needs_close;
6599 NTSTATUS status;
6601 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
6602 if (status == STATUS_BAD_DEVICE_TYPE)
6604 struct async_irp *async;
6605 HANDLE wait_handle;
6607 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
6608 return STATUS_NO_MEMORY;
6609 async->buffer = buffer;
6610 async->size = length;
6612 SERVER_START_REQ( get_volume_info )
6614 req->async = server_async( handle, &async->io, NULL, NULL, NULL, iosb_client_ptr(io) );
6615 req->handle = wine_server_obj_handle( handle );
6616 req->info_class = info_class;
6617 wine_server_set_reply( req, buffer, length );
6618 status = wine_server_call( req );
6619 if (status != STATUS_PENDING)
6621 io->u.Status = status;
6622 io->Information = wine_server_reply_size( reply );
6624 wait_handle = wine_server_ptr_handle( reply->wait );
6626 SERVER_END_REQ;
6627 if (status != STATUS_PENDING) free( async );
6628 if (wait_handle) status = wait_async( wait_handle, FALSE );
6629 return status;
6631 else if (status) return io->u.Status = status;
6633 io->Information = 0;
6635 switch( info_class )
6637 case FileFsLabelInformation:
6638 FIXME( "%p: label info not supported\n", handle );
6639 status = STATUS_NOT_IMPLEMENTED;
6640 break;
6642 case FileFsSizeInformation:
6643 if (length < sizeof(FILE_FS_SIZE_INFORMATION))
6644 status = STATUS_BUFFER_TOO_SMALL;
6645 else
6647 FILE_FS_SIZE_INFORMATION *info = buffer;
6648 FILE_FS_FULL_SIZE_INFORMATION full_info;
6650 if ((status = get_full_size_info(fd, &full_info)) == STATUS_SUCCESS)
6652 info->TotalAllocationUnits = full_info.TotalAllocationUnits;
6653 info->AvailableAllocationUnits = full_info.CallerAvailableAllocationUnits;
6654 info->SectorsPerAllocationUnit = full_info.SectorsPerAllocationUnit;
6655 info->BytesPerSector = full_info.BytesPerSector;
6656 io->Information = sizeof(*info);
6659 break;
6661 case FileFsDeviceInformation:
6662 if (length < sizeof(FILE_FS_DEVICE_INFORMATION))
6663 status = STATUS_BUFFER_TOO_SMALL;
6664 else
6666 FILE_FS_DEVICE_INFORMATION *info = buffer;
6668 if ((status = get_device_info( fd, info )) == STATUS_SUCCESS)
6669 io->Information = sizeof(*info);
6671 break;
6673 case FileFsAttributeInformation:
6675 static const WCHAR fatW[] = {'F','A','T'};
6676 static const WCHAR fat32W[] = {'F','A','T','3','2'};
6677 static const WCHAR ntfsW[] = {'N','T','F','S'};
6678 static const WCHAR cdfsW[] = {'C','D','F','S'};
6679 static const WCHAR udfW[] = {'U','D','F'};
6681 FILE_FS_ATTRIBUTE_INFORMATION *info = buffer;
6682 struct mountmgr_unix_drive drive;
6683 enum mountmgr_fs_type fs_type = MOUNTMGR_FS_TYPE_NTFS;
6685 if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
6687 status = STATUS_INFO_LENGTH_MISMATCH;
6688 break;
6691 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) )) fs_type = drive.fs_type;
6692 else
6694 struct statfs stfs;
6696 if (!fstatfs( fd, &stfs ))
6698 #if defined(linux) && defined(HAVE_FSTATFS)
6699 switch (stfs.f_type)
6701 case 0x9660:
6702 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6703 break;
6704 case 0x15013346:
6705 fs_type = MOUNTMGR_FS_TYPE_UDF;
6706 break;
6707 case 0x4d44:
6708 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6709 break;
6711 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6712 if (!strcmp( stfs.f_fstypename, "cd9660" ))
6713 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6714 else if (!strcmp( stfs.f_fstypename, "udf" ))
6715 fs_type = MOUNTMGR_FS_TYPE_UDF;
6716 else if (!strcmp( stfs.f_fstypename, "msdos" )) /* FreeBSD < 5, Apple */
6717 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6718 else if (!strcmp( stfs.f_fstypename, "msdosfs" )) /* FreeBSD >= 5 */
6719 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6720 #endif
6724 switch (fs_type)
6726 case MOUNTMGR_FS_TYPE_ISO9660:
6727 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
6728 info->MaximumComponentNameLength = 221;
6729 info->FileSystemNameLength = min( sizeof(cdfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6730 memcpy(info->FileSystemName, cdfsW, info->FileSystemNameLength);
6731 break;
6732 case MOUNTMGR_FS_TYPE_UDF:
6733 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
6734 info->MaximumComponentNameLength = 255;
6735 info->FileSystemNameLength = min( sizeof(udfW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6736 memcpy(info->FileSystemName, udfW, info->FileSystemNameLength);
6737 break;
6738 case MOUNTMGR_FS_TYPE_FAT:
6739 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6740 info->MaximumComponentNameLength = 255;
6741 info->FileSystemNameLength = min( sizeof(fatW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6742 memcpy(info->FileSystemName, fatW, info->FileSystemNameLength);
6743 break;
6744 case MOUNTMGR_FS_TYPE_FAT32:
6745 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6746 info->MaximumComponentNameLength = 255;
6747 info->FileSystemNameLength = min( sizeof(fat32W), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6748 memcpy(info->FileSystemName, fat32W, info->FileSystemNameLength);
6749 break;
6750 default:
6751 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
6752 info->MaximumComponentNameLength = 255;
6753 info->FileSystemNameLength = min( sizeof(ntfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6754 memcpy(info->FileSystemName, ntfsW, info->FileSystemNameLength);
6755 break;
6758 io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
6759 status = STATUS_SUCCESS;
6760 break;
6763 case FileFsVolumeInformation:
6765 FILE_FS_VOLUME_INFORMATION *info = buffer;
6766 ULONGLONG data[64];
6767 struct mountmgr_unix_drive *drive = (struct mountmgr_unix_drive *)data;
6768 const WCHAR *label;
6770 if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
6772 status = STATUS_INFO_LENGTH_MISMATCH;
6773 break;
6776 if (get_mountmgr_fs_info( handle, fd, drive, sizeof(data) ))
6778 status = STATUS_NOT_IMPLEMENTED;
6779 break;
6782 label = (WCHAR *)((char *)drive + drive->label_offset);
6783 info->VolumeCreationTime.QuadPart = 0; /* FIXME */
6784 info->VolumeSerialNumber = drive->serial;
6785 info->VolumeLabelLength = min( wcslen( label ) * sizeof(WCHAR),
6786 length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
6787 info->SupportsObjects = (drive->fs_type == MOUNTMGR_FS_TYPE_NTFS);
6788 memcpy( info->VolumeLabel, label, info->VolumeLabelLength );
6789 io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
6790 status = STATUS_SUCCESS;
6791 break;
6794 case FileFsControlInformation:
6795 FIXME( "%p: control info not supported\n", handle );
6796 status = STATUS_NOT_IMPLEMENTED;
6797 break;
6799 case FileFsFullSizeInformation:
6800 if (length < sizeof(FILE_FS_FULL_SIZE_INFORMATION))
6801 status = STATUS_BUFFER_TOO_SMALL;
6802 else
6804 FILE_FS_FULL_SIZE_INFORMATION *info = buffer;
6805 if ((status = get_full_size_info(fd, info)) == STATUS_SUCCESS)
6806 io->Information = sizeof(*info);
6808 break;
6810 case FileFsObjectIdInformation:
6811 FIXME( "%p: object id info not supported\n", handle );
6812 status = STATUS_NOT_IMPLEMENTED;
6813 break;
6815 case FileFsMaximumInformation:
6816 FIXME( "%p: maximum info not supported\n", handle );
6817 status = STATUS_NOT_IMPLEMENTED;
6818 break;
6820 default:
6821 status = STATUS_INVALID_PARAMETER;
6822 break;
6824 if (needs_close) close( fd );
6825 return io->u.Status = status;
6829 /******************************************************************************
6830 * NtSetVolumeInformationFile (NTDLL.@)
6832 NTSTATUS WINAPI NtSetVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, void *info,
6833 ULONG length, FS_INFORMATION_CLASS class )
6835 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle, io, info, length, class );
6836 return STATUS_SUCCESS;
6840 /******************************************************************
6841 * NtQueryEaFile (NTDLL.@)
6843 NTSTATUS WINAPI NtQueryEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
6844 BOOLEAN single_entry, void *list, ULONG list_len,
6845 ULONG *index, BOOLEAN restart )
6847 int fd, needs_close;
6848 NTSTATUS status;
6850 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) semi-stub\n",
6851 handle, io, buffer, length, single_entry, list, list_len, index, restart );
6853 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
6854 return status;
6856 if (buffer && length)
6857 memset( buffer, 0, length );
6859 if (needs_close) close( fd );
6860 return STATUS_NO_EAS_ON_FILE;
6864 /******************************************************************
6865 * NtSetEaFile (NTDLL.@)
6867 NTSTATUS WINAPI NtSetEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length )
6869 FIXME( "(%p,%p,%p,%d) stub\n", handle, io, buffer, length );
6870 return STATUS_ACCESS_DENIED;
6874 /* convert type information from server format; helper for NtQueryObject */
6875 static void *put_object_type_info( OBJECT_TYPE_INFORMATION *p, struct object_type_info *info )
6877 const ULONG align = sizeof(DWORD_PTR) - 1;
6879 memset( p, 0, sizeof(*p) );
6880 p->TypeName.Buffer = (WCHAR *)(p + 1);
6881 p->TypeName.Length = info->name_len;
6882 p->TypeName.MaximumLength = info->name_len + sizeof(WCHAR);
6883 p->TotalNumberOfObjects = info->obj_count;
6884 p->TotalNumberOfHandles = info->handle_count;
6885 p->HighWaterNumberOfObjects = info->obj_max;
6886 p->HighWaterNumberOfHandles = info->handle_max;
6887 p->TypeIndex = info->index + 2;
6888 p->GenericMapping.GenericRead = info->mapping.read;
6889 p->GenericMapping.GenericWrite = info->mapping.write;
6890 p->GenericMapping.GenericExecute = info->mapping.exec;
6891 p->GenericMapping.GenericAll = info->mapping.all;
6892 p->ValidAccessMask = info->valid_access;
6893 memcpy( p->TypeName.Buffer, info + 1, info->name_len );
6894 p->TypeName.Buffer[info->name_len / sizeof(WCHAR)] = 0;
6895 return (char *)(p + 1) + ((p->TypeName.MaximumLength + align) & ~align);
6898 /**************************************************************************
6899 * NtQueryObject (NTDLL.@)
6901 NTSTATUS WINAPI NtQueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
6902 void *ptr, ULONG len, ULONG *used_len )
6904 NTSTATUS status;
6906 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle, info_class, ptr, len, used_len);
6908 if (used_len) *used_len = 0;
6910 switch (info_class)
6912 case ObjectBasicInformation:
6914 OBJECT_BASIC_INFORMATION *p = ptr;
6916 if (len < sizeof(*p)) return STATUS_INFO_LENGTH_MISMATCH;
6918 SERVER_START_REQ( get_object_info )
6920 req->handle = wine_server_obj_handle( handle );
6921 status = wine_server_call( req );
6922 if (status == STATUS_SUCCESS)
6924 memset( p, 0, sizeof(*p) );
6925 p->GrantedAccess = reply->access;
6926 p->PointerCount = reply->ref_count;
6927 p->HandleCount = reply->handle_count;
6928 if (used_len) *used_len = sizeof(*p);
6931 SERVER_END_REQ;
6932 break;
6935 case ObjectNameInformation:
6937 OBJECT_NAME_INFORMATION *p = ptr;
6938 char *unix_name;
6939 WCHAR *nt_name;
6941 /* first try as a file object */
6943 if (!(status = server_get_unix_name( handle, &unix_name )))
6945 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
6947 ULONG size = (wcslen(nt_name) + 1) * sizeof(WCHAR);
6948 if (len < sizeof(*p)) status = STATUS_INFO_LENGTH_MISMATCH;
6949 else if (len < sizeof(*p) + size) status = STATUS_BUFFER_OVERFLOW;
6950 else
6952 p->Name.Buffer = (WCHAR *)(p + 1);
6953 p->Name.Length = size - sizeof(WCHAR);
6954 p->Name.MaximumLength = size;
6955 wcscpy( p->Name.Buffer, nt_name );
6957 if (used_len) *used_len = sizeof(*p) + size;
6958 free( nt_name );
6960 free( unix_name );
6961 break;
6963 else if (status != STATUS_OBJECT_TYPE_MISMATCH) break;
6965 /* not a file, treat as a generic object */
6967 SERVER_START_REQ( get_object_name )
6969 req->handle = wine_server_obj_handle( handle );
6970 if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
6971 status = wine_server_call( req );
6972 if (status == STATUS_SUCCESS)
6974 if (!reply->total) /* no name */
6976 if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
6977 else memset( p, 0, sizeof(*p) );
6978 if (used_len) *used_len = sizeof(*p);
6980 else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
6982 if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
6983 status = STATUS_INFO_LENGTH_MISMATCH;
6985 else
6987 ULONG res = wine_server_reply_size( reply );
6988 p->Name.Buffer = (WCHAR *)(p + 1);
6989 p->Name.Length = res;
6990 p->Name.MaximumLength = res + sizeof(WCHAR);
6991 p->Name.Buffer[res / sizeof(WCHAR)] = 0;
6992 if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
6996 SERVER_END_REQ;
6997 break;
7000 case ObjectTypeInformation:
7002 OBJECT_TYPE_INFORMATION *p = ptr;
7003 char buffer[sizeof(struct object_type_info) + 64];
7004 struct object_type_info *info = (struct object_type_info *)buffer;
7006 SERVER_START_REQ( get_object_type )
7008 req->handle = wine_server_obj_handle( handle );
7009 wine_server_set_reply( req, buffer, sizeof(buffer) );
7010 status = wine_server_call( req );
7012 SERVER_END_REQ;
7013 if (status) break;
7014 if (sizeof(*p) + info->name_len + sizeof(WCHAR) <= len)
7016 put_object_type_info( p, info );
7017 if (used_len) *used_len = sizeof(*p) + p->TypeName.MaximumLength;
7019 else
7021 if (used_len) *used_len = sizeof(*p) + info->name_len + sizeof(WCHAR);
7022 status = STATUS_INFO_LENGTH_MISMATCH;
7024 break;
7027 case ObjectTypesInformation:
7029 OBJECT_TYPES_INFORMATION *types = ptr;
7030 OBJECT_TYPE_INFORMATION *p;
7031 struct object_type_info *buffer;
7032 /* assume at most 32 types, with an average 16-char name */
7033 ULONG size = 32 * (sizeof(struct object_type_info) + 16 * sizeof(WCHAR));
7034 ULONG i, count, pos, total, align = sizeof(DWORD_PTR) - 1;
7036 buffer = malloc( size );
7037 SERVER_START_REQ( get_object_types )
7039 wine_server_set_reply( req, buffer, size );
7040 status = wine_server_call( req );
7041 count = reply->count;
7043 SERVER_END_REQ;
7044 if (!status)
7046 if (len >= sizeof(*types)) types->NumberOfTypes = count;
7047 total = (sizeof(*types) + align) & ~align;
7048 p = (OBJECT_TYPE_INFORMATION *)((char *)ptr + total);
7049 for (i = pos = 0; i < count; i++)
7051 struct object_type_info *info = (struct object_type_info *)((char *)buffer + pos);
7052 pos += sizeof(*info) + ((info->name_len + 3) & ~3);
7053 total += sizeof(*p) + ((info->name_len + sizeof(WCHAR) + align) & ~align);
7054 if (total <= len) p = put_object_type_info( p, info );
7056 if (used_len) *used_len = total;
7057 if (total > len) status = STATUS_INFO_LENGTH_MISMATCH;
7059 else if (status == STATUS_BUFFER_OVERFLOW) FIXME( "size %u too small\n", size );
7061 free( buffer );
7062 break;
7065 case ObjectDataInformation:
7067 OBJECT_DATA_INFORMATION* p = ptr;
7069 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
7071 SERVER_START_REQ( set_handle_info )
7073 req->handle = wine_server_obj_handle( handle );
7074 req->flags = 0;
7075 req->mask = 0;
7076 status = wine_server_call( req );
7077 if (status == STATUS_SUCCESS)
7079 p->InheritHandle = (reply->old_flags & HANDLE_FLAG_INHERIT) != 0;
7080 p->ProtectFromClose = (reply->old_flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0;
7081 if (used_len) *used_len = sizeof(*p);
7084 SERVER_END_REQ;
7085 break;
7088 default:
7089 FIXME("Unsupported information class %u\n", info_class);
7090 status = STATUS_NOT_IMPLEMENTED;
7091 break;
7093 return status;
7097 /**************************************************************************
7098 * NtSetInformationObject (NTDLL.@)
7100 NTSTATUS WINAPI NtSetInformationObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
7101 void *ptr, ULONG len )
7103 NTSTATUS status;
7105 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle, info_class, ptr, len);
7107 switch (info_class)
7109 case ObjectDataInformation:
7111 OBJECT_DATA_INFORMATION* p = ptr;
7113 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
7115 SERVER_START_REQ( set_handle_info )
7117 req->handle = wine_server_obj_handle( handle );
7118 req->mask = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
7119 if (p->InheritHandle) req->flags |= HANDLE_FLAG_INHERIT;
7120 if (p->ProtectFromClose) req->flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
7121 status = wine_server_call( req );
7123 SERVER_END_REQ;
7124 break;
7127 default:
7128 FIXME("Unsupported information class %u\n", info_class);
7129 status = STATUS_NOT_IMPLEMENTED;
7130 break;
7132 return status;