include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / ntdll / unix / file.c
blob15fdf2cb02109de9c8c3525cb0c215befeee4f8d
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 #ifdef HAVE_SYS_EXTATTR_H
106 #undef XATTR_ADDITIONAL_OPTIONS
107 #include <sys/extattr.h>
108 #endif
109 #include <time.h>
110 #include <unistd.h>
112 #include "ntstatus.h"
113 #define WIN32_NO_STATUS
114 #include "windef.h"
115 #include "winnt.h"
116 #include "winioctl.h"
117 #include "winternl.h"
118 #include "ddk/ntddk.h"
119 #include "ddk/ntddser.h"
120 #include "ddk/wdm.h"
121 #define WINE_MOUNTMGR_EXTENSIONS
122 #include "ddk/mountmgr.h"
123 #include "wine/server.h"
124 #include "wine/list.h"
125 #include "wine/debug.h"
126 #include "unix_private.h"
128 WINE_DEFAULT_DEBUG_CHANNEL(file);
129 WINE_DECLARE_DEBUG_CHANNEL(winediag);
131 #define MAX_DOS_DRIVES 26
133 /* just in case... */
134 #undef VFAT_IOCTL_READDIR_BOTH
135 #undef EXT2_IOC_GETFLAGS
136 #undef EXT4_CASEFOLD_FL
138 #ifdef linux
140 /* We want the real kernel dirent structure, not the libc one */
141 typedef struct
143 long d_ino;
144 long d_off;
145 unsigned short d_reclen;
146 char d_name[256];
147 } KERNEL_DIRENT;
149 /* Define the VFAT ioctl to get both short and long file names */
150 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
152 /* Define the ext2 ioctl for handling extra attributes */
153 #define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
155 /* Case-insensitivity attribute */
156 #define EXT4_CASEFOLD_FL 0x40000000
158 #ifndef O_DIRECTORY
159 # define O_DIRECTORY 0200000 /* must be directory */
160 #endif
162 #ifndef AT_NO_AUTOMOUNT
163 #define AT_NO_AUTOMOUNT 0x800
164 #endif
166 #endif /* linux */
168 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
170 #define INVALID_NT_CHARS '*','?','<','>','|','"'
171 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
173 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
175 #define MAX_IGNORED_FILES 4
177 #ifndef XATTR_USER_PREFIX
178 # define XATTR_USER_PREFIX "user."
179 #endif
180 #ifndef XATTR_USER_PREFIX_LEN
181 # define XATTR_USER_PREFIX_LEN (sizeof(XATTR_USER_PREFIX) - 1)
182 #endif
184 #define SAMBA_XATTR_DOS_ATTRIB XATTR_USER_PREFIX "DOSATTRIB"
185 #define XATTR_ATTRIBS_MASK (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)
187 struct file_identity
189 dev_t dev;
190 ino_t ino;
193 static struct file_identity ignored_files[MAX_IGNORED_FILES];
194 static unsigned int ignored_files_count;
196 union file_directory_info
198 ULONG next;
199 FILE_DIRECTORY_INFORMATION dir;
200 FILE_BOTH_DIRECTORY_INFORMATION both;
201 FILE_FULL_DIRECTORY_INFORMATION full;
202 FILE_ID_BOTH_DIRECTORY_INFORMATION id_both;
203 FILE_ID_FULL_DIRECTORY_INFORMATION id_full;
204 FILE_ID_GLOBAL_TX_DIR_INFORMATION id_tx;
205 FILE_NAMES_INFORMATION names;
208 struct dir_data_buffer
210 struct dir_data_buffer *next; /* next buffer in the list */
211 unsigned int size; /* total size of the buffer */
212 unsigned int pos; /* current position in the buffer */
213 char data[1];
216 struct dir_data_names
218 const WCHAR *long_name; /* long file name in Unicode */
219 const WCHAR *short_name; /* short file name in Unicode */
220 const char *unix_name; /* Unix file name in host encoding */
223 struct dir_data
225 unsigned int size; /* size of the names array */
226 unsigned int count; /* count of used entries in the names array */
227 unsigned int pos; /* current reading position in the names array */
228 struct file_identity id; /* directory file identity */
229 struct dir_data_names *names; /* directory file names */
230 struct dir_data_buffer *buffer; /* head of data buffers list */
233 static const unsigned int dir_data_buffer_initial_size = 4096;
234 static const unsigned int dir_data_cache_initial_size = 256;
235 static const unsigned int dir_data_names_initial_size = 64;
237 static struct dir_data **dir_data_cache;
238 static unsigned int dir_data_cache_size;
240 static BOOL show_dot_files;
241 static mode_t start_umask;
243 /* at some point we may want to allow Winelib apps to set this */
244 static const BOOL is_case_sensitive = FALSE;
246 static pthread_mutex_t dir_mutex = PTHREAD_MUTEX_INITIALIZER;
247 static pthread_mutex_t mnt_mutex = PTHREAD_MUTEX_INITIALIZER;
249 /* check if a given Unicode char is OK in a DOS short name */
250 static inline BOOL is_invalid_dos_char( WCHAR ch )
252 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
253 if (ch > 0x7f) return TRUE;
254 return wcschr( invalid_chars, ch ) != NULL;
257 /* check if the device can be a mounted volume */
258 static inline BOOL is_valid_mounted_device( const struct stat *st )
260 #if defined(linux) || defined(__sun__)
261 return S_ISBLK( st->st_mode );
262 #else
263 /* disks are char devices on *BSD */
264 return S_ISCHR( st->st_mode );
265 #endif
268 static inline void ignore_file( const char *name )
270 struct stat st;
271 assert( ignored_files_count < MAX_IGNORED_FILES );
272 if (!stat( name, &st ))
274 ignored_files[ignored_files_count].dev = st.st_dev;
275 ignored_files[ignored_files_count].ino = st.st_ino;
276 ignored_files_count++;
280 static inline BOOL is_same_file( const struct file_identity *file, const struct stat *st )
282 return st->st_dev == file->dev && st->st_ino == file->ino;
285 static inline BOOL is_ignored_file( const struct stat *st )
287 unsigned int i;
289 for (i = 0; i < ignored_files_count; i++)
290 if (is_same_file( &ignored_files[i], st )) return TRUE;
291 return FALSE;
294 static inline unsigned int dir_info_align( unsigned int len )
296 return (len + 7) & ~7;
299 static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned int len )
301 switch (class)
303 case FileDirectoryInformation:
304 return offsetof( FILE_DIRECTORY_INFORMATION, FileName[len] );
305 case FileBothDirectoryInformation:
306 return offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[len] );
307 case FileFullDirectoryInformation:
308 return offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[len] );
309 case FileIdBothDirectoryInformation:
310 return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[len] );
311 case FileIdFullDirectoryInformation:
312 return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[len] );
313 case FileIdGlobalTxDirectoryInformation:
314 return offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[len] );
315 case FileNamesInformation:
316 return offsetof( FILE_NAMES_INFORMATION, FileName[len] );
317 default:
318 assert(0);
319 return 0;
323 static BOOL is_wildcard( WCHAR c )
325 return c == '*' || c == '?' || c == '>' || c == '<' || c == '\"';
328 static inline BOOL has_wildcard( const UNICODE_STRING *mask )
330 int i;
332 if (!mask) return TRUE;
333 for (i = 0; i < mask->Length / sizeof(WCHAR); i++)
334 if (is_wildcard( mask->Buffer[i] )) return TRUE;
336 return FALSE;
339 NTSTATUS errno_to_status( int err )
341 TRACE( "errno = %d\n", err );
342 switch (err)
344 case EAGAIN: return STATUS_SHARING_VIOLATION;
345 case EBADF: return STATUS_INVALID_HANDLE;
346 case EBUSY: return STATUS_DEVICE_BUSY;
347 case ENOSPC: return STATUS_DISK_FULL;
348 case EPERM:
349 case EROFS:
350 case EACCES: return STATUS_ACCESS_DENIED;
351 case ENOTDIR: return STATUS_OBJECT_PATH_NOT_FOUND;
352 case ENOENT: return STATUS_OBJECT_NAME_NOT_FOUND;
353 case EISDIR: return STATUS_INVALID_DEVICE_REQUEST;
354 case EMFILE:
355 case ENFILE: return STATUS_TOO_MANY_OPENED_FILES;
356 case EINVAL: return STATUS_INVALID_PARAMETER;
357 case ENOTEMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
358 case EPIPE: return STATUS_PIPE_DISCONNECTED;
359 case EIO: return STATUS_DEVICE_NOT_READY;
360 #ifdef ENOMEDIUM
361 case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE;
362 #endif
363 case ENXIO: return STATUS_NO_SUCH_DEVICE;
364 case ENOTTY:
365 case EOPNOTSUPP:return STATUS_NOT_SUPPORTED;
366 case ECONNRESET:return STATUS_PIPE_DISCONNECTED;
367 case EFAULT: return STATUS_ACCESS_VIOLATION;
368 case ESPIPE: return STATUS_ILLEGAL_FUNCTION;
369 case ELOOP: return STATUS_REPARSE_POINT_NOT_RESOLVED;
370 #ifdef ETIME /* Missing on FreeBSD */
371 case ETIME: return STATUS_IO_TIMEOUT;
372 #endif
373 case ENOEXEC: /* ?? */
374 case EEXIST: /* ?? */
375 default:
376 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
377 return STATUS_UNSUCCESSFUL;
382 static int xattr_fremove( int filedes, const char *name )
384 #ifdef HAVE_SYS_XATTR_H
385 # ifdef XATTR_ADDITIONAL_OPTIONS
386 return fremovexattr( filedes, name, 0 );
387 # else
388 return fremovexattr( filedes, name );
389 # endif
390 #elif defined(HAVE_SYS_EXTATTR_H)
391 return extattr_delete_fd( filedes, EXTATTR_NAMESPACE_USER, &name[XATTR_USER_PREFIX_LEN] );
392 #else
393 errno = ENOSYS;
394 return -1;
395 #endif
399 static int xattr_fset( int filedes, const char *name, const void *value, size_t size )
401 #ifdef HAVE_SYS_XATTR_H
402 # ifdef XATTR_ADDITIONAL_OPTIONS
403 return fsetxattr( filedes, name, value, size, 0, 0 );
404 # else
405 return fsetxattr( filedes, name, value, size, 0 );
406 # endif
407 #elif defined(HAVE_SYS_EXTATTR_H)
408 return extattr_set_fd( filedes, EXTATTR_NAMESPACE_USER, &name[XATTR_USER_PREFIX_LEN],
409 value, size );
410 #else
411 errno = ENOSYS;
412 return -1;
413 #endif
417 /* On macOS, getxattr() is significantly slower than listxattr()
418 * (even for files with no extended attributes).
420 #ifdef __APPLE__
421 static BOOL xattr_exists( const char **path, int *filedes, const char *name )
423 char xattrs[1024];
424 ssize_t i = 0, ret;
426 if (path)
427 ret = listxattr( *path, xattrs, sizeof(xattrs), 0 );
428 else
429 ret = flistxattr( *filedes, xattrs, sizeof(xattrs), 0 );
430 if (ret == -1)
431 return errno == ERANGE;
433 while (i < ret)
435 if (!strcmp( name, &xattrs[i] ))
436 return TRUE;
437 i += strlen(&xattrs[i]) + 1;
440 errno = ENOATTR;
441 return FALSE;
443 #endif
446 static int xattr_get( const char *path, const char *name, void *value, size_t size )
448 #ifdef __APPLE__
449 if (!xattr_exists( &path, NULL, name ))
450 return -1;
451 #endif
453 #ifdef HAVE_SYS_XATTR_H
454 # ifdef XATTR_ADDITIONAL_OPTIONS
455 return getxattr( path, name, value, size, 0, 0 );
456 # else
457 return getxattr( path, name, value, size );
458 # endif
459 #elif defined(HAVE_SYS_EXTATTR_H)
460 return extattr_get_file( path, EXTATTR_NAMESPACE_USER, &name[XATTR_USER_PREFIX_LEN],
461 value, size );
462 #else
463 errno = ENOSYS;
464 return -1;
465 #endif
469 static int xattr_fget( int filedes, const char *name, void *value, size_t size )
471 #ifdef __APPLE__
472 if (!xattr_exists( NULL, &filedes, name ))
473 return -1;
474 #endif
476 #ifdef HAVE_SYS_XATTR_H
477 # ifdef XATTR_ADDITIONAL_OPTIONS
478 return fgetxattr( filedes, name, value, size, 0, 0 );
479 # else
480 return fgetxattr( filedes, name, value, size );
481 # endif
482 #elif defined(HAVE_SYS_EXTATTR_H)
483 return extattr_get_fd( filedes, EXTATTR_NAMESPACE_USER, &name[XATTR_USER_PREFIX_LEN],
484 value, size );
485 #else
486 errno = ENOSYS;
487 return -1;
488 #endif
492 /* get space from the current directory data buffer, allocating a new one if necessary */
493 static void *get_dir_data_space( struct dir_data *data, unsigned int size )
495 struct dir_data_buffer *buffer = data->buffer;
496 void *ret;
498 if (!buffer || size > buffer->size - buffer->pos)
500 unsigned int new_size = buffer ? buffer->size * 2 : dir_data_buffer_initial_size;
501 if (new_size < size) new_size = size;
502 if (!(buffer = malloc( offsetof( struct dir_data_buffer, data[new_size] ) ))) return NULL;
503 buffer->pos = 0;
504 buffer->size = new_size;
505 buffer->next = data->buffer;
506 data->buffer = buffer;
508 ret = buffer->data + buffer->pos;
509 buffer->pos += size;
510 return ret;
513 /* add a string to the directory data buffer */
514 static const char *add_dir_data_nameA( struct dir_data *data, const char *name )
516 /* keep buffer data WCHAR-aligned */
517 char *ptr = get_dir_data_space( data, (strlen( name ) + sizeof(WCHAR)) & ~(sizeof(WCHAR) - 1) );
518 if (ptr) strcpy( ptr, name );
519 return ptr;
522 /* add a Unicode string to the directory data buffer */
523 static const WCHAR *add_dir_data_nameW( struct dir_data *data, const WCHAR *name )
525 WCHAR *ptr = get_dir_data_space( data, (wcslen( name ) + 1) * sizeof(WCHAR) );
526 if (ptr) wcscpy( ptr, name );
527 return ptr;
530 /* add an entry to the directory names array */
531 static BOOL add_dir_data_names( struct dir_data *data, const WCHAR *long_name,
532 const WCHAR *short_name, const char *unix_name )
534 static const WCHAR empty[1];
535 struct dir_data_names *names = data->names;
537 if (data->count >= data->size)
539 unsigned int new_size = max( data->size * 2, dir_data_names_initial_size );
541 if (!(names = realloc( names, new_size * sizeof(*names) ))) return FALSE;
542 data->size = new_size;
543 data->names = names;
546 if (short_name[0])
548 if (!(names[data->count].short_name = add_dir_data_nameW( data, short_name ))) return FALSE;
550 else names[data->count].short_name = empty;
552 if (!(names[data->count].long_name = add_dir_data_nameW( data, long_name ))) return FALSE;
553 if (!(names[data->count].unix_name = add_dir_data_nameA( data, unix_name ))) return FALSE;
554 data->count++;
555 return TRUE;
558 /* free the complete directory data structure */
559 static void free_dir_data( struct dir_data *data )
561 struct dir_data_buffer *buffer, *next;
563 if (!data) return;
565 for (buffer = data->buffer; buffer; buffer = next)
567 next = buffer->next;
568 free( buffer );
570 free( data->names );
571 free( data );
575 /* support for a directory queue for filesystem searches */
577 struct dir_name
579 struct list entry;
580 char name[1];
583 static struct list dir_queue = LIST_INIT( dir_queue );
585 static NTSTATUS add_dir_to_queue( const char *name )
587 int len = strlen( name ) + 1;
588 struct dir_name *dir = malloc( offsetof( struct dir_name, name[len] ));
589 if (!dir) return STATUS_NO_MEMORY;
590 strcpy( dir->name, name );
591 list_add_tail( &dir_queue, &dir->entry );
592 return STATUS_SUCCESS;
595 static NTSTATUS next_dir_in_queue( char *name )
597 struct list *head = list_head( &dir_queue );
598 if (head)
600 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
601 strcpy( name, dir->name );
602 list_remove( &dir->entry );
603 free( dir );
604 return STATUS_SUCCESS;
606 return STATUS_OBJECT_NAME_NOT_FOUND;
609 static void flush_dir_queue(void)
611 struct list *head;
613 while ((head = list_head( &dir_queue )))
615 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
616 list_remove( &dir->entry );
617 free( dir );
622 #ifdef __ANDROID__
624 static char *unescape_field( char *str )
626 char *in, *out;
628 for (in = out = str; *in; in++, out++)
630 *out = *in;
631 if (in[0] == '\\')
633 if (in[1] == '\\')
635 out[0] = '\\';
636 in++;
638 else if (in[1] == '0' && in[2] == '4' && in[3] == '0')
640 out[0] = ' ';
641 in += 3;
643 else if (in[1] == '0' && in[2] == '1' && in[3] == '1')
645 out[0] = '\t';
646 in += 3;
648 else if (in[1] == '0' && in[2] == '1' && in[3] == '2')
650 out[0] = '\n';
651 in += 3;
653 else if (in[1] == '1' && in[2] == '3' && in[3] == '4')
655 out[0] = '\\';
656 in += 3;
660 *out = '\0';
662 return str;
665 static inline char *get_field( char **str )
667 char *ret;
669 ret = strsep( str, " \t" );
670 if (*str) *str += strspn( *str, " \t" );
672 return ret;
674 /************************************************************************
675 * getmntent_replacement
677 * getmntent replacement for Android.
679 * NB returned static buffer is not thread safe; protect with mnt_mutex.
681 static struct mntent *getmntent_replacement( FILE *f )
683 static struct mntent entry;
684 static char buf[4096];
685 char *p, *start;
689 if (!fgets( buf, sizeof(buf), f )) return NULL;
690 p = strchr( buf, '\n' );
691 if (p) *p = '\0';
692 else /* Partially unread line, move file ptr to end */
694 char tmp[1024];
695 while (fgets( tmp, sizeof(tmp), f ))
696 if (strchr( tmp, '\n' )) break;
698 start = buf + strspn( buf, " \t" );
699 } while (start[0] == '\0' || start[0] == '#');
701 p = get_field( &start );
702 entry.mnt_fsname = p ? unescape_field( p ) : (char *)"";
704 p = get_field( &start );
705 entry.mnt_dir = p ? unescape_field( p ) : (char *)"";
707 p = get_field( &start );
708 entry.mnt_type = p ? unescape_field( p ) : (char *)"";
710 p = get_field( &start );
711 entry.mnt_opts = p ? unescape_field( p ) : (char *)"";
713 p = get_field( &start );
714 entry.mnt_freq = p ? atoi(p) : 0;
716 p = get_field( &start );
717 entry.mnt_passno = p ? atoi(p) : 0;
719 return &entry;
721 #define getmntent getmntent_replacement
722 #endif
724 /***********************************************************************
725 * parse_mount_entries
727 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
730 #ifdef sun
731 #include <sys/vfstab.h>
732 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
734 struct vfstab entry;
735 struct stat st;
736 char *device;
738 while (! getvfsent( f, &entry ))
740 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
741 if (!strcmp( entry.vfs_fstype, "nfs" ) ||
742 !strcmp( entry.vfs_fstype, "smbfs" ) ||
743 !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
745 if (stat( entry.vfs_mountp, &st ) == -1) continue;
746 if (st.st_dev != dev || st.st_ino != ino) continue;
747 if (!strcmp( entry.vfs_fstype, "fd" ))
749 if ((device = strstr( entry.vfs_mntopts, "dev=" )))
751 char *p = strchr( device + 4, ',' );
752 if (p) *p = 0;
753 return device + 4;
756 else
757 return entry.vfs_special;
759 return NULL;
761 #endif
763 #ifdef linux
764 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
766 struct mntent *entry;
767 struct stat st;
768 char *device;
770 while ((entry = getmntent( f )))
772 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
773 if (!strcmp( entry->mnt_type, "nfs" ) ||
774 !strcmp( entry->mnt_type, "cifs" ) ||
775 !strcmp( entry->mnt_type, "smbfs" ) ||
776 !strcmp( entry->mnt_type, "ncpfs" )) continue;
778 if (stat( entry->mnt_dir, &st ) == -1) continue;
779 if (st.st_dev != dev || st.st_ino != ino) continue;
780 if (!strcmp( entry->mnt_type, "supermount" ))
782 if ((device = strstr( entry->mnt_opts, "dev=" )))
784 char *p = strchr( device + 4, ',' );
785 if (p) *p = 0;
786 return device + 4;
789 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
791 /* if device is a regular file check for a loop mount */
792 if ((device = strstr( entry->mnt_opts, "loop=" )))
794 char *p = strchr( device + 5, ',' );
795 if (p) *p = 0;
796 return device + 5;
799 else
800 return entry->mnt_fsname;
802 return NULL;
804 #endif
806 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
807 #include <fstab.h>
808 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
810 struct fstab *entry;
811 struct stat st;
813 while ((entry = getfsent()))
815 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
816 if (!strcmp( entry->fs_vfstype, "nfs" ) ||
817 !strcmp( entry->fs_vfstype, "smbfs" ) ||
818 !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
820 if (stat( entry->fs_file, &st ) == -1) continue;
821 if (st.st_dev != dev || st.st_ino != ino) continue;
822 return entry->fs_spec;
824 return NULL;
826 #endif
828 #ifdef sun
829 #include <sys/mnttab.h>
830 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
832 struct mnttab entry;
833 struct stat st;
834 char *device;
837 while (( ! getmntent( f, &entry) ))
839 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
840 if (!strcmp( entry.mnt_fstype, "nfs" ) ||
841 !strcmp( entry.mnt_fstype, "smbfs" ) ||
842 !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
844 if (stat( entry.mnt_mountp, &st ) == -1) continue;
845 if (st.st_dev != dev || st.st_ino != ino) continue;
846 if (!strcmp( entry.mnt_fstype, "fd" ))
848 if ((device = strstr( entry.mnt_mntopts, "dev=" )))
850 char *p = strchr( device + 4, ',' );
851 if (p) *p = 0;
852 return device + 4;
855 else
856 return entry.mnt_special;
858 return NULL;
860 #endif
862 /***********************************************************************
863 * get_default_drive_device
865 * Return the default device to use for a given drive mount point.
867 static char *get_default_drive_device( const char *root )
869 char *ret = NULL;
871 #ifdef linux
872 FILE *f;
873 char *device = NULL;
874 int fd, res = -1;
875 struct stat st;
877 /* try to open it first to force it to get mounted */
878 if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
880 res = fstat( fd, &st );
881 close( fd );
883 /* now try normal stat just in case */
884 if (res == -1) res = stat( root, &st );
885 if (res == -1) return NULL;
887 mutex_lock( &mnt_mutex );
889 #ifdef __ANDROID__
890 if ((f = fopen( "/proc/mounts", "r" )))
892 device = parse_mount_entries( f, st.st_dev, st.st_ino );
893 fclose( f );
895 #else
896 if ((f = fopen( "/etc/mtab", "r" )))
898 device = parse_mount_entries( f, st.st_dev, st.st_ino );
899 fclose( f );
901 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
902 if (!device && (f = fopen( "/etc/fstab", "r" )))
904 device = parse_mount_entries( f, st.st_dev, st.st_ino );
905 fclose( f );
907 #endif
908 if (device) ret = strdup( device );
909 mutex_unlock( &mnt_mutex );
911 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
912 char *device = NULL;
913 int fd, res = -1;
914 struct stat st;
916 /* try to open it first to force it to get mounted */
917 if ((fd = open( root, O_RDONLY )) != -1)
919 res = fstat( fd, &st );
920 close( fd );
922 /* now try normal stat just in case */
923 if (res == -1) res = stat( root, &st );
924 if (res == -1) return NULL;
926 mutex_lock( &mnt_mutex );
928 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
929 * pass NULL. Leave the argument in for symmetry.
931 device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
932 if (device) ret = strdup( device );
933 mutex_unlock( &mnt_mutex );
935 #elif defined( sun )
936 FILE *f;
937 char *device = NULL;
938 int fd, res = -1;
939 struct stat st;
941 /* try to open it first to force it to get mounted */
942 if ((fd = open( root, O_RDONLY )) != -1)
944 res = fstat( fd, &st );
945 close( fd );
947 /* now try normal stat just in case */
948 if (res == -1) res = stat( root, &st );
949 if (res == -1) return NULL;
951 mutex_lock( &mnt_mutex );
953 if ((f = fopen( "/etc/mnttab", "r" )))
955 device = parse_mount_entries( f, st.st_dev, st.st_ino);
956 fclose( f );
958 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
959 if (!device && (f = fopen( "/etc/vfstab", "r" )))
961 device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
962 fclose( f );
964 if (device) ret = strdup( device );
965 mutex_unlock( &mnt_mutex );
967 #elif defined(__APPLE__)
968 struct statfs *mntStat;
969 struct stat st;
970 int i;
971 int mntSize;
972 dev_t dev;
973 ino_t ino;
974 static const char path_bsd_device[] = "/dev/disk";
975 int res;
977 res = stat( root, &st );
978 if (res == -1) return NULL;
980 dev = st.st_dev;
981 ino = st.st_ino;
983 mutex_lock( &mnt_mutex );
985 mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
987 for (i = 0; i < mntSize && !ret; i++)
989 if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
990 if (st.st_dev != dev || st.st_ino != ino) continue;
992 /* FIXME add support for mounted network drive */
993 if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
995 /* set return value to the corresponding raw BSD node */
996 ret = malloc( strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
997 if (ret)
999 strcpy(ret, "/dev/r");
1000 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
1004 mutex_unlock( &mnt_mutex );
1005 #else
1006 static int warned;
1007 if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
1008 #endif
1009 return ret;
1013 /***********************************************************************
1014 * get_device_mount_point
1016 * Return the current mount point for a device.
1018 static char *get_device_mount_point( dev_t dev )
1020 char *ret = NULL;
1022 #ifdef linux
1023 FILE *f;
1025 mutex_lock( &mnt_mutex );
1027 #ifdef __ANDROID__
1028 if ((f = fopen( "/proc/mounts", "r" )))
1029 #else
1030 if ((f = fopen( "/etc/mtab", "r" )))
1031 #endif
1033 struct mntent *entry;
1034 struct stat st;
1035 char *p, *device;
1037 while ((entry = getmntent( f )))
1039 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
1040 if (!strcmp( entry->mnt_type, "nfs" ) ||
1041 !strcmp( entry->mnt_type, "cifs" ) ||
1042 !strcmp( entry->mnt_type, "smbfs" ) ||
1043 !strcmp( entry->mnt_type, "ncpfs" )) continue;
1045 if (!strcmp( entry->mnt_type, "supermount" ))
1047 if ((device = strstr( entry->mnt_opts, "dev=" )))
1049 device += 4;
1050 if ((p = strchr( device, ',' ))) *p = 0;
1053 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
1055 /* if device is a regular file check for a loop mount */
1056 if ((device = strstr( entry->mnt_opts, "loop=" )))
1058 device += 5;
1059 if ((p = strchr( device, ',' ))) *p = 0;
1062 else device = entry->mnt_fsname;
1064 if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
1066 ret = strdup( entry->mnt_dir );
1067 break;
1070 fclose( f );
1072 mutex_unlock( &mnt_mutex );
1073 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1074 struct statfs *entry;
1075 struct stat st;
1076 int i, size;
1078 mutex_lock( &mnt_mutex );
1080 size = getmntinfo( &entry, MNT_NOWAIT );
1081 for (i = 0; i < size; i++)
1083 if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
1084 if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
1086 ret = strdup( entry[i].f_mntonname );
1087 break;
1090 mutex_unlock( &mnt_mutex );
1091 #else
1092 static int warned;
1093 if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
1094 #endif
1095 return ret;
1099 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1100 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1102 struct get_fsid
1104 ULONG size;
1105 dev_t dev;
1106 fsid_t fsid;
1109 struct fs_cache
1111 dev_t dev;
1112 fsid_t fsid;
1113 BOOLEAN case_sensitive;
1114 } fs_cache[64];
1116 struct vol_caps
1118 ULONG size;
1119 vol_capabilities_attr_t caps;
1122 /***********************************************************************
1123 * look_up_fs_cache
1125 * Checks if the specified file system is in the cache.
1127 static struct fs_cache *look_up_fs_cache( dev_t dev )
1129 int i;
1130 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1131 if (fs_cache[i].dev == dev)
1132 return fs_cache+i;
1133 return NULL;
1136 /***********************************************************************
1137 * add_fs_cache
1139 * Adds the specified file system to the cache.
1141 static void add_fs_cache( dev_t dev, fsid_t fsid, BOOLEAN case_sensitive )
1143 int i;
1144 struct fs_cache *entry = look_up_fs_cache( dev );
1145 static int once = 0;
1146 if (entry)
1148 /* Update the cache */
1149 entry->fsid = fsid;
1150 entry->case_sensitive = case_sensitive;
1151 return;
1154 /* Add a new entry */
1155 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1156 if (fs_cache[i].dev == 0)
1158 /* This entry is empty, use it */
1159 fs_cache[i].dev = dev;
1160 fs_cache[i].fsid = fsid;
1161 fs_cache[i].case_sensitive = case_sensitive;
1162 return;
1165 /* Cache is out of space, warn */
1166 if (!once++)
1167 WARN( "FS cache is out of space, expect performance problems\n" );
1170 /***********************************************************************
1171 * get_dir_case_sensitivity_attr
1173 * Checks if the volume containing the specified directory is case
1174 * sensitive or not. Uses getattrlist(2).
1176 static int get_dir_case_sensitivity_attr( const char *dir )
1178 char *mntpoint;
1179 struct attrlist attr;
1180 struct vol_caps caps;
1181 struct get_fsid get_fsid;
1182 struct fs_cache *entry;
1184 /* First get the FS ID of the volume */
1185 attr.bitmapcount = ATTR_BIT_MAP_COUNT;
1186 attr.reserved = 0;
1187 attr.commonattr = ATTR_CMN_DEVID|ATTR_CMN_FSID;
1188 attr.volattr = attr.dirattr = attr.fileattr = attr.forkattr = 0;
1189 get_fsid.size = 0;
1190 if (getattrlist( dir, &attr, &get_fsid, sizeof(get_fsid), 0 ) != 0 ||
1191 get_fsid.size != sizeof(get_fsid))
1192 return -1;
1193 /* Try to look it up in the cache */
1194 entry = look_up_fs_cache( get_fsid.dev );
1195 if (entry && !memcmp( &entry->fsid, &get_fsid.fsid, sizeof(fsid_t) ))
1196 /* Cache lookup succeeded */
1197 return entry->case_sensitive;
1198 /* Cache is stale at this point, we have to update it */
1200 mntpoint = get_device_mount_point( get_fsid.dev );
1201 /* Now look up the case-sensitivity */
1202 attr.commonattr = 0;
1203 attr.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES;
1204 if (getattrlist( mntpoint, &attr, &caps, sizeof(caps), 0 ) < 0)
1206 free( mntpoint );
1207 add_fs_cache( get_fsid.dev, get_fsid.fsid, TRUE );
1208 return TRUE;
1210 free( mntpoint );
1211 if (caps.size == sizeof(caps) &&
1212 (caps.caps.valid[VOL_CAPABILITIES_FORMAT] &
1213 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING)) ==
1214 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING))
1216 BOOLEAN ret;
1218 if ((caps.caps.capabilities[VOL_CAPABILITIES_FORMAT] &
1219 VOL_CAP_FMT_CASE_SENSITIVE) != VOL_CAP_FMT_CASE_SENSITIVE)
1220 ret = FALSE;
1221 else
1222 ret = TRUE;
1223 /* Update the cache */
1224 add_fs_cache( get_fsid.dev, get_fsid.fsid, ret );
1225 return ret;
1227 return FALSE;
1229 #endif
1231 /***********************************************************************
1232 * get_dir_case_sensitivity_stat
1234 * Checks if the volume containing the specified directory is case
1235 * sensitive or not. Uses (f)statfs(2), statvfs(2), fstatat(2), or ioctl(2).
1237 static BOOLEAN get_dir_case_sensitivity_stat( const char *dir )
1239 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1240 struct statfs stfs;
1242 if (statfs( dir, &stfs ) == -1) return TRUE;
1243 /* Assume these file systems are always case insensitive.*/
1244 if (!strcmp( stfs.f_fstypename, "fusefs" ) &&
1245 !strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1246 return FALSE;
1247 /* msdosfs was case-insensitive since FreeBSD 8, if not earlier */
1248 if (!strcmp( stfs.f_fstypename, "msdosfs" ) ||
1249 /* older CIFS protocol versions uppercase filename on the client,
1250 * newer versions should be case-insensitive on the server anyway */
1251 !strcmp( stfs.f_fstypename, "smbfs" ))
1252 return FALSE;
1253 /* no ntfs-3g: modern fusefs has no way to report the filesystem on FreeBSD
1254 * no cd9660 or udf, they're case-sensitive on FreeBSD
1256 #ifdef __APPLE__
1257 if (!strcmp( stfs.f_fstypename, "msdos" ) ||
1258 !strcmp( stfs.f_fstypename, "cd9660" ) ||
1259 !strcmp( stfs.f_fstypename, "udf" ) ||
1260 !strcmp( stfs.f_fstypename, "ntfs" ))
1261 return FALSE;
1262 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1263 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_fssubtype == 0 ||
1264 stfs.f_fssubtype == 1 ||
1265 stfs.f_fssubtype == 128))
1266 return FALSE;
1267 #else
1268 /* The field says "reserved", but a quick look at the kernel source
1269 * tells us that this "reserved" field is really the same as the
1270 * "fssubtype" field from the inode64 structure (see munge_statfs()
1271 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1273 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_reserved1 == 0 ||
1274 stfs.f_reserved1 == 1 ||
1275 stfs.f_reserved1 == 128))
1276 return FALSE;
1277 #endif
1278 #endif
1279 return TRUE;
1281 #elif defined(__NetBSD__)
1282 struct statvfs stfs;
1284 if (statvfs( dir, &stfs ) == -1) return TRUE;
1285 /* Only assume CIOPFS is case insensitive. */
1286 if (strcmp( stfs.f_fstypename, "fusefs" ) ||
1287 strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1288 return FALSE;
1289 return TRUE;
1291 #elif defined(__linux__)
1292 BOOLEAN sens = TRUE;
1293 struct statfs stfs;
1294 struct stat st;
1295 int fd, flags;
1297 if ((fd = open( dir, O_RDONLY | O_NONBLOCK )) == -1)
1298 return TRUE;
1300 if (ioctl( fd, EXT2_IOC_GETFLAGS, &flags ) != -1 && (flags & EXT4_CASEFOLD_FL))
1302 sens = FALSE;
1304 else if (fstatfs( fd, &stfs ) == 0 && /* CIOPFS is case insensitive. Instead of */
1305 stfs.f_type == 0x65735546 /* FUSE_SUPER_MAGIC */ && /* parsing mtab to discover if the FUSE FS */
1306 fstatat( fd, ".ciopfs", &st, AT_NO_AUTOMOUNT ) == 0) /* is CIOPFS, look for .ciopfs in the dir. */
1308 sens = FALSE;
1311 close( fd );
1312 return sens;
1313 #else
1314 return TRUE;
1315 #endif
1319 /***********************************************************************
1320 * get_dir_case_sensitivity
1322 * Checks if the volume containing the specified directory is case
1323 * sensitive or not. Uses multiple methods, depending on platform.
1325 static BOOLEAN get_dir_case_sensitivity( const char *dir )
1327 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1328 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1329 int case_sensitive = get_dir_case_sensitivity_attr( dir );
1330 if (case_sensitive != -1) return case_sensitive;
1331 #endif
1332 return get_dir_case_sensitivity_stat( dir );
1336 /***********************************************************************
1337 * is_hidden_file
1339 * Check if the specified file should be hidden based on its unix path and the show dot files option.
1341 static BOOL is_hidden_file( const char *name )
1343 const char *p;
1345 if (show_dot_files) return FALSE;
1347 p = name + strlen( name );
1348 while (p > name && p[-1] == '/') p--;
1349 while (p > name && p[-1] != '/') p--;
1350 if (*p++ != '.') return FALSE;
1351 if (!*p || *p == '/') return FALSE; /* "." directory */
1352 if (*p++ != '.') return TRUE;
1353 if (!*p || *p == '/') return FALSE; /* ".." directory */
1354 return TRUE;
1358 /***********************************************************************
1359 * hash_short_file_name
1361 * Transform a Unix file name into a hashed DOS name. If the name is not a valid
1362 * DOS name, it is replaced by a hashed version that fits in 8.3 format.
1363 * 'buffer' must be at least 12 characters long.
1364 * Returns length of short name in bytes; short name is NOT null-terminated.
1366 static ULONG hash_short_file_name( const WCHAR *name, int length, LPWSTR buffer )
1368 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1370 LPCWSTR p, ext, end = name + length;
1371 LPWSTR dst;
1372 unsigned short hash;
1373 int i;
1375 /* Compute the hash code of the file name */
1376 /* If you know something about hash functions, feel free to */
1377 /* insert a better algorithm here... */
1378 if (!is_case_sensitive)
1380 for (p = name, hash = 0xbeef; p < end - 1; p++)
1381 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p) ^ (towlower(p[1]) << 8);
1382 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p); /* Last character */
1384 else
1386 for (p = name, hash = 0xbeef; p < end - 1; p++)
1387 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
1388 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
1391 /* Find last dot for start of the extension */
1392 p = name;
1393 while (*p == '.') ++p;
1394 for (p = p + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
1396 /* Copy first 4 chars, replacing invalid chars with '_' */
1397 for (i = 4, p = name, dst = buffer; i > 0; p++)
1399 if (p == end || p == ext) break;
1400 if (*p == '.') continue;
1401 *dst++ = is_invalid_dos_char(*p) ? '_' : *p;
1402 i--;
1404 /* Pad to 5 chars with '~' */
1405 while (i-- >= 0) *dst++ = '~';
1407 /* Insert hash code converted to 3 ASCII chars */
1408 *dst++ = hash_chars[(hash >> 10) & 0x1f];
1409 *dst++ = hash_chars[(hash >> 5) & 0x1f];
1410 *dst++ = hash_chars[hash & 0x1f];
1412 /* Copy the first 3 chars of the extension (if any) */
1413 if (ext)
1415 *dst++ = '.';
1416 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
1417 *dst++ = is_invalid_dos_char(*ext) ? '_' : *ext;
1419 return dst - buffer;
1423 /***********************************************************************
1424 * match_filename_part
1426 * Recursive helper for match_filename().
1429 static BOOLEAN match_filename_part( const WCHAR *name, const WCHAR *name_end, const WCHAR *mask, const WCHAR *mask_end )
1431 WCHAR c;
1433 while (name < name_end && mask < mask_end)
1435 switch(*mask)
1437 case '*':
1438 mask++;
1439 while (mask < mask_end && *mask == '*') mask++; /* Skip consecutive '*' */
1440 if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
1442 while (name < name_end)
1444 c = *mask == '"' ? '.' : *mask;
1445 if (!is_wildcard(c))
1447 if (is_case_sensitive)
1448 while (name < name_end && (*name != c)) name++;
1449 else
1450 while (name < name_end && (towupper(*name) != towupper(c))) name++;
1452 if (match_filename_part( name, name_end, mask, mask_end )) return TRUE;
1453 ++name;
1455 break;
1456 case '<':
1458 const WCHAR *next_dot;
1459 BOOL had_dot = FALSE;
1461 ++mask;
1462 while (name < name_end)
1464 next_dot = name;
1465 while (next_dot < name_end && *next_dot != '.') ++next_dot;
1466 if (next_dot == name_end && had_dot) break;
1467 if (next_dot < name_end)
1469 had_dot = TRUE;
1470 ++next_dot;
1472 if (mask < mask_end)
1474 while (name < next_dot)
1476 c = *mask == '"' ? '.' : *mask;
1477 if (!is_wildcard(c))
1479 if (is_case_sensitive)
1480 while (name < next_dot && (*name != c)) name++;
1481 else
1482 while (name < next_dot && (towupper(*name) != towupper(c))) name++;
1484 if (match_filename_part( name, name_end, mask, mask_end )) return TRUE;
1485 ++name;
1488 name = next_dot;
1490 break;
1492 case '?':
1493 mask++;
1494 name++;
1495 break;
1496 case '>':
1497 mask++;
1498 if (*name == '.')
1500 while (mask < mask_end && *mask == '>') mask++;
1501 if (mask == mask_end) name++;
1503 else name++;
1504 break;
1505 default:
1506 c = *mask == '"' ? '.' : *mask;
1507 if (is_case_sensitive && c != *name) return FALSE;
1508 if (!is_case_sensitive && towupper(c) != towupper(*name)) return FALSE;
1509 mask++;
1510 name++;
1511 break;
1514 while (mask < mask_end && (*mask == '*' || *mask == '<' || *mask == '"' || *mask == '>'))
1515 mask++;
1516 return (name == name_end && mask == mask_end);
1520 /***********************************************************************
1521 * match_filename
1523 * Check a file name against a mask.
1526 static BOOLEAN match_filename( const WCHAR *name, int length, const UNICODE_STRING *mask_str )
1528 /* Special handling for parent directory. */
1529 if (length == 2 && name[0] == '.' && name[1] == '.') --length;
1531 return match_filename_part( name, name + length, mask_str->Buffer,
1532 mask_str->Buffer + mask_str->Length / sizeof(WCHAR));
1536 /***********************************************************************
1537 * is_legal_8dot3_name
1539 * Simplified version of RtlIsNameLegalDOS8Dot3.
1541 static BOOLEAN is_legal_8dot3_name( const WCHAR *name, int len )
1543 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,':','/','\\',0 };
1544 int i, dot = -1;
1546 if (len > 12) return FALSE;
1548 /* a starting . is invalid, except for . and .. */
1549 if (len > 0 && name[0] == '.') return (len == 1 || (len == 2 && name[1] == '.'));
1551 for (i = 0; i < len; i++)
1553 if (name[i] > 0x7f) return FALSE;
1554 if (wcschr( invalid_chars, name[i] )) return FALSE;
1555 if (name[i] == '.')
1557 if (dot != -1) return FALSE;
1558 dot = i;
1562 if (dot == -1) return (len <= 8);
1563 if (dot > 8) return FALSE;
1564 return (len - dot > 1 && len - dot < 5);
1568 /***********************************************************************
1569 * append_entry
1571 * Add a file to the directory data if it matches the mask.
1573 static BOOL append_entry( struct dir_data *data, const char *long_name,
1574 const char *short_name, const UNICODE_STRING *mask )
1576 int long_len, short_len;
1577 WCHAR long_nameW[MAX_DIR_ENTRY_LEN + 1];
1578 WCHAR short_nameW[13];
1580 long_len = ntdll_umbstowcs( long_name, strlen(long_name), long_nameW, ARRAY_SIZE(long_nameW) );
1581 if (long_len == ARRAY_SIZE(long_nameW)) return TRUE;
1582 long_nameW[long_len] = 0;
1584 if (short_name)
1586 short_len = ntdll_umbstowcs( short_name, strlen(short_name),
1587 short_nameW, ARRAY_SIZE( short_nameW ) - 1 );
1589 else /* generate a short name if necessary */
1591 short_len = 0;
1592 if (!is_legal_8dot3_name( long_nameW, long_len ))
1593 short_len = hash_short_file_name( long_nameW, long_len, short_nameW );
1595 short_nameW[short_len] = 0;
1596 wcsupr( short_nameW );
1598 TRACE( "long %s short %s mask %s\n",
1599 debugstr_w( long_nameW ), debugstr_w( short_nameW ), debugstr_us( mask ));
1601 if (mask && !match_filename( long_nameW, long_len, mask ))
1603 if (!short_len) return TRUE; /* no short name to match */
1604 if (!match_filename( short_nameW, short_len, mask )) return TRUE;
1607 return add_dir_data_names( data, long_nameW, short_nameW, long_name );
1611 /* fetch the attributes of a file */
1612 static inline ULONG get_file_attributes( const struct stat *st )
1614 ULONG attr;
1616 if (S_ISDIR(st->st_mode))
1617 attr = FILE_ATTRIBUTE_DIRECTORY;
1618 else
1619 attr = FILE_ATTRIBUTE_ARCHIVE;
1620 if (!(st->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
1621 attr |= FILE_ATTRIBUTE_READONLY;
1622 return attr;
1626 /* decode the xattr-stored DOS attributes */
1627 static int parse_samba_dos_attrib_data( char *data, int len )
1629 char *end;
1630 int val;
1632 if (len > 2 && data[0] == '0' && data[1] == 'x')
1634 data[len] = 0;
1635 val = strtol( data, &end, 16 );
1636 if (!*end) return val & XATTR_ATTRIBS_MASK;
1638 else
1640 static BOOL once;
1641 if (!once++) FIXME( "Unhandled " SAMBA_XATTR_DOS_ATTRIB " extended attribute value.\n" );
1643 return 0;
1647 static BOOL fd_is_mount_point( int fd, const struct stat *st )
1649 struct stat parent;
1650 return S_ISDIR( st->st_mode ) && !fstatat( fd, "..", &parent, 0 )
1651 && (parent.st_dev != st->st_dev || parent.st_ino == st->st_ino);
1655 /* get the stat info and file attributes for a file (by file descriptor) */
1656 static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr )
1658 char attr_data[65];
1659 int attr_len, ret;
1661 *attr = 0;
1662 ret = fstat( fd, st );
1663 if (ret == -1) return ret;
1664 *attr |= get_file_attributes( st );
1665 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1666 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st ))
1667 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1669 attr_len = xattr_fget( fd, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 );
1670 if (attr_len != -1)
1671 *attr |= parse_samba_dos_attrib_data( attr_data, attr_len );
1672 else
1674 if (errno == ENOTSUP) return ret;
1675 #ifdef ENODATA
1676 if (errno == ENODATA) return ret;
1677 #endif
1678 #ifdef ENOATTR
1679 if (errno == ENOATTR) return ret;
1680 #endif
1681 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)\n",
1682 errno, strerror( errno ) );
1684 return ret;
1688 static int fd_set_dos_attrib( int fd, UINT attr, BOOL force_set )
1690 /* we only store the HIDDEN and SYSTEM attributes */
1691 attr &= XATTR_ATTRIBS_MASK;
1692 if (force_set || attr != 0)
1694 /* encode the attributes in Samba 3 ASCII format. Samba 4 has extended
1695 * this format with more features, but retains compatibility with the
1696 * earlier format. */
1697 char data[11];
1698 int len = snprintf( data, sizeof(data), "0x%x", attr );
1699 return xattr_fset( fd, SAMBA_XATTR_DOS_ATTRIB, data, len );
1701 else return xattr_fremove( fd, SAMBA_XATTR_DOS_ATTRIB );
1705 /* set the stat info and file attributes for a file (by file descriptor) */
1706 static NTSTATUS fd_set_file_info( int fd, UINT attr, BOOL force_set_xattr )
1708 struct stat st;
1710 if (fstat( fd, &st ) == -1) return errno_to_status( errno );
1711 if (attr & FILE_ATTRIBUTE_READONLY)
1713 if (S_ISDIR( st.st_mode))
1714 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
1715 else
1716 st.st_mode &= ~0222; /* clear write permission bits */
1718 else
1720 /* add write permission only where we already have read permission */
1721 st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~start_umask);
1723 if (fchmod( fd, st.st_mode ) == -1) return errno_to_status( errno );
1725 /* if the file has multiple names, we can't be sure that it is safe to not
1726 set the extended attribute, since any of the names could start with a dot */
1727 force_set_xattr = force_set_xattr || st.st_nlink > 1;
1729 if (fd_set_dos_attrib( fd, attr, force_set_xattr ) == -1 && errno != ENOTSUP)
1730 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)\n",
1731 errno, strerror( errno ) );
1733 return STATUS_SUCCESS;
1737 /* get the stat info and file attributes for a file (by name) */
1738 static int get_file_info( const char *path, struct stat *st, ULONG *attr )
1740 char *parent_path;
1741 char attr_data[65];
1742 int attr_len, ret;
1744 *attr = 0;
1745 ret = lstat( path, st );
1746 if (ret == -1) return ret;
1747 if (S_ISLNK( st->st_mode ))
1749 ret = stat( path, st );
1750 if (ret == -1) return ret;
1751 /* is a symbolic link and a directory, consider these "reparse points" */
1752 if (S_ISDIR( st->st_mode )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1754 else if (S_ISDIR( st->st_mode ) && (parent_path = malloc( strlen(path) + 4 )))
1756 struct stat parent_st;
1758 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1759 strcpy( parent_path, path );
1760 strcat( parent_path, "/.." );
1761 if (!stat( parent_path, &parent_st )
1762 && (st->st_dev != parent_st.st_dev || st->st_ino == parent_st.st_ino))
1763 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1765 free( parent_path );
1767 *attr |= get_file_attributes( st );
1769 attr_len = xattr_get( path, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 );
1770 if (attr_len != -1)
1771 *attr |= parse_samba_dos_attrib_data( attr_data, attr_len );
1772 else
1774 if (is_hidden_file( path ))
1775 *attr |= FILE_ATTRIBUTE_HIDDEN;
1776 if (errno == ENOTSUP) return ret;
1777 #ifdef ENODATA
1778 if (errno == ENODATA) return ret;
1779 #endif
1780 #ifdef ENOATTR
1781 if (errno == ENOATTR) return ret;
1782 #endif
1783 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB " from \"%s\". errno %d (%s)\n",
1784 path, errno, strerror( errno ) );
1786 return ret;
1790 #if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
1791 static int futimens( int fd, const struct timespec spec[2] )
1793 return syscall( __NR_utimensat, fd, NULL, spec, 0 );
1795 #define HAVE_FUTIMENS
1796 #endif /* __ANDROID__ */
1798 #ifndef UTIME_OMIT
1799 #define UTIME_OMIT ((1 << 30) - 2)
1800 #endif
1802 static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
1803 const LARGE_INTEGER *atime, NTSTATUS *status )
1805 #ifdef HAVE_FUTIMENS
1806 struct timespec tv[2];
1808 tv[0].tv_sec = tv[1].tv_sec = 0;
1809 tv[0].tv_nsec = tv[1].tv_nsec = UTIME_OMIT;
1810 if (atime->QuadPart)
1812 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1813 tv[0].tv_nsec = (atime->QuadPart % 10000000) * 100;
1815 if (mtime->QuadPart)
1817 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1818 tv[1].tv_nsec = (mtime->QuadPart % 10000000) * 100;
1820 #ifdef __APPLE__
1821 if (!&futimens) return FALSE;
1822 #endif
1823 if (futimens( fd, tv ) == -1) *status = errno_to_status( errno );
1824 else *status = STATUS_SUCCESS;
1825 return TRUE;
1826 #else
1827 return FALSE;
1828 #endif
1832 static NTSTATUS set_file_times( int fd, const LARGE_INTEGER *mtime, const LARGE_INTEGER *atime )
1834 NTSTATUS status = STATUS_SUCCESS;
1835 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1836 struct timeval tv[2];
1837 struct stat st;
1838 #endif
1840 if (set_file_times_precise( fd, mtime, atime, &status ))
1841 return status;
1843 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1844 if (!atime->QuadPart || !mtime->QuadPart)
1847 tv[0].tv_sec = tv[0].tv_usec = 0;
1848 tv[1].tv_sec = tv[1].tv_usec = 0;
1849 if (!fstat( fd, &st ))
1851 tv[0].tv_sec = st.st_atime;
1852 tv[1].tv_sec = st.st_mtime;
1853 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1854 tv[0].tv_usec = st.st_atim.tv_nsec / 1000;
1855 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1856 tv[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
1857 #endif
1858 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1859 tv[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1860 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1861 tv[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
1862 #endif
1865 if (atime->QuadPart)
1867 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1868 tv[0].tv_usec = (atime->QuadPart % 10000000) / 10;
1870 if (mtime->QuadPart)
1872 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1873 tv[1].tv_usec = (mtime->QuadPart % 10000000) / 10;
1875 #ifdef HAVE_FUTIMES
1876 if (futimes( fd, tv ) == -1) status = errno_to_status( errno );
1877 #elif defined(HAVE_FUTIMESAT)
1878 if (futimesat( fd, NULL, tv ) == -1) status = errno_to_status( errno );
1879 #endif
1881 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1882 FIXME( "setting file times not supported\n" );
1883 status = STATUS_NOT_IMPLEMENTED;
1884 #endif
1885 return status;
1889 static inline void get_file_times( const struct stat *st, LARGE_INTEGER *mtime, LARGE_INTEGER *ctime,
1890 LARGE_INTEGER *atime, LARGE_INTEGER *creation )
1892 mtime->QuadPart = ticks_from_time_t( st->st_mtime );
1893 ctime->QuadPart = ticks_from_time_t( st->st_ctime );
1894 atime->QuadPart = ticks_from_time_t( st->st_atime );
1895 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1896 mtime->QuadPart += st->st_mtim.tv_nsec / 100;
1897 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1898 mtime->QuadPart += st->st_mtimespec.tv_nsec / 100;
1899 #endif
1900 #ifdef HAVE_STRUCT_STAT_ST_CTIM
1901 ctime->QuadPart += st->st_ctim.tv_nsec / 100;
1902 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1903 ctime->QuadPart += st->st_ctimespec.tv_nsec / 100;
1904 #endif
1905 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1906 atime->QuadPart += st->st_atim.tv_nsec / 100;
1907 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1908 atime->QuadPart += st->st_atimespec.tv_nsec / 100;
1909 #endif
1910 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1911 creation->QuadPart = ticks_from_time_t( st->st_birthtime );
1912 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
1913 creation->QuadPart += st->st_birthtim.tv_nsec / 100;
1914 #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1915 creation->QuadPart += st->st_birthtimespec.tv_nsec / 100;
1916 #endif
1917 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
1918 creation->QuadPart = ticks_from_time_t( st->__st_birthtime );
1919 #ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
1920 creation->QuadPart += st->__st_birthtim.tv_nsec / 100;
1921 #endif
1922 #else
1923 *creation = *mtime;
1924 #endif
1928 /* fill in the file information that depends on the stat and attribute info */
1929 static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr,
1930 FILE_INFORMATION_CLASS class )
1932 switch (class)
1934 case FileBasicInformation:
1936 FILE_BASIC_INFORMATION *info = ptr;
1938 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1939 &info->LastAccessTime, &info->CreationTime );
1940 info->FileAttributes = attr;
1942 break;
1943 case FileStandardInformation:
1945 FILE_STANDARD_INFORMATION *info = ptr;
1947 if ((info->Directory = S_ISDIR(st->st_mode)))
1949 info->AllocationSize.QuadPart = 0;
1950 info->EndOfFile.QuadPart = 0;
1951 info->NumberOfLinks = 1;
1953 else
1955 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1956 info->EndOfFile.QuadPart = st->st_size;
1957 info->NumberOfLinks = st->st_nlink;
1960 break;
1961 case FileInternalInformation:
1963 FILE_INTERNAL_INFORMATION *info = ptr;
1964 info->IndexNumber.QuadPart = st->st_ino;
1966 break;
1967 case FileEndOfFileInformation:
1969 FILE_END_OF_FILE_INFORMATION *info = ptr;
1970 info->EndOfFile.QuadPart = S_ISDIR(st->st_mode) ? 0 : st->st_size;
1972 break;
1973 case FileAllInformation:
1975 FILE_ALL_INFORMATION *info = ptr;
1976 fill_file_info( st, attr, &info->BasicInformation, FileBasicInformation );
1977 fill_file_info( st, attr, &info->StandardInformation, FileStandardInformation );
1978 fill_file_info( st, attr, &info->InternalInformation, FileInternalInformation );
1980 break;
1981 /* all directory structures start with the FileDirectoryInformation layout */
1982 case FileBothDirectoryInformation:
1983 case FileFullDirectoryInformation:
1984 case FileDirectoryInformation:
1986 FILE_DIRECTORY_INFORMATION *info = ptr;
1988 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1989 &info->LastAccessTime, &info->CreationTime );
1990 if (S_ISDIR(st->st_mode))
1992 info->AllocationSize.QuadPart = 0;
1993 info->EndOfFile.QuadPart = 0;
1995 else
1997 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1998 info->EndOfFile.QuadPart = st->st_size;
2000 info->FileAttributes = attr;
2002 break;
2003 case FileIdFullDirectoryInformation:
2005 FILE_ID_FULL_DIRECTORY_INFORMATION *info = ptr;
2006 info->FileId.QuadPart = st->st_ino;
2007 fill_file_info( st, attr, info, FileDirectoryInformation );
2009 break;
2010 case FileIdBothDirectoryInformation:
2012 FILE_ID_BOTH_DIRECTORY_INFORMATION *info = ptr;
2013 info->FileId.QuadPart = st->st_ino;
2014 fill_file_info( st, attr, info, FileDirectoryInformation );
2016 break;
2017 case FileIdGlobalTxDirectoryInformation:
2019 FILE_ID_GLOBAL_TX_DIR_INFORMATION *info = ptr;
2020 info->FileId.QuadPart = st->st_ino;
2021 fill_file_info( st, attr, info, FileDirectoryInformation );
2023 break;
2025 default:
2026 return STATUS_INVALID_INFO_CLASS;
2028 return STATUS_SUCCESS;
2032 static unsigned int server_get_unix_name( HANDLE handle, char **unix_name )
2034 data_size_t size = 1024;
2035 unsigned int ret;
2036 char *name;
2038 for (;;)
2040 if (!(name = malloc( size + 1 ))) return STATUS_NO_MEMORY;
2042 SERVER_START_REQ( get_handle_unix_name )
2044 req->handle = wine_server_obj_handle( handle );
2045 wine_server_set_reply( req, name, size );
2046 ret = wine_server_call( req );
2047 size = reply->name_len;
2049 SERVER_END_REQ;
2051 if (!ret)
2053 name[size] = 0;
2054 *unix_name = name;
2055 break;
2057 free( name );
2058 if (ret != STATUS_BUFFER_OVERFLOW) break;
2060 return ret;
2063 static NTSTATUS fill_name_info( const char *unix_name, FILE_NAME_INFORMATION *info, LONG *name_len )
2065 WCHAR *nt_name;
2066 NTSTATUS status;
2068 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
2070 const WCHAR *ptr = nt_name;
2071 const WCHAR *end = ptr + wcslen( nt_name );
2073 /* Skip the volume mount point. */
2074 while (ptr != end && *ptr == '\\') ++ptr;
2075 while (ptr != end && *ptr != '\\') ++ptr;
2076 while (ptr != end && *ptr == '\\') ++ptr;
2077 while (ptr != end && *ptr != '\\') ++ptr;
2079 info->FileNameLength = (end - ptr) * sizeof(WCHAR);
2080 if (*name_len < info->FileNameLength) status = STATUS_BUFFER_OVERFLOW;
2081 else *name_len = info->FileNameLength;
2083 memcpy( info->FileName, ptr, *name_len );
2084 free( nt_name );
2087 return status;
2091 static NTSTATUS get_full_size_info(int fd, FILE_FS_FULL_SIZE_INFORMATION *info) {
2092 struct stat st;
2093 ULONGLONG bsize;
2095 #if !defined(linux) || !defined(HAVE_FSTATFS)
2096 struct statvfs stfs;
2097 #else
2098 struct statfs stfs;
2099 #endif
2101 if (fstat( fd, &st ) < 0) return errno_to_status( errno );
2102 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) return STATUS_INVALID_DEVICE_REQUEST;
2104 /* Linux's fstatvfs is buggy */
2105 #if !defined(linux) || !defined(HAVE_FSTATFS)
2106 if (fstatvfs( fd, &stfs ) < 0) return errno_to_status( errno );
2107 bsize = stfs.f_frsize;
2108 #else
2109 if (fstatfs( fd, &stfs ) < 0) return errno_to_status( errno );
2110 bsize = stfs.f_bsize;
2111 #endif
2112 if (bsize == 2048) /* assume CD-ROM */
2114 info->BytesPerSector = 2048;
2115 info->SectorsPerAllocationUnit = 1;
2117 else
2119 info->BytesPerSector = 512;
2120 info->SectorsPerAllocationUnit = 8;
2122 info->TotalAllocationUnits.QuadPart = bsize * stfs.f_blocks / (info->BytesPerSector * info->SectorsPerAllocationUnit);
2123 info->CallerAvailableAllocationUnits.QuadPart = bsize * stfs.f_bavail / (info->BytesPerSector * info->SectorsPerAllocationUnit);
2124 info->ActualAvailableAllocationUnits.QuadPart = bsize * stfs.f_bfree / (info->BytesPerSector * info->SectorsPerAllocationUnit);
2125 return STATUS_SUCCESS;
2129 static NTSTATUS server_get_file_info( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer,
2130 ULONG length, FILE_INFORMATION_CLASS info_class )
2132 SERVER_START_REQ( get_file_info )
2134 req->handle = wine_server_obj_handle( handle );
2135 req->info_class = info_class;
2136 wine_server_set_reply( req, buffer, length );
2137 io->Status = wine_server_call( req );
2138 io->Information = wine_server_reply_size( reply );
2140 SERVER_END_REQ;
2141 if (io->Status == STATUS_NOT_IMPLEMENTED)
2142 FIXME( "Unsupported info class %x\n", info_class );
2143 return io->Status;
2148 static unsigned int server_open_file_object( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
2149 ULONG sharing, ULONG options )
2151 unsigned int status;
2153 SERVER_START_REQ( open_file_object )
2155 req->access = access;
2156 req->attributes = attr->Attributes;
2157 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
2158 req->sharing = sharing;
2159 req->options = options;
2160 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
2161 status = wine_server_call( req );
2162 *handle = wine_server_ptr_handle( reply->handle );
2164 SERVER_END_REQ;
2165 return status;
2169 /* retrieve device/inode number for all the drives */
2170 static unsigned int get_drives_info( struct file_identity info[MAX_DOS_DRIVES] )
2172 static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
2173 static struct file_identity cache[MAX_DOS_DRIVES];
2174 static time_t last_update;
2175 static unsigned int nb_drives;
2176 unsigned int ret;
2177 time_t now = time(NULL);
2179 mutex_lock( &cache_mutex );
2180 if (now != last_update)
2182 char *buffer, *p;
2183 struct stat st;
2184 unsigned int i;
2186 if (asprintf( &buffer, "%s/dosdevices/a:", config_dir ) != -1)
2188 p = buffer + strlen(buffer) - 2;
2190 for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
2192 *p = 'a' + i;
2193 if (!stat( buffer, &st ))
2195 cache[i].dev = st.st_dev;
2196 cache[i].ino = st.st_ino;
2197 nb_drives++;
2199 else
2201 cache[i].dev = 0;
2202 cache[i].ino = 0;
2205 free( buffer );
2207 last_update = now;
2209 memcpy( info, cache, sizeof(cache) );
2210 ret = nb_drives;
2211 mutex_unlock( &cache_mutex );
2212 return ret;
2216 /* Find a DOS device which can act as the root of "path".
2217 * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
2218 static int find_dos_device( const char *path )
2220 int len = strlen(path);
2221 int drive;
2222 char *buffer;
2223 struct stat st;
2224 struct file_identity info[MAX_DOS_DRIVES];
2225 dev_t dev_id;
2227 if (!get_drives_info( info )) return -1;
2229 if (stat( path, &st ) < 0) return -1;
2230 dev_id = st.st_dev;
2232 /* strip off trailing slashes */
2233 while (len > 1 && path[len - 1] == '/') len--;
2235 /* make a copy of the path */
2236 if (!(buffer = malloc( len + 1 ))) return -1;
2237 memcpy( buffer, path, len );
2238 buffer[len] = 0;
2240 for (;;)
2242 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
2244 if (st.st_dev != dev_id) break;
2246 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
2248 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
2250 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2251 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
2252 free( buffer );
2253 return drive;
2257 if (len <= 1) break; /* reached root */
2258 while (len > 1 && path[len - 1] != '/') len--;
2259 while (len > 1 && path[len - 1] == '/') len--;
2260 buffer[len] = 0;
2262 free( buffer );
2263 return -1;
2266 static NTSTATUS get_mountmgr_fs_info( HANDLE handle, int fd, struct mountmgr_unix_drive *drive, ULONG size )
2268 OBJECT_ATTRIBUTES attr;
2269 UNICODE_STRING string;
2270 char *unix_name;
2271 HANDLE mountmgr;
2272 unsigned int status;
2273 int letter;
2275 if ((status = server_get_unix_name( handle, &unix_name ))) return status;
2276 letter = find_dos_device( unix_name );
2277 free( unix_name );
2279 memset( drive, 0, sizeof(*drive) );
2280 if (letter == -1)
2282 struct stat st;
2284 fstat( fd, &st );
2285 drive->unix_dev = st.st_rdev ? st.st_rdev : st.st_dev;
2287 else
2288 drive->letter = 'a' + letter;
2290 init_unicode_string( &string, MOUNTMGR_DEVICE_NAME );
2291 InitializeObjectAttributes( &attr, &string, 0, NULL, NULL );
2292 status = server_open_file_object( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr,
2293 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT );
2294 if (status) return status;
2296 status = sync_ioctl( mountmgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, drive, sizeof(*drive), drive, size );
2297 NtClose( mountmgr );
2298 if (status == STATUS_BUFFER_OVERFLOW) status = STATUS_SUCCESS;
2299 else if (status) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status);
2300 return status;
2304 /***********************************************************************
2305 * get_dir_data_entry
2307 * Return a directory entry from the cached data.
2309 static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, IO_STATUS_BLOCK *io,
2310 ULONG max_length, FILE_INFORMATION_CLASS class,
2311 union file_directory_info **last_info )
2313 const struct dir_data_names *names = &dir_data->names[dir_data->pos];
2314 union file_directory_info *info;
2315 struct stat st;
2316 ULONG name_len, start, dir_size, attributes;
2318 if (get_file_info( names->unix_name, &st, &attributes ) == -1)
2320 TRACE( "file no longer exists %s\n", names->unix_name );
2321 return STATUS_SUCCESS;
2323 if (is_ignored_file( &st ))
2325 TRACE( "ignoring file %s\n", names->unix_name );
2326 return STATUS_SUCCESS;
2328 start = dir_info_align( io->Information );
2329 dir_size = dir_info_size( class, 0 );
2330 if (start + dir_size > max_length) return STATUS_MORE_ENTRIES;
2332 max_length -= start + dir_size;
2333 name_len = wcslen( names->long_name ) * sizeof(WCHAR);
2334 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2335 if (*last_info && name_len > max_length) return STATUS_MORE_ENTRIES;
2337 info = (union file_directory_info *)((char *)info_ptr + start);
2338 info->dir.NextEntryOffset = 0;
2339 info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
2341 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2342 if (class != FileNamesInformation)
2344 if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */
2345 fill_file_info( &st, attributes, info, class );
2348 switch (class)
2350 case FileDirectoryInformation:
2351 info->dir.FileNameLength = name_len;
2352 break;
2354 case FileFullDirectoryInformation:
2355 info->full.EaSize = 0; /* FIXME */
2356 info->full.FileNameLength = name_len;
2357 break;
2359 case FileIdFullDirectoryInformation:
2360 info->id_full.EaSize = 0; /* FIXME */
2361 info->id_full.FileNameLength = name_len;
2362 break;
2364 case FileBothDirectoryInformation:
2365 info->both.EaSize = 0; /* FIXME */
2366 info->both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2367 memcpy( info->both.ShortName, names->short_name, info->both.ShortNameLength );
2368 info->both.FileNameLength = name_len;
2369 break;
2371 case FileIdBothDirectoryInformation:
2372 info->id_both.EaSize = 0; /* FIXME */
2373 info->id_both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2374 memcpy( info->id_both.ShortName, names->short_name, info->id_both.ShortNameLength );
2375 info->id_both.FileNameLength = name_len;
2376 break;
2378 case FileIdGlobalTxDirectoryInformation:
2379 info->id_tx.TxInfoFlags = 0;
2380 info->id_tx.FileNameLength = name_len;
2381 break;
2383 case FileNamesInformation:
2384 info->names.FileNameLength = name_len;
2385 break;
2387 default:
2388 assert(0);
2389 return 0;
2392 memcpy( (char *)info + dir_size, names->long_name, min( name_len, max_length ) );
2393 io->Information = start + dir_size + min( name_len, max_length );
2394 if (*last_info) (*last_info)->next = (char *)info - (char *)*last_info;
2395 *last_info = info;
2396 return name_len > max_length ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
2399 #ifdef VFAT_IOCTL_READDIR_BOTH
2401 /***********************************************************************
2402 * read_directory_vfat
2404 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2406 static NTSTATUS read_directory_data_vfat( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2408 char *short_name, *long_name;
2409 KERNEL_DIRENT de[2];
2410 NTSTATUS status = STATUS_NO_MEMORY;
2411 off_t old_pos = lseek( fd, 0, SEEK_CUR );
2413 lseek( fd, 0, SEEK_SET );
2415 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
2417 if (errno != ENOENT)
2419 status = STATUS_NOT_SUPPORTED;
2420 goto done;
2422 de[0].d_reclen = 0;
2425 if (!append_entry( data, ".", NULL, mask )) goto done;
2426 if (!append_entry( data, "..", NULL, mask )) goto done;
2428 while (de[0].d_reclen)
2430 if (strcmp( de[0].d_name, "." ) && strcmp( de[0].d_name, ".." ))
2432 if (de[1].d_name[0])
2434 short_name = de[0].d_name;
2435 long_name = de[1].d_name;
2437 else
2439 long_name = de[0].d_name;
2440 short_name = NULL;
2442 if (!append_entry( data, long_name, short_name, mask )) goto done;
2444 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
2446 status = STATUS_SUCCESS;
2447 done:
2448 lseek( fd, old_pos, SEEK_SET );
2449 return status;
2451 #endif /* VFAT_IOCTL_READDIR_BOTH */
2454 #ifdef HAVE_GETATTRLIST
2455 /***********************************************************************
2456 * read_directory_getattrlist
2458 * Read a single file from a directory by determining whether the file
2459 * identified by mask exists using getattrlist.
2461 static NTSTATUS read_directory_data_getattrlist( struct dir_data *data, const char *unix_name )
2463 struct attrlist attrlist;
2464 #include "pshpack4.h"
2465 struct
2467 u_int32_t length;
2468 struct attrreference name_reference;
2469 fsobj_type_t type;
2470 char name[NAME_MAX * 3 + 1];
2471 } buffer;
2472 #include "poppack.h"
2474 memset( &attrlist, 0, sizeof(attrlist) );
2475 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
2476 attrlist.commonattr = ATTR_CMN_NAME | ATTR_CMN_OBJTYPE;
2477 if (getattrlist( unix_name, &attrlist, &buffer, sizeof(buffer), FSOPT_NOFOLLOW ) == -1)
2478 return STATUS_NO_SUCH_FILE;
2479 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2480 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2481 if (buffer.type == VLNK)
2483 u_int32_t dummy;
2484 attrlist.commonattr = 0;
2485 if (getattrlist( unix_name, &attrlist, &dummy, sizeof(dummy), 0 ) == -1)
2486 return STATUS_NO_SUCH_FILE;
2489 TRACE( "found %s\n", buffer.name );
2491 if (!append_entry( data, buffer.name, NULL, NULL )) return STATUS_NO_MEMORY;
2493 return STATUS_SUCCESS;
2495 #endif /* HAVE_GETATTRLIST */
2498 /***********************************************************************
2499 * read_directory_stat
2501 * Read a single file from a directory by determining whether the file
2502 * identified by mask exists using stat.
2504 static NTSTATUS read_directory_data_stat( struct dir_data *data, const char *unix_name )
2506 struct stat st;
2508 /* if the file system is not case sensitive we can't find the actual name through stat() */
2509 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE;
2510 if (stat( unix_name, &st ) == -1) return STATUS_NO_SUCH_FILE;
2512 TRACE( "found %s\n", unix_name );
2514 if (!append_entry( data, unix_name, NULL, NULL )) return STATUS_NO_MEMORY;
2516 return STATUS_SUCCESS;
2520 /***********************************************************************
2521 * read_directory_readdir
2523 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2525 static NTSTATUS read_directory_data_readdir( struct dir_data *data, const UNICODE_STRING *mask )
2527 struct dirent *de;
2528 NTSTATUS status = STATUS_NO_MEMORY;
2529 DIR *dir = opendir( "." );
2531 if (!dir) return STATUS_NO_SUCH_FILE;
2533 if (!append_entry( data, ".", NULL, mask )) goto done;
2534 if (!append_entry( data, "..", NULL, mask )) goto done;
2535 while ((de = readdir( dir )))
2537 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2538 if (!append_entry( data, de->d_name, NULL, mask )) goto done;
2540 status = STATUS_SUCCESS;
2542 done:
2543 closedir( dir );
2544 return status;
2548 /***********************************************************************
2549 * read_directory_data
2551 * Read the full contents of a directory, using one of the above helper functions.
2553 static NTSTATUS read_directory_data( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2555 NTSTATUS status;
2557 #ifdef VFAT_IOCTL_READDIR_BOTH
2558 if (!(status = read_directory_data_vfat( data, fd, mask ))) return status;
2559 #endif
2561 if (!has_wildcard( mask ))
2563 /* convert the mask to a Unix name and check for it */
2564 char unix_name[MAX_DIR_ENTRY_LEN * 3 + 1];
2565 int ret = ntdll_wcstoumbs( mask->Buffer, mask->Length / sizeof(WCHAR),
2566 unix_name, sizeof(unix_name) - 1, TRUE );
2567 if (ret > 0)
2569 unix_name[ret] = 0;
2570 #ifdef HAVE_GETATTRLIST
2571 if (!(status = read_directory_data_getattrlist( data, unix_name ))) return status;
2572 #endif
2573 if (!(status = read_directory_data_stat( data, unix_name ))) return status;
2577 return read_directory_data_readdir( data, mask );
2581 /* compare file names for directory sorting */
2582 static int name_compare( const void *a, const void *b )
2584 const struct dir_data_names *file_a = (const struct dir_data_names *)a;
2585 const struct dir_data_names *file_b = (const struct dir_data_names *)b;
2586 int ret = wcsicmp( file_a->long_name, file_b->long_name );
2587 if (!ret) ret = wcscmp( file_a->long_name, file_b->long_name );
2588 return ret;
2592 /***********************************************************************
2593 * init_cached_dir_data
2595 * Initialize the cached directory contents.
2597 static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const UNICODE_STRING *mask )
2599 struct dir_data *data;
2600 struct stat st;
2601 NTSTATUS status;
2602 unsigned int i;
2604 if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY;
2606 if ((status = read_directory_data( data, fd, mask )))
2608 free_dir_data( data );
2609 return status;
2612 /* sort filenames, but not "." and ".." */
2613 i = 0;
2614 if (i < data->count && !strcmp( data->names[i].unix_name, "." )) i++;
2615 if (i < data->count && !strcmp( data->names[i].unix_name, ".." )) i++;
2616 if (i < data->count) qsort( data->names + i, data->count - i, sizeof(*data->names), name_compare );
2618 if (data->count)
2620 fstat( fd, &st );
2621 data->id.dev = st.st_dev;
2622 data->id.ino = st.st_ino;
2625 TRACE( "mask %s found %u files\n", debugstr_us( mask ), data->count );
2626 for (i = 0; i < data->count; i++)
2627 TRACE( "%s %s\n", debugstr_w(data->names[i].long_name), debugstr_w(data->names[i].short_name) );
2629 *data_ret = data;
2630 return data->count ? STATUS_SUCCESS : STATUS_NO_SUCH_FILE;
2634 /***********************************************************************
2635 * get_cached_dir_data
2637 * Retrieve the cached directory data, or initialize it if necessary.
2639 static unsigned int get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd,
2640 const UNICODE_STRING *mask )
2642 unsigned int i;
2643 int entry = -1, free_entries[16];
2644 unsigned int status;
2646 SERVER_START_REQ( get_directory_cache_entry )
2648 req->handle = wine_server_obj_handle( handle );
2649 wine_server_set_reply( req, free_entries, sizeof(free_entries) );
2650 if (!(status = wine_server_call( req ))) entry = reply->entry;
2652 for (i = 0; i < wine_server_reply_size( reply ) / sizeof(*free_entries); i++)
2654 int free_idx = free_entries[i];
2655 if (free_idx < dir_data_cache_size)
2657 free_dir_data( dir_data_cache[free_idx] );
2658 dir_data_cache[free_idx] = NULL;
2662 SERVER_END_REQ;
2664 if (status)
2666 if (status == STATUS_SHARING_VIOLATION) FIXME( "shared directory handle not supported yet\n" );
2667 return status;
2670 if (entry >= dir_data_cache_size)
2672 unsigned int size = max( dir_data_cache_initial_size, max( dir_data_cache_size * 2, entry + 1 ) );
2673 struct dir_data **new_cache = realloc( dir_data_cache, size * sizeof(*new_cache) );
2675 if (!new_cache) return STATUS_NO_MEMORY;
2676 memset( new_cache + dir_data_cache_size, 0, (size - dir_data_cache_size) * sizeof(*new_cache) );
2677 dir_data_cache = new_cache;
2678 dir_data_cache_size = size;
2681 if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask );
2683 *data_ret = dir_data_cache[entry];
2684 return status;
2688 /******************************************************************************
2689 * NtQueryDirectoryFile (NTDLL.@)
2691 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine,
2692 void *apc_context, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
2693 FILE_INFORMATION_CLASS info_class, BOOLEAN single_entry,
2694 UNICODE_STRING *mask, BOOLEAN restart_scan )
2696 int cwd, fd, needs_close;
2697 enum server_fd_type type;
2698 struct dir_data *data;
2699 unsigned int status;
2701 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2702 handle, event, apc_routine, apc_context, io, buffer,
2703 (int)length, info_class, single_entry, debugstr_us(mask),
2704 restart_scan);
2706 if (event || apc_routine)
2708 FIXME( "Unsupported yet option\n" );
2709 return STATUS_NOT_IMPLEMENTED;
2711 switch (info_class)
2713 case FileDirectoryInformation:
2714 case FileBothDirectoryInformation:
2715 case FileFullDirectoryInformation:
2716 case FileIdBothDirectoryInformation:
2717 case FileIdFullDirectoryInformation:
2718 case FileIdGlobalTxDirectoryInformation:
2719 case FileNamesInformation:
2720 if (length < dir_info_align( dir_info_size( info_class, 1 ))) return STATUS_INFO_LENGTH_MISMATCH;
2721 break;
2722 case FileObjectIdInformation:
2723 if (length != sizeof(FILE_OBJECTID_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2724 return STATUS_INVALID_INFO_CLASS;
2725 case FileQuotaInformation:
2726 if (length != sizeof(FILE_QUOTA_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2727 return STATUS_INVALID_INFO_CLASS;
2728 case FileReparsePointInformation:
2729 if (length != sizeof(FILE_REPARSE_POINT_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2730 return STATUS_INVALID_INFO_CLASS;
2731 default:
2732 return STATUS_INVALID_INFO_CLASS;
2734 if (!buffer) return STATUS_ACCESS_VIOLATION;
2736 if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, &type, NULL )))
2737 return status;
2739 if (type != FD_TYPE_DIR)
2741 if (needs_close) close( fd );
2742 return STATUS_INVALID_PARAMETER;
2745 io->Information = 0;
2747 mutex_lock( &dir_mutex );
2749 cwd = open( ".", O_RDONLY );
2750 if (fchdir( fd ) != -1)
2752 if (!(status = get_cached_dir_data( handle, &data, fd, mask )))
2754 union file_directory_info *last_info = NULL;
2756 if (restart_scan) data->pos = 0;
2758 while (!status && data->pos < data->count)
2760 status = get_dir_data_entry( data, buffer, io, length, info_class, &last_info );
2761 if (!status || status == STATUS_BUFFER_OVERFLOW) data->pos++;
2762 if (single_entry && last_info) break;
2765 if (!last_info) status = STATUS_NO_MORE_FILES;
2766 else if (status == STATUS_MORE_ENTRIES) status = STATUS_SUCCESS;
2768 io->Status = status;
2770 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
2772 else status = errno_to_status( errno );
2774 mutex_unlock( &dir_mutex );
2776 if (needs_close) close( fd );
2777 if (cwd != -1) close( cwd );
2778 TRACE( "=> %x (%ld)\n", status, io->Information );
2779 return status;
2783 /***********************************************************************
2784 * find_file_in_dir
2786 * Find a file in a directory the hard way, by doing a case-insensitive search.
2787 * The file found is appended to unix_name at pos.
2788 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2790 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
2791 BOOLEAN check_case )
2793 WCHAR buffer[MAX_DIR_ENTRY_LEN];
2794 BOOLEAN is_name_8_dot_3;
2795 DIR *dir;
2796 struct dirent *de;
2797 struct stat st;
2798 int ret;
2800 /* try a shortcut for this directory */
2802 unix_name[pos++] = '/';
2803 ret = ntdll_wcstoumbs( name, length, unix_name + pos, MAX_DIR_ENTRY_LEN + 1, TRUE );
2804 if (ret >= 0 && ret <= MAX_DIR_ENTRY_LEN)
2806 unix_name[pos + ret] = 0;
2807 if (!stat( unix_name, &st )) return STATUS_SUCCESS;
2809 if (check_case) goto not_found; /* we want an exact match */
2811 if (pos > 1) unix_name[pos - 1] = 0;
2812 else unix_name[1] = 0; /* keep the initial slash */
2814 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2816 is_name_8_dot_3 = is_legal_8dot3_name( name, length );
2817 #ifndef VFAT_IOCTL_READDIR_BOTH
2818 is_name_8_dot_3 = is_name_8_dot_3 && length >= 8 && name[4] == '~';
2819 #endif
2821 if (!is_name_8_dot_3 && !get_dir_case_sensitivity( unix_name )) goto not_found;
2823 /* now look for it through the directory */
2825 #ifdef VFAT_IOCTL_READDIR_BOTH
2826 if (is_name_8_dot_3)
2828 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
2829 if (fd != -1)
2831 KERNEL_DIRENT kde[2];
2833 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) != -1)
2835 unix_name[pos - 1] = '/';
2836 while (kde[0].d_reclen)
2838 if (kde[1].d_name[0])
2840 ret = ntdll_umbstowcs( kde[1].d_name, strlen(kde[1].d_name),
2841 buffer, MAX_DIR_ENTRY_LEN );
2842 if (ret == length && !wcsnicmp( buffer, name, ret ))
2844 strcpy( unix_name + pos, kde[1].d_name );
2845 close( fd );
2846 return STATUS_SUCCESS;
2849 ret = ntdll_umbstowcs( kde[0].d_name, strlen(kde[0].d_name),
2850 buffer, MAX_DIR_ENTRY_LEN );
2851 if (ret == length && !wcsnicmp( buffer, name, ret ))
2853 strcpy( unix_name + pos,
2854 kde[1].d_name[0] ? kde[1].d_name : kde[0].d_name );
2855 close( fd );
2856 return STATUS_SUCCESS;
2858 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) == -1)
2860 close( fd );
2861 goto not_found;
2864 /* if that did not work, restore previous state of unix_name */
2865 unix_name[pos - 1] = 0;
2867 close( fd );
2869 /* fall through to normal handling */
2871 #endif /* VFAT_IOCTL_READDIR_BOTH */
2873 if (!(dir = opendir( unix_name ))) return errno_to_status( errno );
2875 unix_name[pos - 1] = '/';
2876 while ((de = readdir( dir )))
2878 ret = ntdll_umbstowcs( de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
2879 if (ret == length && !wcsnicmp( buffer, name, ret ))
2881 strcpy( unix_name + pos, de->d_name );
2882 closedir( dir );
2883 return STATUS_SUCCESS;
2886 if (!is_name_8_dot_3) continue;
2888 if (!is_legal_8dot3_name( buffer, ret ))
2890 WCHAR short_nameW[12];
2891 ret = hash_short_file_name( buffer, ret, short_nameW );
2892 if (ret == length && !wcsnicmp( short_nameW, name, length ))
2894 strcpy( unix_name + pos, de->d_name );
2895 closedir( dir );
2896 return STATUS_SUCCESS;
2900 closedir( dir );
2902 not_found:
2903 unix_name[pos - 1] = 0;
2904 return STATUS_OBJECT_NAME_NOT_FOUND;
2908 #ifndef _WIN64
2910 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2911 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2912 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};
2913 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2914 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2915 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2916 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
2917 static const WCHAR syswow64W[] = {'s','y','s','w','o','w','6','4',0};
2918 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
2919 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2920 static const WCHAR syswow64_regeditW[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
2921 static const WCHAR windirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\',0};
2922 static const WCHAR syswow64dirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','w','o','w','6','4','\\'};
2924 static const WCHAR * const no_redirect[] =
2926 catrootW,
2927 catroot2W,
2928 driversstoreW,
2929 driversetcW,
2930 logfilesW,
2931 spoolW
2934 static struct file_identity windir, sysdir;
2936 static inline ULONG starts_with_path( const WCHAR *name, ULONG name_len, const WCHAR *prefix )
2938 ULONG len = wcslen( prefix );
2940 if (name_len < len) return 0;
2941 if (wcsnicmp( name, prefix, len )) return 0;
2942 if (name_len > len && name[len] != '\\') return 0;
2943 return len;
2946 static BOOL replace_path( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *str, ULONG prefix_len,
2947 const WCHAR *match, const WCHAR *replace )
2949 const WCHAR *name = attr->ObjectName->Buffer;
2950 ULONG match_len, replace_len, len = attr->ObjectName->Length / sizeof(WCHAR);
2951 WCHAR *p;
2953 if (!starts_with_path( name + prefix_len, len - prefix_len, match )) return FALSE;
2955 match_len = wcslen( match );
2956 replace_len = wcslen( replace );
2957 str->Length = (len + replace_len - match_len) * sizeof(WCHAR);
2958 str->MaximumLength = str->Length + sizeof(WCHAR);
2959 if (!(p = str->Buffer = malloc( str->MaximumLength ))) return FALSE;
2961 memcpy( p, name, prefix_len * sizeof(WCHAR) );
2962 p += prefix_len;
2963 memcpy( p, replace, replace_len * sizeof(WCHAR) );
2964 p += replace_len;
2965 name += prefix_len + match_len;
2966 len -= prefix_len + match_len;
2967 memcpy( p, name, len * sizeof(WCHAR) );
2968 p[len] = 0;
2969 attr->ObjectName = str;
2970 return TRUE;
2973 /***********************************************************************
2974 * init_redirects
2976 static void init_redirects(void)
2978 static const char system_dir[] = "/dosdevices/c:/windows/system32";
2979 char *dir;
2980 struct stat st;
2982 if (asprintf( &dir, "%s%s", config_dir, system_dir ) == -1) return;
2983 if (!stat( dir, &st ))
2985 sysdir.dev = st.st_dev;
2986 sysdir.ino = st.st_ino;
2988 *strrchr( dir, '/' ) = 0;
2989 if (!stat( dir, &st ))
2991 windir.dev = st.st_dev;
2992 windir.ino = st.st_ino;
2994 else ERR( "%s: %s\n", dir, strerror(errno) );
2995 free( dir );
2999 /***********************************************************************
3000 * get_redirect
3002 BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir )
3004 const WCHAR *name = attr->ObjectName->Buffer;
3005 unsigned int i, prefix_len = 0, len = attr->ObjectName->Length / sizeof(WCHAR);
3007 redir->Buffer = NULL;
3008 if (!NtCurrentTeb64()) return FALSE;
3009 if (!len) return FALSE;
3011 if (!attr->RootDirectory)
3013 prefix_len = wcslen( windirW );
3014 if (len < prefix_len || wcsnicmp( name, windirW, prefix_len )) return FALSE;
3016 else
3018 int fd, needs_close;
3019 struct stat st;
3021 if (server_get_unix_fd( attr->RootDirectory, 0, &fd, &needs_close, NULL, NULL )) return FALSE;
3022 fstat( fd, &st );
3023 if (needs_close) close( fd );
3024 if (!is_same_file( &windir, &st ))
3026 if (!is_same_file( &sysdir, &st )) return FALSE;
3027 if (NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE;
3028 if (name[0] == '\\') return FALSE;
3030 /* only check for paths that should NOT be redirected */
3031 for (i = 0; i < ARRAY_SIZE( no_redirect ); i++)
3032 if (starts_with_path( name, len, no_redirect[i] + 9 /* "system32\\" */)) return FALSE;
3034 /* redirect everything else */
3035 redir->Length = sizeof(syswow64dirW) + len * sizeof(WCHAR);
3036 redir->MaximumLength = redir->Length + sizeof(WCHAR);
3037 if (!(redir->Buffer = malloc( redir->MaximumLength ))) return FALSE;
3038 memcpy( redir->Buffer, syswow64dirW, sizeof(syswow64dirW) );
3039 memcpy( redir->Buffer + ARRAY_SIZE(syswow64dirW), name, len * sizeof(WCHAR) );
3040 redir->Buffer[redir->Length / sizeof(WCHAR)] = 0;
3041 attr->RootDirectory = 0;
3042 attr->ObjectName = redir;
3043 return TRUE;
3047 /* sysnative is redirected even when redirection is disabled */
3049 if (replace_path( attr, redir, prefix_len, sysnativeW, system32W )) return TRUE;
3051 if (NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE;
3053 for (i = 0; i < ARRAY_SIZE( no_redirect ); i++)
3054 if (starts_with_path( name + prefix_len, len - prefix_len, no_redirect[i] )) return FALSE;
3056 if (replace_path( attr, redir, prefix_len, system32W, syswow64W )) return TRUE;
3057 if (replace_path( attr, redir, prefix_len, regeditW, syswow64_regeditW )) return TRUE;
3058 return FALSE;
3061 #else /* _WIN64 */
3063 /* there are no redirects on 64-bit */
3064 BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir )
3066 redir->Buffer = NULL;
3067 return FALSE;
3070 #endif
3073 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
3075 /***********************************************************************
3076 * init_files
3078 void init_files(void)
3080 HANDLE key;
3082 #ifndef _WIN64
3083 if (is_old_wow64()) init_redirects();
3084 #endif
3085 /* a couple of directories that we don't want to return in directory searches */
3086 ignore_file( config_dir );
3087 ignore_file( "/dev" );
3088 ignore_file( "/proc" );
3089 #ifdef linux
3090 ignore_file( "/sys" );
3091 #endif
3092 /* retrieve initial umask */
3093 start_umask = umask( 0777 );
3094 umask( start_umask );
3096 if (!open_hkcu_key( "Software\\Wine", &key ))
3098 static WCHAR showdotfilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
3099 char tmp[80];
3100 DWORD dummy;
3101 UNICODE_STRING nameW;
3103 init_unicode_string( &nameW, showdotfilesW );
3104 if (!NtQueryValueKey( key, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
3106 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
3107 show_dot_files = IS_OPTION_TRUE( str[0] );
3109 NtClose( key );
3114 /******************************************************************************
3115 * get_dos_device
3117 * Get the Unix path of a DOS device.
3119 static NTSTATUS get_dos_device( char **unix_name, int start_pos )
3121 struct stat st;
3122 char *new_name, *dev = *unix_name + start_pos;
3124 /* special case for drive devices */
3125 if (dev[0] && dev[1] == ':' && !dev[2]) strcpy( dev + 1, "::" );
3127 if (strchr( dev, '/' )) goto failed;
3129 for (;;)
3131 if (!stat( *unix_name, &st ))
3133 TRACE( "-> %s\n", debugstr_a(*unix_name));
3134 return STATUS_SUCCESS;
3136 if (!dev) break;
3138 /* now try some defaults for it */
3139 if (!strcmp( dev, "aux" ))
3141 strcpy( dev, "com1" );
3142 continue;
3144 if (!strcmp( dev, "prn" ))
3146 strcpy( dev, "lpt1" );
3147 continue;
3150 new_name = NULL;
3151 if (dev[1] == ':' && dev[2] == ':') /* drive device */
3153 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
3154 new_name = get_default_drive_device( *unix_name );
3156 free( *unix_name );
3157 *unix_name = new_name;
3158 if (!new_name) return STATUS_BAD_DEVICE_TYPE;
3159 dev = NULL; /* last try */
3161 failed:
3162 free( *unix_name );
3163 *unix_name = NULL;
3164 return STATUS_BAD_DEVICE_TYPE;
3168 /* return the length of the DOS namespace prefix if any */
3169 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
3171 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
3172 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
3174 if (name->Length >= sizeof(nt_prefixW) &&
3175 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
3176 return ARRAY_SIZE( nt_prefixW );
3178 if (name->Length >= sizeof(dosdev_prefixW) &&
3179 !wcsnicmp( name->Buffer, dosdev_prefixW, ARRAY_SIZE( dosdev_prefixW )))
3180 return ARRAY_SIZE( dosdev_prefixW );
3182 return 0;
3186 /***********************************************************************
3187 * remove_last_componentA
3189 * Remove the last component of the path. Helper for find_drive_rootA.
3191 static inline unsigned int remove_last_componentA( const char *path, unsigned int len )
3193 int level = 0;
3195 while (level < 1)
3197 /* find start of the last path component */
3198 unsigned int prev = len;
3199 if (prev <= 1) break; /* reached root */
3200 while (prev > 1 && path[prev - 1] != '/') prev--;
3201 /* does removing it take us up a level? */
3202 if (len - prev != 1 || path[prev] != '.') /* not '.' */
3204 if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.') /* is it '..'? */
3205 level--;
3206 else
3207 level++;
3209 /* strip off trailing slashes */
3210 while (prev > 1 && path[prev - 1] == '/') prev--;
3211 len = prev;
3213 return len;
3217 /***********************************************************************
3218 * find_drive_rootA
3220 * Find a drive for which the root matches the beginning of the given path.
3221 * This can be used to translate a Unix path into a drive + DOS path.
3222 * Return value is the drive, or -1 on error. On success, ppath is modified
3223 * to point to the beginning of the DOS path.
3225 static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_ret )
3227 /* Starting with the full path, check if the device and inode match any of
3228 * the wine 'drives'. If not then remove the last path component and try
3229 * again. If the last component was a '..' then skip a normal component
3230 * since it's a directory that's ascended back out of.
3232 int drive;
3233 char *buffer;
3234 const char *path = *ppath;
3235 struct stat st;
3236 struct file_identity info[MAX_DOS_DRIVES];
3238 /* get device and inode of all drives */
3239 if (!get_drives_info( info )) return STATUS_OBJECT_PATH_NOT_FOUND;
3241 /* strip off trailing slashes */
3242 while (len > 1 && path[len - 1] == '/') len--;
3244 /* make a copy of the path */
3245 if (!(buffer = malloc( len + 1 ))) return STATUS_NO_MEMORY;
3246 memcpy( buffer, path, len );
3247 buffer[len] = 0;
3249 for (;;)
3251 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
3253 /* Find the drive */
3254 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
3256 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
3258 if (len == 1) len = 0; /* preserve root slash in returned path */
3259 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
3260 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
3261 *ppath += len;
3262 *drive_ret = drive;
3263 free( buffer );
3264 return STATUS_SUCCESS;
3268 if (len <= 1) break; /* reached root */
3269 len = remove_last_componentA( buffer, len );
3270 buffer[len] = 0;
3272 free( buffer );
3273 return STATUS_OBJECT_PATH_NOT_FOUND;
3277 /******************************************************************************
3278 * find_file_id
3280 * Recursively search directories from the dir queue for a given inode.
3282 static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev )
3284 unsigned int pos;
3285 DIR *dir;
3286 struct dirent *de;
3287 NTSTATUS status;
3288 struct stat st;
3289 char *name = *unix_name;
3291 while (!(status = next_dir_in_queue( name )))
3293 if (!(dir = opendir( name ))) continue;
3294 TRACE( "searching %s for %s\n", debugstr_a(name), wine_dbgstr_longlong(file_id) );
3295 pos = strlen( name );
3296 if (pos + MAX_DIR_ENTRY_LEN >= *len / sizeof(WCHAR))
3298 if (!(name = realloc( name, *len * 2 )))
3300 closedir( dir );
3301 return STATUS_NO_MEMORY;
3303 *len *= 2;
3304 *unix_name = name;
3306 name[pos++] = '/';
3307 while ((de = readdir( dir )))
3309 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
3310 strcpy( name + pos, de->d_name );
3311 if (lstat( name, &st ) == -1) continue;
3312 if (st.st_dev != dev) continue;
3313 if (st.st_ino == file_id)
3315 closedir( dir );
3316 return STATUS_SUCCESS;
3318 if (!S_ISDIR( st.st_mode )) continue;
3319 if ((status = add_dir_to_queue( name )) != STATUS_SUCCESS)
3321 closedir( dir );
3322 return status;
3325 closedir( dir );
3327 return status;
3331 /******************************************************************************
3332 * file_id_to_unix_file_name
3334 * Lookup a file from its file id instead of its name.
3336 static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **unix_name_ret,
3337 UNICODE_STRING *nt_name )
3339 enum server_fd_type type;
3340 int old_cwd, root_fd, needs_close;
3341 char *unix_name;
3342 ULONG len;
3343 NTSTATUS status;
3344 ULONGLONG file_id;
3345 struct stat st, root_st;
3347 nt_name->Buffer = NULL;
3348 if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
3349 if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
3350 memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
3352 len = 2 * MAX_DIR_ENTRY_LEN + 4;
3353 if (!(unix_name = malloc( len ))) return STATUS_NO_MEMORY;
3354 strcpy( unix_name, "." );
3356 if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3357 goto done;
3359 if (type != FD_TYPE_DIR)
3361 status = STATUS_OBJECT_TYPE_MISMATCH;
3362 goto done;
3365 fstat( root_fd, &root_st );
3366 if (root_st.st_ino == file_id) /* shortcut for "." */
3368 status = STATUS_SUCCESS;
3369 goto done;
3372 mutex_lock( &dir_mutex );
3373 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3375 /* shortcut for ".." */
3376 if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
3378 strcpy( unix_name, ".." );
3379 status = STATUS_SUCCESS;
3381 else
3383 status = add_dir_to_queue( "." );
3384 if (!status)
3385 status = find_file_id( &unix_name, &len, file_id, root_st.st_dev );
3386 if (!status) /* get rid of "./" prefix */
3387 memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 );
3388 flush_dir_queue();
3390 if (fchdir( old_cwd ) == -1) chdir( "/" );
3392 else status = errno_to_status( errno );
3393 mutex_unlock( &dir_mutex );
3394 if (old_cwd != -1) close( old_cwd );
3396 done:
3397 if (status == STATUS_SUCCESS)
3399 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name) );
3400 *unix_name_ret = unix_name;
3402 nt_name->MaximumLength = (strlen(unix_name) + 1) * sizeof(WCHAR);
3403 if ((nt_name->Buffer = malloc( nt_name->MaximumLength )))
3405 DWORD i, len = ntdll_umbstowcs( unix_name, strlen(unix_name), nt_name->Buffer, strlen(unix_name) );
3406 nt_name->Buffer[len] = 0;
3407 nt_name->Length = len * sizeof(WCHAR);
3408 for (i = 0; i < len; i++) if (nt_name->Buffer[i] == '/') nt_name->Buffer[i] = '\\';
3411 else
3413 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
3414 free( unix_name );
3416 if (needs_close) close( root_fd );
3417 return status;
3421 /******************************************************************************
3422 * lookup_unix_name
3424 * Helper for nt_to_unix_file_name
3426 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
3427 UINT disposition, BOOL is_unix )
3429 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, '/', 0 };
3430 NTSTATUS status;
3431 int ret;
3432 struct stat st;
3433 char *unix_name = *buffer;
3434 const WCHAR *ptr, *end;
3436 /* check syntax of individual components */
3438 for (ptr = name, end = name + name_len; ptr < end; ptr++)
3440 if (*ptr == '\\') return STATUS_OBJECT_NAME_INVALID; /* duplicate backslash */
3441 if (*ptr == '.')
3443 if (ptr + 1 == end) return STATUS_OBJECT_NAME_INVALID; /* "." element */
3444 if (ptr[1] == '\\') return STATUS_OBJECT_NAME_INVALID; /* "." element */
3445 if (ptr[1] == '.')
3447 if (ptr + 2 == end) return STATUS_OBJECT_NAME_INVALID; /* ".." element */
3448 if (ptr[2] == '\\') return STATUS_OBJECT_NAME_INVALID; /* ".." element */
3451 /* check for invalid characters (all chars except 0 are valid for unix) */
3452 for ( ; ptr < end && *ptr != '\\'; ptr++)
3454 if (!*ptr) return STATUS_OBJECT_NAME_INVALID;
3455 if (is_unix) continue;
3456 if (*ptr < 32 || wcschr( invalid_charsW, *ptr )) return STATUS_OBJECT_NAME_INVALID;
3460 /* try a shortcut first */
3462 unix_name[pos] = '/';
3463 ret = ntdll_wcstoumbs( name, name_len, unix_name + pos + 1, unix_len - pos - 1, TRUE );
3464 if (ret >= 0 && ret < unix_len - pos - 1)
3466 char *p;
3467 unix_name[pos + 1 + ret] = 0;
3468 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
3469 if (!stat( unix_name, &st ))
3471 if (disposition == FILE_CREATE) return STATUS_OBJECT_NAME_COLLISION;
3472 return STATUS_SUCCESS;
3476 if (!name_len) /* empty name -> drive root doesn't exist */
3477 return STATUS_OBJECT_PATH_NOT_FOUND;
3478 if (is_unix && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
3479 return STATUS_OBJECT_NAME_NOT_FOUND;
3481 /* now do it component by component */
3483 while (name_len)
3485 const WCHAR *end, *next;
3487 end = name;
3488 while (end < name + name_len && *end != '\\') end++;
3489 next = end;
3490 if (next < name + name_len) next++;
3491 name_len -= next - name;
3493 /* grow the buffer if needed */
3495 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 3)
3497 char *new_name;
3498 unix_len += 2 * MAX_DIR_ENTRY_LEN;
3499 if (!(new_name = realloc( unix_name, unix_len ))) return STATUS_NO_MEMORY;
3500 unix_name = *buffer = new_name;
3503 status = find_file_in_dir( unix_name, pos, name, end - name, is_unix );
3505 /* if this is the last element, not finding it is not necessarily fatal */
3506 if (!name_len)
3508 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
3510 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3512 ret = ntdll_wcstoumbs( name, end - name, unix_name + pos + 1, MAX_DIR_ENTRY_LEN + 1, TRUE );
3513 if (ret > 0 && ret <= MAX_DIR_ENTRY_LEN)
3515 unix_name[pos] = '/';
3516 pos += ret + 1;
3517 if (end < next) unix_name[pos++] = '/';
3518 unix_name[pos] = 0;
3519 status = STATUS_NO_SUCH_FILE;
3520 break;
3524 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
3526 status = STATUS_OBJECT_NAME_COLLISION;
3528 if (end < next) strcat( unix_name, "/" );
3530 else if (status == STATUS_OBJECT_NAME_NOT_FOUND) status = STATUS_OBJECT_PATH_NOT_FOUND;
3532 if (status != STATUS_SUCCESS) break;
3534 pos += strlen( unix_name + pos );
3535 name = next;
3538 return status;
3542 /******************************************************************************
3543 * nt_to_unix_file_name_no_root
3545 static NTSTATUS nt_to_unix_file_name_no_root( const UNICODE_STRING *nameW, char **unix_name_ret,
3546 UINT disposition )
3548 static const WCHAR unixW[] = {'u','n','i','x'};
3549 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
3551 NTSTATUS status = STATUS_SUCCESS;
3552 const WCHAR *name;
3553 struct stat st;
3554 char *unix_name;
3555 int pos, ret, name_len, unix_len, prefix_len;
3556 WCHAR prefix[MAX_DIR_ENTRY_LEN + 1];
3557 BOOLEAN is_unix = FALSE;
3559 name = nameW->Buffer;
3560 name_len = nameW->Length / sizeof(WCHAR);
3562 if (!name_len || name[0] != '\\') return STATUS_OBJECT_PATH_SYNTAX_BAD;
3564 if (!(pos = get_dos_prefix_len( nameW )))
3565 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
3567 name += pos;
3568 name_len -= pos;
3570 if (!name_len) return STATUS_OBJECT_NAME_INVALID;
3572 /* check for sub-directory */
3573 for (pos = 0; pos < name_len && pos <= MAX_DIR_ENTRY_LEN; pos++)
3575 if (name[pos] == '\\') break;
3576 if (name[pos] < 32 || wcschr( invalid_charsW, name[pos] ))
3577 return STATUS_OBJECT_NAME_INVALID;
3578 prefix[pos] = (name[pos] >= 'A' && name[pos] <= 'Z') ? name[pos] + 'a' - 'A' : name[pos];
3580 if (pos > MAX_DIR_ENTRY_LEN) return STATUS_OBJECT_NAME_INVALID;
3582 if (pos >= 4 && !memcmp( prefix, unixW, sizeof(unixW) ))
3584 /* allow slash for unix namespace */
3585 if (pos > 4 && prefix[4] == '/') pos = 4;
3586 is_unix = pos == 4;
3588 prefix_len = pos;
3589 prefix[prefix_len] = 0;
3591 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3592 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
3593 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3594 strcpy( unix_name, config_dir );
3595 strcat( unix_name, "/dosdevices/" );
3596 pos = strlen(unix_name);
3598 ret = ntdll_wcstoumbs( prefix, prefix_len, unix_name + pos, unix_len - pos - 1, TRUE );
3599 if (ret <= 0)
3601 free( unix_name );
3602 return STATUS_OBJECT_NAME_INVALID;
3605 if (prefix_len == name_len) /* no subdir, plain DOS device */
3607 unix_name[pos + ret] = 0;
3608 *unix_name_ret = unix_name;
3609 return get_dos_device( unix_name_ret, pos );
3611 pos += ret;
3613 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3615 if (wcschr( prefix, '/' ))
3617 free( unix_name );
3618 return STATUS_OBJECT_PATH_NOT_FOUND;
3621 if (prefix_len != 2 || prefix[1] != ':')
3623 unix_name[pos] = 0;
3624 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
3626 if (!is_unix)
3628 free( unix_name );
3629 return STATUS_BAD_DEVICE_TYPE;
3631 pos = 0; /* fall back to unix root */
3635 prefix_len++; /* skip initial backslash */
3636 if (name_len > prefix_len && name[prefix_len] == '\\') prefix_len++; /* allow a second backslash */
3637 name += prefix_len;
3638 name_len -= prefix_len;
3640 status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, is_unix );
3641 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3643 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
3644 *unix_name_ret = unix_name;
3646 else
3648 TRACE( "%s not found in %s\n", debugstr_w(name), debugstr_an(unix_name, pos) );
3649 free( unix_name );
3651 return status;
3655 /******************************************************************************
3656 * nt_to_unix_file_name
3658 * Convert a file name from NT namespace to Unix namespace.
3660 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3661 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3662 * returned, but the unix name is still filled in properly.
3664 NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition )
3666 enum server_fd_type type;
3667 int old_cwd, root_fd, needs_close;
3668 const WCHAR *name;
3669 char *unix_name;
3670 int name_len, unix_len;
3671 NTSTATUS status;
3673 if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
3674 return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition );
3676 name = attr->ObjectName->Buffer;
3677 name_len = attr->ObjectName->Length / sizeof(WCHAR);
3679 if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER;
3681 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3682 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3683 unix_name[0] = '.';
3685 if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3687 if (type != FD_TYPE_DIR)
3689 if (needs_close) close( root_fd );
3690 status = STATUS_BAD_DEVICE_TYPE;
3692 else
3694 mutex_lock( &dir_mutex );
3695 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3697 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1, disposition, FALSE );
3698 if (fchdir( old_cwd ) == -1) chdir( "/" );
3700 else status = errno_to_status( errno );
3701 mutex_unlock( &dir_mutex );
3702 if (old_cwd != -1) close( old_cwd );
3703 if (needs_close) close( root_fd );
3706 else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
3708 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3710 TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
3711 *name_ret = unix_name;
3713 else
3715 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3716 free( unix_name );
3718 return status;
3722 /******************************************************************************
3723 * wine_nt_to_unix_file_name
3725 * Convert a file name from NT namespace to Unix namespace.
3727 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3728 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3729 * returned, but the unix name is still filled in properly.
3731 NTSTATUS WINAPI wine_nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char *nameA, ULONG *size,
3732 UINT disposition )
3734 char *buffer = NULL;
3735 NTSTATUS status;
3736 UNICODE_STRING redir;
3737 OBJECT_ATTRIBUTES new_attr = *attr;
3739 get_redirect( &new_attr, &redir );
3740 status = nt_to_unix_file_name( &new_attr, &buffer, disposition );
3742 if (buffer)
3744 struct stat st1, st2;
3745 char *name = buffer;
3747 /* remove dosdevices prefix for z: drive if it points to the Unix root */
3748 if (!strncmp( buffer, config_dir, strlen(config_dir) ) &&
3749 !strncmp( buffer + strlen(config_dir), "/dosdevices/z:/", 15 ))
3751 char *p = buffer + strlen(config_dir) + 14;
3752 *p = 0;
3753 if (!stat( buffer, &st1 ) && !stat( "/", &st2 ) &&
3754 st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
3755 name = p;
3756 *p = '/';
3759 if (*size > strlen(name)) strcpy( nameA, name );
3760 else status = STATUS_BUFFER_TOO_SMALL;
3761 *size = strlen(name) + 1;
3762 free( buffer );
3764 free( redir.Buffer );
3765 return status;
3769 /******************************************************************
3770 * collapse_path
3772 * Get rid of . and .. components in the path.
3774 static void collapse_path( WCHAR *path )
3776 WCHAR *p, *start, *next;
3778 /* convert every / into a \ */
3779 for (p = path; *p; p++) if (*p == '/') *p = '\\';
3781 p = path + 4;
3782 while (*p && *p != '\\') p++;
3783 start = p + 1;
3785 /* collapse duplicate backslashes */
3786 next = start;
3787 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
3788 *next = 0;
3790 p = start;
3791 while (*p)
3793 if (*p == '.')
3795 switch(p[1])
3797 case '\\': /* .\ component */
3798 next = p + 2;
3799 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3800 continue;
3801 case 0: /* final . */
3802 if (p > start) p--;
3803 *p = 0;
3804 continue;
3805 case '.':
3806 if (p[2] == '\\') /* ..\ component */
3808 next = p + 3;
3809 if (p > start)
3811 p--;
3812 while (p > start && p[-1] != '\\') p--;
3814 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3815 continue;
3817 else if (!p[2]) /* final .. */
3819 if (p > start)
3821 p--;
3822 while (p > start && p[-1] != '\\') p--;
3823 if (p > start) p--;
3825 *p = 0;
3826 continue;
3828 break;
3831 /* skip to the next component */
3832 while (*p && *p != '\\') p++;
3833 if (*p == '\\')
3835 /* remove last dot in previous dir name */
3836 if (p > start && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
3837 else p++;
3841 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3842 while (p > start && (p[-1] == ' ' || p[-1] == '.')) p--;
3843 *p = 0;
3847 /******************************************************************
3848 * unix_to_nt_file_name
3850 NTSTATUS unix_to_nt_file_name( const char *name, WCHAR **nt )
3852 static const WCHAR unix_prefixW[] = {'\\','?','?','\\','u','n','i','x',0};
3853 WCHAR dos_prefixW[] = {'\\','?','?','\\','A',':','\\',0};
3854 const WCHAR *prefix = unix_prefixW;
3855 unsigned int lenW, lenA = strlen(name);
3856 const char *path = name;
3857 NTSTATUS status;
3858 WCHAR *buffer;
3859 int drive;
3861 status = find_drive_rootA( &path, lenA, &drive );
3862 lenA -= path - name;
3864 if (status == STATUS_SUCCESS)
3866 while (lenA && path[0] == '/') { lenA--; path++; }
3867 dos_prefixW[4] += drive;
3868 prefix = dos_prefixW;
3870 else if (status != STATUS_OBJECT_PATH_NOT_FOUND) return status;
3872 lenW = wcslen( prefix );
3873 if (!(buffer = malloc( (lenA + lenW + 1) * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
3874 memcpy( buffer, prefix, lenW * sizeof(WCHAR) );
3875 lenW += ntdll_umbstowcs( path, lenA, buffer + lenW, lenA );
3876 buffer[lenW] = 0;
3877 collapse_path( buffer );
3878 *nt = buffer;
3879 return STATUS_SUCCESS;
3883 /******************************************************************
3884 * wine_unix_to_nt_file_name
3886 NTSTATUS WINAPI wine_unix_to_nt_file_name( const char *name, WCHAR *buffer, ULONG *size )
3888 WCHAR *nt_name = NULL;
3889 NTSTATUS status;
3891 if (name[0] != '/') return STATUS_INVALID_PARAMETER; /* relative paths are not supported */
3893 status = unix_to_nt_file_name( name, &nt_name );
3894 if (nt_name)
3896 if (*size > wcslen(nt_name)) wcscpy( buffer, nt_name );
3897 else status = STATUS_BUFFER_TOO_SMALL;
3898 *size = wcslen(nt_name) + 1;
3899 free( nt_name );
3901 return status;
3905 /***********************************************************************
3906 * get_full_path
3908 * Simplified version of RtlGetFullPathName_U.
3910 NTSTATUS get_full_path( const WCHAR *name, const WCHAR *curdir, WCHAR **path )
3912 static const WCHAR uncW[] = {'\\','?','?','\\','U','N','C','\\',0};
3913 static const WCHAR devW[] = {'\\','?','?','\\',0};
3914 static const WCHAR unixW[] = {'u','n','i','x'};
3915 WCHAR *ret, root[] = {'\\','?','?','\\','C',':','\\',0};
3916 NTSTATUS status = STATUS_SUCCESS;
3917 const WCHAR *prefix;
3919 if (IS_SEPARATOR(name[0]) && IS_SEPARATOR(name[1])) /* \\ prefix */
3921 if ((name[2] == '.' || name[2] == '?') && IS_SEPARATOR(name[3])) /* \\?\ device */
3923 name += 4;
3924 if (!wcsnicmp( name, unixW, 4 ) && IS_SEPARATOR(name[4])) /* \\?\unix special name */
3926 char *unix_name;
3927 name += 4;
3928 unix_name = malloc( wcslen(name) * 3 + 1 );
3929 ntdll_wcstoumbs( name, wcslen(name) + 1, unix_name, wcslen(name) * 3 + 1, FALSE );
3930 status = unix_to_nt_file_name( unix_name, path );
3931 free( unix_name );
3932 return status;
3934 prefix = devW;
3936 else prefix = uncW; /* UNC path */
3938 else if (IS_SEPARATOR(name[0])) /* absolute path */
3940 root[4] = curdir[4];
3941 prefix = root;
3943 else if (name[0] && name[1] == ':') /* drive letter */
3945 root[4] = towupper(name[0]);
3946 name += 2;
3947 prefix = root;
3949 else prefix = curdir; /* relative path */
3951 ret = malloc( (wcslen(prefix) + wcslen(name) + 1) * sizeof(WCHAR) );
3952 wcscpy( ret, prefix );
3953 wcscat( ret, name );
3954 collapse_path( ret );
3955 *path = ret;
3956 return STATUS_SUCCESS;
3960 /***********************************************************************
3961 * unmount_device
3963 * Unmount the specified device.
3965 static NTSTATUS unmount_device( HANDLE handle )
3967 NTSTATUS status;
3968 int unix_fd, needs_close;
3970 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
3972 struct stat st;
3973 char *mount_point = NULL;
3975 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
3976 status = STATUS_INVALID_PARAMETER;
3977 else
3979 if ((mount_point = get_device_mount_point( st.st_rdev )))
3981 #ifdef __APPLE__
3982 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
3983 #else
3984 static const char umount[] = "umount >/dev/null 2>&1 ";
3985 #endif
3986 char *cmd;
3987 if (asprintf( &cmd, "%s%s", umount, mount_point ) != -1)
3989 system( cmd );
3990 free( cmd );
3991 #ifdef linux
3992 /* umount will fail to release the loop device since we still have
3993 a handle to it, so we release it here */
3994 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3995 #endif
3997 free( mount_point );
4000 if (needs_close) close( unix_fd );
4002 return status;
4006 /******************************************************************************
4007 * open_unix_file
4009 * Helper for NtCreateFile that takes a Unix path.
4011 NTSTATUS open_unix_file( HANDLE *handle, const char *unix_name, ACCESS_MASK access,
4012 OBJECT_ATTRIBUTES *attr, ULONG attributes, ULONG sharing, ULONG disposition,
4013 ULONG options, void *ea_buffer, ULONG ea_length )
4015 struct object_attributes *objattr;
4016 unsigned int status;
4017 data_size_t len;
4019 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
4021 SERVER_START_REQ( create_file )
4023 req->access = access;
4024 req->sharing = sharing;
4025 req->create = disposition;
4026 req->options = options;
4027 req->attrs = attributes;
4028 wine_server_add_data( req, objattr, len );
4029 wine_server_add_data( req, unix_name, strlen(unix_name) );
4030 status = wine_server_call( req );
4031 *handle = wine_server_ptr_handle( reply->handle );
4033 SERVER_END_REQ;
4034 free( objattr );
4035 return status;
4039 /******************************************************************************
4040 * NtCreateFile (NTDLL.@)
4042 NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
4043 IO_STATUS_BLOCK *io, LARGE_INTEGER *alloc_size,
4044 ULONG attributes, ULONG sharing, ULONG disposition,
4045 ULONG options, void *ea_buffer, ULONG ea_length )
4047 OBJECT_ATTRIBUTES new_attr;
4048 UNICODE_STRING nt_name;
4049 char *unix_name;
4050 BOOL name_hidden = FALSE;
4051 BOOL created = FALSE;
4052 unsigned int status;
4054 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
4055 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
4056 handle, (int)access, debugstr_us(attr->ObjectName), (int)attr->Attributes,
4057 attr->RootDirectory, attr->SecurityDescriptor, io, alloc_size,
4058 (int)attributes, (int)sharing, (int)disposition, (int)options, ea_buffer, (int)ea_length );
4060 *handle = 0;
4061 if (!attr || !attr->ObjectName) return STATUS_INVALID_PARAMETER;
4063 if (alloc_size) FIXME( "alloc_size not supported\n" );
4065 new_attr = *attr;
4066 if (options & FILE_OPEN_BY_FILE_ID)
4068 status = file_id_to_unix_file_name( &new_attr, &unix_name, &nt_name );
4069 if (!status) new_attr.ObjectName = &nt_name;
4071 else
4073 get_redirect( &new_attr, &nt_name );
4074 status = nt_to_unix_file_name( &new_attr, &unix_name, disposition );
4077 if (status == STATUS_BAD_DEVICE_TYPE)
4079 status = server_open_file_object( handle, access, &new_attr, sharing, options );
4080 if (status == STATUS_SUCCESS) io->Information = FILE_OPENED;
4081 free( nt_name.Buffer );
4082 return io->Status = status;
4085 if (status == STATUS_NO_SUCH_FILE && disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
4087 created = TRUE;
4088 status = STATUS_SUCCESS;
4091 if (status == STATUS_SUCCESS)
4093 name_hidden = is_hidden_file( unix_name );
4094 status = open_unix_file( handle, unix_name, access, &new_attr, attributes,
4095 sharing, disposition, options, ea_buffer, ea_length );
4096 free( unix_name );
4098 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
4100 if (status == STATUS_SUCCESS)
4102 if (created) io->Information = FILE_CREATED;
4103 else switch(disposition)
4105 case FILE_SUPERSEDE:
4106 io->Information = FILE_SUPERSEDED;
4107 break;
4108 case FILE_CREATE:
4109 io->Information = FILE_CREATED;
4110 break;
4111 case FILE_OPEN:
4112 case FILE_OPEN_IF:
4113 io->Information = FILE_OPENED;
4114 break;
4115 case FILE_OVERWRITE:
4116 case FILE_OVERWRITE_IF:
4117 io->Information = FILE_OVERWRITTEN;
4118 break;
4121 if (io->Information == FILE_CREATED &&
4122 ((attributes & XATTR_ATTRIBS_MASK) || name_hidden))
4124 int fd, needs_close;
4126 /* set any DOS extended attributes */
4127 if (!server_get_unix_fd( *handle, 0, &fd, &needs_close, NULL, NULL ))
4129 if (fd_set_dos_attrib( fd, attributes, TRUE ) == -1 && errno != ENOTSUP)
4130 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)",
4131 errno, strerror( errno ) );
4132 if (needs_close) close( fd );
4136 else if (status == STATUS_TOO_MANY_OPENED_FILES)
4138 static int once;
4139 if (!once++) ERR_(winediag)( "Too many open files, ulimit -n probably needs to be increased\n" );
4142 free( nt_name.Buffer );
4143 return io->Status = status;
4147 /******************************************************************************
4148 * NtOpenFile (NTDLL.@)
4150 NTSTATUS WINAPI NtOpenFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
4151 IO_STATUS_BLOCK *io, ULONG sharing, ULONG options )
4153 return NtCreateFile( handle, access, attr, io, NULL, 0, sharing, FILE_OPEN, options, NULL, 0 );
4157 /******************************************************************************
4158 * NtCreateMailslotFile (NTDLL.@)
4160 NTSTATUS WINAPI NtCreateMailslotFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
4161 IO_STATUS_BLOCK *io, ULONG options, ULONG quota, ULONG msg_size,
4162 LARGE_INTEGER *timeout )
4164 unsigned int status;
4165 data_size_t len;
4166 struct object_attributes *objattr;
4168 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
4169 handle, (int)access, attr, io, (int)options, (int)quota, (int)msg_size, timeout );
4171 *handle = 0;
4172 if (!attr) return STATUS_INVALID_PARAMETER;
4173 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
4175 SERVER_START_REQ( create_mailslot )
4177 req->access = access;
4178 req->max_msgsize = msg_size;
4179 req->read_timeout = timeout ? timeout->QuadPart : -1;
4180 wine_server_add_data( req, objattr, len );
4181 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
4183 SERVER_END_REQ;
4185 free( objattr );
4186 return status;
4190 /******************************************************************
4191 * NtCreateNamedPipeFile (NTDLL.@)
4193 NTSTATUS WINAPI NtCreateNamedPipeFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
4194 IO_STATUS_BLOCK *io, ULONG sharing, ULONG dispo, ULONG options,
4195 ULONG pipe_type, ULONG read_mode, ULONG completion_mode,
4196 ULONG max_inst, ULONG inbound_quota, ULONG outbound_quota,
4197 LARGE_INTEGER *timeout )
4199 unsigned int status;
4200 data_size_t len;
4201 struct object_attributes *objattr;
4203 *handle = 0;
4204 if (!attr) return STATUS_INVALID_PARAMETER;
4206 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
4207 handle, (int)access, debugstr_us(attr->ObjectName), io, (int)sharing, (int)dispo,
4208 (int)options, (int)pipe_type, (int)read_mode, (int)completion_mode, (int)max_inst,
4209 (int)inbound_quota, (int)outbound_quota, timeout );
4211 /* assume we only get relative timeout */
4212 if (timeout && timeout->QuadPart > 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout->QuadPart) );
4214 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
4216 SERVER_START_REQ( create_named_pipe )
4218 req->access = access;
4219 req->options = options;
4220 req->sharing = sharing;
4221 req->flags =
4222 (pipe_type ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0) |
4223 (read_mode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0) |
4224 (completion_mode ? NAMED_PIPE_NONBLOCKING_MODE : 0);
4225 req->disposition = dispo;
4226 req->maxinstances = max_inst;
4227 req->outsize = outbound_quota;
4228 req->insize = inbound_quota;
4229 req->timeout = timeout ? timeout->QuadPart : 0ULL;
4230 wine_server_add_data( req, objattr, len );
4231 if (!(status = wine_server_call( req )))
4233 *handle = wine_server_ptr_handle( reply->handle );
4234 io->Information = reply->created ? FILE_CREATED : FILE_OPENED;
4237 SERVER_END_REQ;
4239 free( objattr );
4240 return io->Status = status;
4244 /******************************************************************
4245 * NtDeleteFile (NTDLL.@)
4247 NTSTATUS WINAPI NtDeleteFile( OBJECT_ATTRIBUTES *attr )
4249 HANDLE handle;
4250 NTSTATUS status;
4251 char *unix_name;
4252 UNICODE_STRING nt_name;
4253 OBJECT_ATTRIBUTES new_attr = *attr;
4255 get_redirect( &new_attr, &nt_name );
4256 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
4258 if (!(status = open_unix_file( &handle, unix_name, GENERIC_READ | GENERIC_WRITE | DELETE, &new_attr,
4259 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN,
4260 FILE_DELETE_ON_CLOSE, NULL, 0 )))
4261 NtClose( handle );
4262 free( unix_name );
4264 free( nt_name.Buffer );
4265 return status;
4269 /******************************************************************************
4270 * NtQueryFullAttributesFile (NTDLL.@)
4272 NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr,
4273 FILE_NETWORK_OPEN_INFORMATION *info )
4275 char *unix_name;
4276 unsigned int status;
4277 UNICODE_STRING redir;
4278 OBJECT_ATTRIBUTES new_attr = *attr;
4280 get_redirect( &new_attr, &redir );
4281 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
4283 ULONG attributes;
4284 struct stat st;
4286 if (get_file_info( unix_name, &st, &attributes ) == -1)
4287 status = errno_to_status( errno );
4288 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4289 status = STATUS_INVALID_INFO_CLASS;
4290 else
4292 FILE_BASIC_INFORMATION basic;
4293 FILE_STANDARD_INFORMATION std;
4295 fill_file_info( &st, attributes, &basic, FileBasicInformation );
4296 fill_file_info( &st, attributes, &std, FileStandardInformation );
4298 info->CreationTime = basic.CreationTime;
4299 info->LastAccessTime = basic.LastAccessTime;
4300 info->LastWriteTime = basic.LastWriteTime;
4301 info->ChangeTime = basic.ChangeTime;
4302 info->AllocationSize = std.AllocationSize;
4303 info->EndOfFile = std.EndOfFile;
4304 info->FileAttributes = basic.FileAttributes;
4306 free( unix_name );
4308 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
4309 free( redir.Buffer );
4310 return status;
4314 /******************************************************************************
4315 * NtQueryAttributesFile (NTDLL.@)
4317 NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC_INFORMATION *info )
4319 char *unix_name;
4320 unsigned int status;
4321 UNICODE_STRING redir;
4322 OBJECT_ATTRIBUTES new_attr = *attr;
4324 get_redirect( &new_attr, &redir );
4325 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
4327 ULONG attributes;
4328 struct stat st;
4330 if (get_file_info( unix_name, &st, &attributes ) == -1)
4331 status = errno_to_status( errno );
4332 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4333 status = STATUS_INVALID_INFO_CLASS;
4334 else
4335 status = fill_file_info( &st, attributes, info, FileBasicInformation );
4336 free( unix_name );
4338 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
4339 free( redir.Buffer );
4340 return status;
4344 /******************************************************************************
4345 * NtQueryInformationFile (NTDLL.@)
4347 NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4348 void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
4350 static const size_t info_sizes[FileMaximumInformation] =
4353 sizeof(FILE_DIRECTORY_INFORMATION), /* FileDirectoryInformation */
4354 sizeof(FILE_FULL_DIRECTORY_INFORMATION), /* FileFullDirectoryInformation */
4355 sizeof(FILE_BOTH_DIRECTORY_INFORMATION), /* FileBothDirectoryInformation */
4356 sizeof(FILE_BASIC_INFORMATION), /* FileBasicInformation */
4357 sizeof(FILE_STANDARD_INFORMATION), /* FileStandardInformation */
4358 sizeof(FILE_INTERNAL_INFORMATION), /* FileInternalInformation */
4359 sizeof(FILE_EA_INFORMATION), /* FileEaInformation */
4360 0, /* FileAccessInformation */
4361 sizeof(FILE_NAME_INFORMATION), /* FileNameInformation */
4362 sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
4363 0, /* FileLinkInformation */
4364 sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR), /* FileNamesInformation */
4365 sizeof(FILE_DISPOSITION_INFORMATION), /* FileDispositionInformation */
4366 sizeof(FILE_POSITION_INFORMATION), /* FilePositionInformation */
4367 sizeof(FILE_FULL_EA_INFORMATION), /* FileFullEaInformation */
4368 0, /* FileModeInformation */
4369 sizeof(FILE_ALIGNMENT_INFORMATION), /* FileAlignmentInformation */
4370 sizeof(FILE_ALL_INFORMATION), /* FileAllInformation */
4371 sizeof(FILE_ALLOCATION_INFORMATION), /* FileAllocationInformation */
4372 sizeof(FILE_END_OF_FILE_INFORMATION), /* FileEndOfFileInformation */
4373 0, /* FileAlternateNameInformation */
4374 sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
4375 sizeof(FILE_PIPE_INFORMATION), /* FilePipeInformation */
4376 sizeof(FILE_PIPE_LOCAL_INFORMATION), /* FilePipeLocalInformation */
4377 0, /* FilePipeRemoteInformation */
4378 sizeof(FILE_MAILSLOT_QUERY_INFORMATION), /* FileMailslotQueryInformation */
4379 0, /* FileMailslotSetInformation */
4380 0, /* FileCompressionInformation */
4381 0, /* FileObjectIdInformation */
4382 0, /* FileCompletionInformation */
4383 0, /* FileMoveClusterInformation */
4384 0, /* FileQuotaInformation */
4385 0, /* FileReparsePointInformation */
4386 sizeof(FILE_NETWORK_OPEN_INFORMATION), /* FileNetworkOpenInformation */
4387 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), /* FileAttributeTagInformation */
4388 0, /* FileTrackingInformation */
4389 0, /* FileIdBothDirectoryInformation */
4390 0, /* FileIdFullDirectoryInformation */
4391 0, /* FileValidDataLengthInformation */
4392 0, /* FileShortNameInformation */
4393 0, /* FileIoCompletionNotificationInformation, */
4394 0, /* FileIoStatusBlockRangeInformation */
4395 0, /* FileIoPriorityHintInformation */
4396 0, /* FileSfioReserveInformation */
4397 0, /* FileSfioVolumeInformation */
4398 0, /* FileHardLinkInformation */
4399 0, /* FileProcessIdsUsingFileInformation */
4400 0, /* FileNormalizedNameInformation */
4401 0, /* FileNetworkPhysicalNameInformation */
4402 0, /* FileIdGlobalTxDirectoryInformation */
4403 0, /* FileIsRemoteDeviceInformation */
4404 0, /* FileAttributeCacheInformation */
4405 0, /* FileNumaNodeInformation */
4406 0, /* FileStandardLinkInformation */
4407 0, /* FileRemoteProtocolInformation */
4408 0, /* FileRenameInformationBypassAccessCheck */
4409 0, /* FileLinkInformationBypassAccessCheck */
4410 0, /* FileVolumeNameInformation */
4411 sizeof(FILE_ID_INFORMATION), /* FileIdInformation */
4412 0, /* FileIdExtdDirectoryInformation */
4413 0, /* FileReplaceCompletionInformation */
4414 0, /* FileHardLinkFullIdInformation */
4415 0, /* FileIdExtdBothDirectoryInformation */
4416 0, /* FileDispositionInformationEx */
4417 0, /* FileRenameInformationEx */
4418 0, /* FileRenameInformationExBypassAccessCheck */
4419 0, /* FileDesiredStorageClassInformation */
4420 sizeof(FILE_STAT_INFORMATION), /* FileStatInformation */
4421 0, /* FileMemoryPartitionInformation */
4422 0, /* FileStatLxInformation */
4423 0, /* FileCaseSensitiveInformation */
4424 0, /* FileLinkInformationEx */
4425 0, /* FileLinkInformationExBypassAccessCheck */
4426 0, /* FileStorageReserveIdInformation */
4427 0, /* FileCaseSensitiveInformationForceAccessCheck */
4428 0, /* FileKnownFolderInformation */
4431 struct stat st;
4432 int fd, needs_close = FALSE;
4433 ULONG attr;
4434 unsigned int options;
4435 unsigned int status;
4437 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, (int)len, class);
4439 io->Information = 0;
4441 if (class <= 0 || class >= FileMaximumInformation)
4442 return io->Status = STATUS_INVALID_INFO_CLASS;
4443 if (!info_sizes[class])
4444 return server_get_file_info( handle, io, ptr, len, class );
4445 if (len < info_sizes[class])
4446 return io->Status = STATUS_INFO_LENGTH_MISMATCH;
4448 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options )))
4450 if (status != STATUS_BAD_DEVICE_TYPE) return io->Status = status;
4451 return server_get_file_info( handle, io, ptr, len, class );
4454 switch (class)
4456 case FileBasicInformation:
4457 if (fd_get_file_info( fd, options, &st, &attr ) == -1)
4458 status = errno_to_status( errno );
4459 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4460 status = STATUS_INVALID_INFO_CLASS;
4461 else
4462 fill_file_info( &st, attr, ptr, class );
4463 break;
4464 case FileStandardInformation:
4466 FILE_STANDARD_INFORMATION *info = ptr;
4468 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4469 else
4471 fill_file_info( &st, attr, info, class );
4472 info->DeletePending = FALSE; /* FIXME */
4475 break;
4476 case FilePositionInformation:
4478 FILE_POSITION_INFORMATION *info = ptr;
4479 off_t res = lseek( fd, 0, SEEK_CUR );
4480 if (res == (off_t)-1) status = errno_to_status( errno );
4481 else info->CurrentByteOffset.QuadPart = res;
4483 break;
4484 case FileInternalInformation:
4485 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4486 else fill_file_info( &st, attr, ptr, class );
4487 break;
4488 case FileEaInformation:
4490 FILE_EA_INFORMATION *info = ptr;
4491 info->EaSize = 0;
4493 break;
4494 case FileEndOfFileInformation:
4495 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4496 else fill_file_info( &st, attr, ptr, class );
4497 break;
4498 case FileAllInformation:
4500 FILE_ALL_INFORMATION *info = ptr;
4501 char *unix_name;
4503 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4504 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4505 status = STATUS_INVALID_INFO_CLASS;
4506 else if (!(status = server_get_unix_name( handle, &unix_name )))
4508 LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
4510 fill_file_info( &st, attr, info, FileAllInformation );
4511 info->StandardInformation.DeletePending = FALSE; /* FIXME */
4512 info->EaInformation.EaSize = 0;
4513 info->AccessInformation.AccessFlags = 0; /* FIXME */
4514 info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
4515 info->ModeInformation.Mode = 0; /* FIXME */
4516 info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
4518 status = fill_name_info( unix_name, &info->NameInformation, &name_len );
4519 free( unix_name );
4520 io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len;
4523 break;
4524 case FileMailslotQueryInformation:
4526 FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;
4528 SERVER_START_REQ( set_mailslot_info )
4530 req->handle = wine_server_obj_handle( handle );
4531 req->flags = 0;
4532 status = wine_server_call( req );
4533 if (status == STATUS_SUCCESS)
4535 info->MaximumMessageSize = reply->max_msgsize;
4536 info->MailslotQuota = 0;
4537 info->NextMessageSize = 0;
4538 info->MessagesAvailable = 0;
4539 info->ReadTimeout.QuadPart = reply->read_timeout;
4542 SERVER_END_REQ;
4543 if (!status)
4545 char *tmpbuf;
4546 ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
4547 if (size > 0x10000) size = 0x10000;
4548 if ((tmpbuf = malloc( size )))
4550 if (needs_close) close( fd );
4551 if (!server_get_unix_fd( handle, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
4553 int res = recv( fd, tmpbuf, size, MSG_PEEK );
4554 info->MessagesAvailable = (res > 0);
4555 info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
4557 free( tmpbuf );
4561 break;
4562 case FileNameInformation:
4564 FILE_NAME_INFORMATION *info = ptr;
4565 char *unix_name;
4567 if (!(status = server_get_unix_name( handle, &unix_name )))
4569 LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
4570 status = fill_name_info( unix_name, info, &name_len );
4571 free( unix_name );
4572 io->Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + name_len;
4575 break;
4576 case FileNetworkOpenInformation:
4578 FILE_NETWORK_OPEN_INFORMATION *info = ptr;
4579 char *unix_name;
4581 if (!(status = server_get_unix_name( handle, &unix_name )))
4583 ULONG attributes;
4584 struct stat st;
4586 if (get_file_info( unix_name, &st, &attributes ) == -1)
4587 status = errno_to_status( errno );
4588 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4589 status = STATUS_INVALID_INFO_CLASS;
4590 else
4592 FILE_BASIC_INFORMATION basic;
4593 FILE_STANDARD_INFORMATION std;
4595 fill_file_info( &st, attributes, &basic, FileBasicInformation );
4596 fill_file_info( &st, attributes, &std, FileStandardInformation );
4598 info->CreationTime = basic.CreationTime;
4599 info->LastAccessTime = basic.LastAccessTime;
4600 info->LastWriteTime = basic.LastWriteTime;
4601 info->ChangeTime = basic.ChangeTime;
4602 info->AllocationSize = std.AllocationSize;
4603 info->EndOfFile = std.EndOfFile;
4604 info->FileAttributes = basic.FileAttributes;
4606 free( unix_name );
4609 break;
4610 case FileIdInformation:
4611 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4612 else
4614 struct mountmgr_unix_drive drive;
4615 FILE_ID_INFORMATION *info = ptr;
4617 info->VolumeSerialNumber = 0;
4618 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) ))
4619 info->VolumeSerialNumber = drive.serial;
4620 memset( &info->FileId, 0, sizeof(info->FileId) );
4621 *(ULONGLONG *)&info->FileId = st.st_ino;
4623 break;
4624 case FileAttributeTagInformation:
4625 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4626 else
4628 FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
4629 info->FileAttributes = attr;
4630 info->ReparseTag = 0; /* FIXME */
4631 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
4632 info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
4634 break;
4635 case FileStatInformation:
4636 if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
4637 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4638 status = STATUS_INVALID_INFO_CLASS;
4639 else
4641 FILE_STAT_INFORMATION *info = ptr;
4642 FILE_BASIC_INFORMATION basic;
4643 FILE_STANDARD_INFORMATION std;
4645 fill_file_info( &st, attr, &basic, FileBasicInformation );
4646 fill_file_info( &st, attr, &std, FileStandardInformation );
4648 info->FileId.QuadPart = st.st_ino;
4649 info->CreationTime = basic.CreationTime;
4650 info->LastAccessTime = basic.LastAccessTime;
4651 info->LastWriteTime = basic.LastWriteTime;
4652 info->ChangeTime = basic.ChangeTime;
4653 info->AllocationSize = std.AllocationSize;
4654 info->EndOfFile = std.EndOfFile;
4655 info->FileAttributes = attr;
4656 info->ReparseTag = 0; /* FIXME */
4657 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
4658 info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
4659 info->NumberOfLinks = std.NumberOfLinks;
4660 info->EffectiveAccess = FILE_ALL_ACCESS; /* FIXME */
4662 break;
4663 default:
4664 FIXME("Unsupported class (%d)\n", class);
4665 status = STATUS_NOT_IMPLEMENTED;
4666 break;
4668 if (needs_close) close( fd );
4669 if (status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
4670 return io->Status = status;
4674 /******************************************************************************
4675 * NtSetInformationFile (NTDLL.@)
4677 NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4678 void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
4680 int fd, needs_close;
4681 unsigned int status = STATUS_SUCCESS;
4683 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, (int)len, class );
4685 switch (class)
4687 case FileBasicInformation:
4688 if (len >= sizeof(FILE_BASIC_INFORMATION))
4690 const FILE_BASIC_INFORMATION *info = ptr;
4691 LARGE_INTEGER mtime, atime;
4692 char *unix_name;
4694 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4695 return io->Status = status;
4697 if (server_get_unix_name( handle, &unix_name )) unix_name = NULL;
4699 mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart;
4700 atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
4702 if (atime.QuadPart || mtime.QuadPart)
4703 status = set_file_times( fd, &mtime, &atime );
4705 if (status == STATUS_SUCCESS)
4706 status = fd_set_file_info( fd, info->FileAttributes,
4707 unix_name && is_hidden_file( unix_name ));
4709 if (needs_close) close( fd );
4710 free( unix_name );
4712 else status = STATUS_INVALID_PARAMETER_3;
4713 break;
4715 case FilePositionInformation:
4716 if (len >= sizeof(FILE_POSITION_INFORMATION))
4718 const FILE_POSITION_INFORMATION *info = ptr;
4720 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4721 return io->Status = status;
4723 if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
4724 status = errno_to_status( errno );
4726 if (needs_close) close( fd );
4728 else status = STATUS_INVALID_PARAMETER_3;
4729 break;
4731 case FileEndOfFileInformation:
4732 if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
4734 const FILE_END_OF_FILE_INFORMATION *info = ptr;
4736 SERVER_START_REQ( set_fd_eof_info )
4738 req->handle = wine_server_obj_handle( handle );
4739 req->eof = info->EndOfFile.QuadPart;
4740 status = wine_server_call( req );
4742 SERVER_END_REQ;
4744 else status = STATUS_INVALID_PARAMETER_3;
4745 break;
4747 case FilePipeInformation:
4748 if (len >= sizeof(FILE_PIPE_INFORMATION))
4750 FILE_PIPE_INFORMATION *info = ptr;
4752 if ((info->CompletionMode | info->ReadMode) & ~1)
4754 status = STATUS_INVALID_PARAMETER;
4755 break;
4758 SERVER_START_REQ( set_named_pipe_info )
4760 req->handle = wine_server_obj_handle( handle );
4761 req->flags = (info->CompletionMode ? NAMED_PIPE_NONBLOCKING_MODE : 0) |
4762 (info->ReadMode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0);
4763 status = wine_server_call( req );
4765 SERVER_END_REQ;
4767 else status = STATUS_INVALID_PARAMETER_3;
4768 break;
4770 case FileMailslotSetInformation:
4772 FILE_MAILSLOT_SET_INFORMATION *info = ptr;
4774 SERVER_START_REQ( set_mailslot_info )
4776 req->handle = wine_server_obj_handle( handle );
4777 req->flags = MAILSLOT_SET_READ_TIMEOUT;
4778 req->read_timeout = info->ReadTimeout.QuadPart;
4779 status = wine_server_call( req );
4781 SERVER_END_REQ;
4783 break;
4785 case FileCompletionInformation:
4786 if (len >= sizeof(FILE_COMPLETION_INFORMATION))
4788 FILE_COMPLETION_INFORMATION *info = ptr;
4790 SERVER_START_REQ( set_completion_info )
4792 req->handle = wine_server_obj_handle( handle );
4793 req->chandle = wine_server_obj_handle( info->CompletionPort );
4794 req->ckey = info->CompletionKey;
4795 status = wine_server_call( req );
4797 SERVER_END_REQ;
4799 else status = STATUS_INVALID_PARAMETER_3;
4800 break;
4802 case FileIoCompletionNotificationInformation:
4803 if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION))
4805 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr;
4807 if (info->Flags & FILE_SKIP_SET_USER_EVENT_ON_FAST_IO)
4808 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4810 SERVER_START_REQ( set_fd_completion_mode )
4812 req->handle = wine_server_obj_handle( handle );
4813 req->flags = info->Flags;
4814 status = wine_server_call( req );
4816 SERVER_END_REQ;
4818 else status = STATUS_INFO_LENGTH_MISMATCH;
4819 break;
4821 case FileIoPriorityHintInformation:
4822 if (len >= sizeof(FILE_IO_PRIORITY_HINT_INFO))
4824 FILE_IO_PRIORITY_HINT_INFO *info = ptr;
4825 if (info->PriorityHint < MaximumIoPriorityHintType)
4826 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info->PriorityHint );
4827 else
4828 status = STATUS_INVALID_PARAMETER;
4830 else status = STATUS_INFO_LENGTH_MISMATCH;
4831 break;
4833 case FileAllInformation:
4834 status = STATUS_INVALID_INFO_CLASS;
4835 break;
4837 case FileValidDataLengthInformation:
4838 if (len >= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION))
4840 struct stat st;
4841 const FILE_VALID_DATA_LENGTH_INFORMATION *info = ptr;
4843 if ((status = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL )))
4844 return io->Status = status;
4846 if (fstat( fd, &st ) == -1) status = errno_to_status( errno );
4847 else if (info->ValidDataLength.QuadPart <= 0 || (off_t)info->ValidDataLength.QuadPart > st.st_size)
4848 status = STATUS_INVALID_PARAMETER;
4849 else
4851 #ifdef HAVE_POSIX_FALLOCATE
4852 int err;
4853 if ((err = posix_fallocate( fd, 0, (off_t)info->ValidDataLength.QuadPart )) != 0)
4855 if (err == EOPNOTSUPP) WARN( "posix_fallocate not supported on this filesystem\n" );
4856 else status = errno_to_status( err );
4858 #else
4859 WARN( "setting valid data length not supported\n" );
4860 #endif
4862 if (needs_close) close( fd );
4864 else status = STATUS_INVALID_PARAMETER_3;
4865 break;
4867 case FileDispositionInformation:
4868 if (len >= sizeof(FILE_DISPOSITION_INFORMATION))
4870 FILE_DISPOSITION_INFORMATION *info = ptr;
4872 SERVER_START_REQ( set_fd_disp_info )
4874 req->handle = wine_server_obj_handle( handle );
4875 req->flags = info->DoDeleteFile ? FILE_DISPOSITION_DELETE : FILE_DISPOSITION_DO_NOT_DELETE;
4876 status = wine_server_call( req );
4878 SERVER_END_REQ;
4880 else status = STATUS_INVALID_PARAMETER_3;
4881 break;
4883 case FileDispositionInformationEx:
4884 if (len >= sizeof(FILE_DISPOSITION_INFORMATION_EX))
4886 FILE_DISPOSITION_INFORMATION_EX *info = ptr;
4888 if (info->Flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK)
4889 FIXME( "FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK not supported\n" );
4891 SERVER_START_REQ( set_fd_disp_info )
4893 req->handle = wine_server_obj_handle( handle );
4894 req->flags = info->Flags;
4895 status = wine_server_call( req );
4897 SERVER_END_REQ;
4899 else status = STATUS_INVALID_PARAMETER_3;
4900 break;
4902 case FileRenameInformation:
4903 case FileRenameInformationEx:
4904 if (len >= sizeof(FILE_RENAME_INFORMATION))
4906 FILE_RENAME_INFORMATION *info = ptr;
4907 unsigned int flags;
4908 UNICODE_STRING name_str, redir;
4909 OBJECT_ATTRIBUTES attr;
4910 char *unix_name;
4912 if (class == FileRenameInformation)
4913 flags = info->ReplaceIfExists ? FILE_RENAME_REPLACE_IF_EXISTS : 0;
4914 else
4915 flags = info->Flags;
4917 if (flags & ~(FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE))
4918 FIXME( "unsupported flags: %#x\n", flags );
4920 name_str.Buffer = info->FileName;
4921 name_str.Length = info->FileNameLength;
4922 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4923 InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL );
4924 get_redirect( &attr, &redir );
4926 status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF );
4927 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
4929 SERVER_START_REQ( set_fd_name_info )
4931 req->handle = wine_server_obj_handle( handle );
4932 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4933 req->namelen = attr.ObjectName->Length;
4934 req->link = FALSE;
4935 req->flags = flags;
4936 wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length );
4937 wine_server_add_data( req, unix_name, strlen(unix_name) );
4938 status = wine_server_call( req );
4940 SERVER_END_REQ;
4942 free( unix_name );
4944 free( redir.Buffer );
4946 else status = STATUS_INVALID_PARAMETER_3;
4947 break;
4949 case FileLinkInformation:
4950 case FileLinkInformationEx:
4951 if (len >= sizeof(FILE_LINK_INFORMATION))
4953 FILE_LINK_INFORMATION *info = ptr;
4954 unsigned int flags;
4955 UNICODE_STRING name_str, redir;
4956 OBJECT_ATTRIBUTES attr;
4957 char *unix_name;
4959 if (class == FileLinkInformation)
4960 flags = info->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0;
4961 else
4962 flags = info->Flags;
4964 if (flags & ~(FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_IGNORE_READONLY_ATTRIBUTE))
4965 FIXME( "unsupported flags: %#x\n", flags );
4967 name_str.Buffer = info->FileName;
4968 name_str.Length = info->FileNameLength;
4969 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4970 InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL );
4971 get_redirect( &attr, &redir );
4973 status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF );
4974 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
4976 SERVER_START_REQ( set_fd_name_info )
4978 req->handle = wine_server_obj_handle( handle );
4979 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4980 req->namelen = attr.ObjectName->Length;
4981 req->link = TRUE;
4982 req->flags = flags;
4983 wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length );
4984 wine_server_add_data( req, unix_name, strlen(unix_name) );
4985 status = wine_server_call( req );
4987 SERVER_END_REQ;
4989 free( unix_name );
4991 free( redir.Buffer );
4993 else status = STATUS_INVALID_PARAMETER_3;
4994 break;
4996 default:
4997 FIXME("Unsupported class (%d)\n", class);
4998 status = STATUS_NOT_IMPLEMENTED;
4999 break;
5001 io->Information = 0;
5002 return io->Status = status;
5006 /***********************************************************************
5007 * Asynchronous file I/O *
5010 struct async_fileio_read
5012 struct async_fileio io;
5013 char *buffer;
5014 unsigned int already;
5015 unsigned int count;
5016 BOOL avail_mode;
5019 struct async_fileio_write
5021 struct async_fileio io;
5022 const char *buffer;
5023 unsigned int already;
5024 unsigned int count;
5027 struct async_fileio_read_changes
5029 struct async_fileio io;
5030 void *buffer;
5031 ULONG buffer_size;
5032 ULONG data_size;
5033 char data[1];
5036 struct async_irp
5038 struct async_fileio io;
5039 void *buffer; /* buffer for output */
5040 ULONG size; /* size of buffer */
5043 static struct async_fileio *fileio_freelist;
5045 void release_fileio( struct async_fileio *io )
5047 for (;;)
5049 struct async_fileio *next = fileio_freelist;
5050 io->next = next;
5051 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist, io, next ) == next) return;
5055 struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle )
5057 /* first free remaining previous fileinfos */
5058 struct async_fileio *io = InterlockedExchangePointer( (void **)&fileio_freelist, NULL );
5060 while (io)
5062 struct async_fileio *next = io->next;
5063 free( io );
5064 io = next;
5067 if ((io = malloc( size )))
5069 io->callback = callback;
5070 io->handle = handle;
5072 return io;
5075 /* callback for irp async I/O completion */
5076 static BOOL irp_completion( void *user, ULONG_PTR *info, unsigned int *status )
5078 struct async_irp *async = user;
5080 if (*status == STATUS_ALERTED)
5082 SERVER_START_REQ( get_async_result )
5084 req->user_arg = wine_server_client_ptr( async );
5085 wine_server_set_reply( req, async->buffer, async->size );
5086 *status = virtual_locked_server_call( req );
5088 SERVER_END_REQ;
5090 release_fileio( &async->io );
5091 return TRUE;
5094 static BOOL async_read_proc( void *user, ULONG_PTR *info, unsigned int *status )
5096 struct async_fileio_read *fileio = user;
5097 int fd, needs_close, result;
5099 switch (*status)
5101 case STATUS_ALERTED: /* got some new data */
5102 /* check to see if the data is ready (non-blocking) */
5103 if ((*status = server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd,
5104 &needs_close, NULL, NULL )))
5105 break;
5107 result = virtual_locked_read(fd, &fileio->buffer[fileio->already], fileio->count-fileio->already);
5108 if (needs_close) close( fd );
5110 if (result < 0)
5112 if (errno == EAGAIN || errno == EINTR)
5113 return FALSE;
5115 /* check to see if the transfer is complete */
5116 *status = errno_to_status( errno );
5118 else if (result == 0)
5120 *status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN;
5122 else
5124 fileio->already += result;
5126 if (fileio->already < fileio->count && !fileio->avail_mode)
5127 return FALSE;
5129 *status = STATUS_SUCCESS;
5131 break;
5133 case STATUS_TIMEOUT:
5134 case STATUS_IO_TIMEOUT:
5135 if (fileio->already) *status = STATUS_SUCCESS;
5136 break;
5139 *info = fileio->already;
5140 release_fileio( &fileio->io );
5141 return TRUE;
5144 static BOOL async_write_proc( void *user, ULONG_PTR *info, unsigned int *status )
5146 struct async_fileio_write *fileio = user;
5147 int result, fd, needs_close;
5148 enum server_fd_type type;
5150 switch (*status)
5152 case STATUS_ALERTED:
5153 /* write some data (non-blocking) */
5154 if ((*status = server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd,
5155 &needs_close, &type, NULL )))
5156 break;
5158 if (!fileio->count && type == FD_TYPE_MAILSLOT)
5159 result = send( fd, fileio->buffer, 0, 0 );
5160 else
5161 result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already );
5163 if (needs_close) close( fd );
5165 if (result < 0)
5167 if (errno == EAGAIN || errno == EINTR) return FALSE;
5168 *status = errno_to_status( errno );
5170 else
5172 fileio->already += result;
5173 if (fileio->already < fileio->count) return FALSE;
5174 *status = STATUS_SUCCESS;
5176 break;
5178 case STATUS_TIMEOUT:
5179 case STATUS_IO_TIMEOUT:
5180 if (fileio->already) *status = STATUS_SUCCESS;
5181 break;
5184 *info = fileio->already;
5185 release_fileio( &fileio->io );
5186 return TRUE;
5189 static void set_sync_iosb( IO_STATUS_BLOCK *io, NTSTATUS status, ULONG_PTR info, unsigned int options )
5191 if (in_wow64_call() && !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)))
5193 IO_STATUS_BLOCK32 *io32 = io->Pointer;
5195 io32->Status = status;
5196 io32->Information = info;
5198 else
5200 io->Status = status;
5201 io->Information = info;
5205 /* do a read call through the server */
5206 static unsigned int server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5207 IO_STATUS_BLOCK *io, void *buffer, ULONG size,
5208 LARGE_INTEGER *offset, ULONG *key )
5210 struct async_irp *async;
5211 unsigned int status;
5212 HANDLE wait_handle;
5213 ULONG options;
5215 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5216 return STATUS_NO_MEMORY;
5218 async->buffer = buffer;
5219 async->size = size;
5221 SERVER_START_REQ( read )
5223 req->async = server_async( handle, &async->io, event, apc, apc_context, iosb_client_ptr(io) );
5224 req->pos = offset ? offset->QuadPart : 0;
5225 wine_server_set_reply( req, buffer, size );
5226 status = virtual_locked_server_call( req );
5227 wait_handle = wine_server_ptr_handle( reply->wait );
5228 options = reply->options;
5229 if (wait_handle && status != STATUS_PENDING)
5230 set_sync_iosb( io, status, wine_server_reply_size( reply ), options );
5232 SERVER_END_REQ;
5234 if (status != STATUS_PENDING) free( async );
5236 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
5237 return status;
5240 /* do a write call through the server */
5241 static unsigned int server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5242 IO_STATUS_BLOCK *io, const void *buffer, ULONG size,
5243 LARGE_INTEGER *offset, ULONG *key )
5245 struct async_irp *async;
5246 unsigned int status;
5247 HANDLE wait_handle;
5248 ULONG options;
5250 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5251 return STATUS_NO_MEMORY;
5253 async->buffer = NULL;
5254 async->size = 0;
5256 SERVER_START_REQ( write )
5258 req->async = server_async( handle, &async->io, event, apc, apc_context, iosb_client_ptr(io) );
5259 req->pos = offset ? offset->QuadPart : 0;
5260 wine_server_add_data( req, buffer, size );
5261 status = wine_server_call( req );
5262 wait_handle = wine_server_ptr_handle( reply->wait );
5263 options = reply->options;
5264 if (wait_handle && status != STATUS_PENDING)
5265 set_sync_iosb( io, status, reply->size, options );
5267 SERVER_END_REQ;
5269 if (status != STATUS_PENDING) free( async );
5271 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
5272 return status;
5275 /* do an ioctl call through the server */
5276 static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
5277 PIO_APC_ROUTINE apc, PVOID apc_context,
5278 IO_STATUS_BLOCK *io, UINT code,
5279 const void *in_buffer, UINT in_size,
5280 PVOID out_buffer, UINT out_size )
5282 struct async_irp *async;
5283 unsigned int status;
5284 HANDLE wait_handle;
5285 ULONG options;
5287 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5288 return STATUS_NO_MEMORY;
5289 async->buffer = out_buffer;
5290 async->size = out_size;
5292 SERVER_START_REQ( ioctl )
5294 req->code = code;
5295 req->async = server_async( handle, &async->io, event, apc, apc_context, iosb_client_ptr(io) );
5296 wine_server_add_data( req, in_buffer, in_size );
5297 if ((code & 3) != METHOD_BUFFERED) wine_server_add_data( req, out_buffer, out_size );
5298 wine_server_set_reply( req, out_buffer, out_size );
5299 status = virtual_locked_server_call( req );
5300 wait_handle = wine_server_ptr_handle( reply->wait );
5301 options = reply->options;
5302 if (wait_handle && status != STATUS_PENDING)
5303 set_sync_iosb( io, status, wine_server_reply_size( reply ), options );
5305 SERVER_END_REQ;
5307 if (status == STATUS_NOT_SUPPORTED)
5308 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
5309 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
5311 if (status != STATUS_PENDING) free( async );
5313 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
5314 return status;
5318 struct io_timeouts
5320 int interval; /* max interval between two bytes */
5321 int total; /* total timeout for the whole operation */
5322 int end_time; /* absolute time of end of operation */
5325 /* retrieve the I/O timeouts to use for a given handle */
5326 static unsigned int get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read,
5327 struct io_timeouts *timeouts )
5329 timeouts->interval = timeouts->total = -1;
5331 switch(type)
5333 case FD_TYPE_SERIAL:
5335 /* GetCommTimeouts */
5336 SERIAL_TIMEOUTS st;
5338 if (sync_ioctl( handle, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) ))
5339 break;
5341 if (is_read)
5343 if (st.ReadIntervalTimeout)
5344 timeouts->interval = st.ReadIntervalTimeout;
5346 if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant)
5348 timeouts->total = st.ReadTotalTimeoutConstant;
5349 if (st.ReadTotalTimeoutMultiplier != MAXDWORD)
5350 timeouts->total += count * st.ReadTotalTimeoutMultiplier;
5352 else if (st.ReadIntervalTimeout == MAXDWORD)
5353 timeouts->interval = timeouts->total = 0;
5355 else /* write */
5357 if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant)
5359 timeouts->total = st.WriteTotalTimeoutConstant;
5360 if (st.WriteTotalTimeoutMultiplier != MAXDWORD)
5361 timeouts->total += count * st.WriteTotalTimeoutMultiplier;
5364 break;
5366 case FD_TYPE_MAILSLOT:
5367 if (is_read)
5369 timeouts->interval = 0; /* return as soon as we got something */
5370 SERVER_START_REQ( set_mailslot_info )
5372 req->handle = wine_server_obj_handle( handle );
5373 req->flags = 0;
5374 if (!wine_server_call( req ) && reply->read_timeout != TIMEOUT_INFINITE)
5375 timeouts->total = reply->read_timeout / -10000;
5377 SERVER_END_REQ;
5379 break;
5380 case FD_TYPE_SOCKET:
5381 case FD_TYPE_CHAR:
5382 if (is_read) timeouts->interval = 0; /* return as soon as we got something */
5383 break;
5384 default:
5385 break;
5387 if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total;
5388 return STATUS_SUCCESS;
5392 /* retrieve the timeout for the next wait, in milliseconds */
5393 static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already )
5395 int ret = -1;
5397 if (timeouts->total != -1)
5399 ret = timeouts->end_time - NtGetTickCount();
5400 if (ret < 0) ret = 0;
5402 if (already && timeouts->interval != -1)
5404 if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval;
5406 return ret;
5410 /* retrieve the avail_mode flag for async reads */
5411 static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode )
5413 NTSTATUS status = STATUS_SUCCESS;
5415 switch(type)
5417 case FD_TYPE_SERIAL:
5419 /* GetCommTimeouts */
5420 SERIAL_TIMEOUTS st;
5422 if (!(status = sync_ioctl( handle, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) )))
5424 *avail_mode = (!st.ReadTotalTimeoutMultiplier &&
5425 !st.ReadTotalTimeoutConstant &&
5426 st.ReadIntervalTimeout == MAXDWORD);
5428 break;
5430 case FD_TYPE_MAILSLOT:
5431 case FD_TYPE_SOCKET:
5432 case FD_TYPE_CHAR:
5433 *avail_mode = TRUE;
5434 break;
5435 default:
5436 *avail_mode = FALSE;
5437 break;
5439 return status;
5442 /* register an async I/O for a file read; helper for NtReadFile */
5443 static unsigned int register_async_file_read( HANDLE handle, HANDLE event,
5444 PIO_APC_ROUTINE apc, void *apc_user,
5445 client_ptr_t iosb, void *buffer,
5446 ULONG already, ULONG length, BOOL avail_mode )
5448 struct async_fileio_read *fileio;
5449 unsigned int status;
5451 if (!(fileio = (struct async_fileio_read *)alloc_fileio( sizeof(*fileio), async_read_proc, handle )))
5452 return STATUS_NO_MEMORY;
5454 fileio->already = already;
5455 fileio->count = length;
5456 fileio->buffer = buffer;
5457 fileio->avail_mode = avail_mode;
5459 SERVER_START_REQ( register_async )
5461 req->type = ASYNC_TYPE_READ;
5462 req->count = length;
5463 req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb );
5464 status = wine_server_call( req );
5466 SERVER_END_REQ;
5468 if (status != STATUS_PENDING) free( fileio );
5469 return status;
5472 static void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async )
5474 SERVER_START_REQ( add_fd_completion )
5476 req->handle = wine_server_obj_handle( handle );
5477 req->cvalue = value;
5478 req->status = status;
5479 req->information = info;
5480 req->async = async;
5481 wine_server_call( req );
5483 SERVER_END_REQ;
5486 /* notify direct completion of async and close the wait handle if it is no longer needed */
5487 void set_async_direct_result( HANDLE *async_handle, unsigned int options, IO_STATUS_BLOCK *io,
5488 NTSTATUS status, ULONG_PTR information, BOOL mark_pending )
5490 unsigned int ret;
5492 /* if we got STATUS_ALERTED, we must have a valid async handle */
5493 assert( *async_handle );
5495 if (!NT_ERROR(status) && status != STATUS_PENDING)
5496 set_sync_iosb( io, status, information, options );
5498 SERVER_START_REQ( set_async_direct_result )
5500 req->handle = wine_server_obj_handle( *async_handle );
5501 req->status = status;
5502 req->information = information;
5503 req->mark_pending = mark_pending;
5504 ret = wine_server_call( req );
5505 if (ret == STATUS_SUCCESS)
5506 *async_handle = wine_server_ptr_handle( reply->handle );
5508 SERVER_END_REQ;
5510 if (ret != STATUS_SUCCESS)
5511 ERR( "cannot report I/O result back to server: %#x\n", ret );
5514 /* complete async file I/O, signaling completion in all ways necessary */
5515 void file_complete_async( HANDLE handle, unsigned int options, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5516 IO_STATUS_BLOCK *io, NTSTATUS status, ULONG_PTR information )
5518 ULONG_PTR iosb_ptr = iosb_client_ptr(io);
5520 set_sync_iosb( io, status, information, options );
5521 if (event) NtSetEvent( event, NULL );
5522 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, (ULONG_PTR)apc_user, iosb_ptr, 0 );
5523 else if (apc_user) add_completion( handle, (ULONG_PTR)apc_user, status, information, FALSE );
5527 static unsigned int set_pending_write( HANDLE device )
5529 unsigned int status;
5531 SERVER_START_REQ( set_serial_info )
5533 req->handle = wine_server_obj_handle( device );
5534 req->flags = SERIALINFO_PENDING_WRITE;
5535 status = wine_server_call( req );
5537 SERVER_END_REQ;
5538 return status;
5542 /******************************************************************************
5543 * NtReadFile (NTDLL.@)
5545 NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5546 IO_STATUS_BLOCK *io, void *buffer, ULONG length,
5547 LARGE_INTEGER *offset, ULONG *key )
5549 int result, unix_handle, needs_close;
5550 unsigned int options;
5551 struct io_timeouts timeouts;
5552 unsigned int status, ret_status;
5553 UINT total = 0;
5554 client_ptr_t iosb_ptr = iosb_client_ptr(io);
5555 enum server_fd_type type;
5556 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5557 BOOL send_completion = FALSE, async_read, timeout_init_done = FALSE;
5559 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5560 handle, event, apc, apc_user, io, buffer, (int)length, offset, key );
5562 if (!io) return STATUS_ACCESS_VIOLATION;
5564 status = server_get_unix_fd( handle, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5565 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5567 if (!virtual_check_buffer_for_write( buffer, length )) return STATUS_ACCESS_VIOLATION;
5569 if (status == STATUS_BAD_DEVICE_TYPE)
5570 return server_read_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5572 async_read = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5574 if (type == FD_TYPE_FILE)
5576 if (async_read && (!offset || offset->QuadPart < 0))
5578 status = STATUS_INVALID_PARAMETER;
5579 goto done;
5582 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5584 /* async I/O doesn't make sense on regular files */
5585 while ((result = virtual_locked_pread( unix_handle, buffer, length, offset->QuadPart )) == -1)
5587 if (errno != EINTR)
5589 status = errno_to_status( errno );
5590 goto done;
5593 if (!async_read) /* update file pointer position */
5594 lseek( unix_handle, offset->QuadPart + result, SEEK_SET );
5596 total = result;
5597 status = (total || !length) ? STATUS_SUCCESS : STATUS_END_OF_FILE;
5598 goto done;
5601 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5603 if (async_read && (!offset || offset->QuadPart < 0))
5605 status = STATUS_INVALID_PARAMETER;
5606 goto done;
5609 else if (type == FD_TYPE_SOCKET)
5611 status = sock_read( handle, unix_handle, event, apc, apc_user, io, buffer, length );
5612 if (needs_close) close( unix_handle );
5613 return status;
5616 if (type == FD_TYPE_SERIAL && async_read && length)
5618 /* an asynchronous serial port read with a read interval timeout needs to
5619 skip the synchronous read to make sure that the server starts the read
5620 interval timer after the first read */
5621 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5622 if (timeouts.interval)
5624 status = register_async_file_read( handle, event, apc, apc_user, iosb_ptr,
5625 buffer, total, length, FALSE );
5626 goto err;
5630 for (;;)
5632 if ((result = virtual_locked_read( unix_handle, (char *)buffer + total, length - total )) >= 0)
5634 total += result;
5635 if (!result || total == length)
5637 if (total)
5639 status = STATUS_SUCCESS;
5640 goto done;
5642 switch (type)
5644 case FD_TYPE_FILE:
5645 case FD_TYPE_CHAR:
5646 case FD_TYPE_DEVICE:
5647 status = length ? STATUS_END_OF_FILE : STATUS_SUCCESS;
5648 goto done;
5649 case FD_TYPE_SERIAL:
5650 if (!length)
5652 status = STATUS_SUCCESS;
5653 goto done;
5655 break;
5656 default:
5657 status = STATUS_PIPE_BROKEN;
5658 goto err;
5661 else if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5663 else if (errno != EAGAIN)
5665 if (errno == EINTR) continue;
5666 if (!total) status = errno_to_status( errno );
5667 goto err;
5670 if (async_read)
5672 BOOL avail_mode;
5674 if ((status = get_io_avail_mode( handle, type, &avail_mode ))) goto err;
5675 if (total && avail_mode)
5677 status = STATUS_SUCCESS;
5678 goto done;
5680 status = register_async_file_read( handle, event, apc, apc_user, iosb_ptr,
5681 buffer, total, length, avail_mode );
5682 goto err;
5684 else /* synchronous read, wait for the fd to become ready */
5686 struct pollfd pfd;
5687 int ret, timeout;
5689 if (!timeout_init_done)
5691 timeout_init_done = TRUE;
5692 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5693 if (event) NtResetEvent( event, NULL );
5695 timeout = get_next_io_timeout( &timeouts, total );
5697 pfd.fd = unix_handle;
5698 pfd.events = POLLIN;
5700 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5702 if (total) /* return with what we got so far */
5703 status = STATUS_SUCCESS;
5704 else
5705 status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT;
5706 goto done;
5708 if (ret == -1 && errno != EINTR)
5710 status = errno_to_status( errno );
5711 goto done;
5713 /* will now restart the read */
5717 done:
5718 send_completion = cvalue != 0;
5720 err:
5721 if (needs_close) close( unix_handle );
5722 if (status == STATUS_SUCCESS || (status == STATUS_END_OF_FILE && (!async_read || type == FD_TYPE_FILE)))
5724 set_sync_iosb( io, status, total, options );
5725 TRACE("= SUCCESS (%u)\n", total);
5726 if (event) NtSetEvent( event, NULL );
5727 if (apc && (!status || async_read)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5728 (ULONG_PTR)apc_user, iosb_ptr, 0 );
5730 else
5732 TRACE("= 0x%08x\n", status);
5733 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5736 ret_status = async_read && type == FD_TYPE_FILE && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE)
5737 ? STATUS_PENDING : status;
5739 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5740 return ret_status;
5744 /******************************************************************************
5745 * NtReadFileScatter (NTDLL.@)
5747 NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5748 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5749 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5751 int result, unix_handle, needs_close;
5752 unsigned int options, status;
5753 UINT pos = 0, total = 0;
5754 client_ptr_t iosb_ptr = iosb_client_ptr(io);
5755 enum server_fd_type type;
5756 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5757 BOOL send_completion = FALSE;
5759 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5760 file, event, apc, apc_user, io, segments, (int)length, offset, key );
5762 if (!io) return STATUS_ACCESS_VIOLATION;
5764 status = server_get_unix_fd( file, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5765 if (status) return status;
5767 if ((type != FD_TYPE_FILE) ||
5768 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5769 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5771 status = STATUS_INVALID_PARAMETER;
5772 goto error;
5775 while (length)
5777 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5778 result = pread( unix_handle, (char *)segments->Buffer + pos,
5779 min( length - pos, page_size - pos ), offset->QuadPart + total );
5780 else
5781 result = read( unix_handle, (char *)segments->Buffer + pos, min( length - pos, page_size - pos ) );
5783 if (result == -1)
5785 if (errno == EINTR) continue;
5786 status = errno_to_status( errno );
5787 break;
5789 if (!result) break;
5790 total += result;
5791 length -= result;
5792 if ((pos += result) == page_size)
5794 pos = 0;
5795 segments++;
5799 if (total == 0) status = STATUS_END_OF_FILE;
5801 send_completion = cvalue != 0;
5803 if (needs_close) close( unix_handle );
5804 set_sync_iosb( io, status, total, options );
5805 TRACE("= 0x%08x (%u)\n", status, total);
5806 if (event) NtSetEvent( event, NULL );
5807 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, (ULONG_PTR)apc_user, iosb_ptr, 0 );
5808 if (send_completion) add_completion( file, cvalue, status, total, TRUE );
5810 return STATUS_PENDING;
5812 error:
5813 if (needs_close) close( unix_handle );
5814 if (event) NtResetEvent( event, NULL );
5815 TRACE("= 0x%08x\n", status);
5816 return status;
5820 /******************************************************************************
5821 * NtWriteFile (NTDLL.@)
5823 NTSTATUS WINAPI NtWriteFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5824 IO_STATUS_BLOCK *io, const void *buffer, ULONG length,
5825 LARGE_INTEGER *offset, ULONG *key )
5827 int result, unix_handle, needs_close;
5828 unsigned int options;
5829 struct io_timeouts timeouts;
5830 unsigned int status, ret_status;
5831 UINT total = 0;
5832 client_ptr_t iosb_ptr = iosb_client_ptr(io);
5833 enum server_fd_type type;
5834 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5835 BOOL send_completion = FALSE, async_write, append_write = FALSE, timeout_init_done = FALSE;
5836 LARGE_INTEGER offset_eof;
5838 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5839 handle, event, apc, apc_user, io, buffer, (int)length, offset, key );
5841 if (!io) return STATUS_ACCESS_VIOLATION;
5843 status = server_get_unix_fd( handle, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5844 if (status == STATUS_ACCESS_DENIED)
5846 status = server_get_unix_fd( handle, FILE_APPEND_DATA, &unix_handle,
5847 &needs_close, &type, &options );
5848 append_write = TRUE;
5850 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5852 async_write = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5854 if (!virtual_check_buffer_for_read( buffer, length ))
5856 status = STATUS_INVALID_USER_BUFFER;
5857 goto done;
5860 if (status == STATUS_BAD_DEVICE_TYPE)
5861 return server_write_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5863 if (type == FD_TYPE_FILE)
5865 if (async_write &&
5866 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5868 status = STATUS_INVALID_PARAMETER;
5869 goto done;
5872 if (append_write)
5874 offset_eof.QuadPart = FILE_WRITE_TO_END_OF_FILE;
5875 offset = &offset_eof;
5878 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5880 off_t off = offset->QuadPart;
5882 if (offset->QuadPart == FILE_WRITE_TO_END_OF_FILE)
5884 struct stat st;
5886 if (fstat( unix_handle, &st ) == -1)
5888 status = errno_to_status( errno );
5889 goto done;
5891 off = st.st_size;
5893 else if (offset->QuadPart < 0)
5895 status = STATUS_INVALID_PARAMETER;
5896 goto done;
5899 /* async I/O doesn't make sense on regular files */
5900 while ((result = pwrite( unix_handle, buffer, length, off )) == -1)
5902 if (errno != EINTR)
5904 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5905 else status = errno_to_status( errno );
5906 goto done;
5910 if (!async_write) /* update file pointer position */
5911 lseek( unix_handle, off + result, SEEK_SET );
5913 total = result;
5914 status = STATUS_SUCCESS;
5915 goto done;
5918 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5920 if (async_write &&
5921 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5923 status = STATUS_INVALID_PARAMETER;
5924 goto done;
5927 else if (type == FD_TYPE_SOCKET)
5929 status = sock_write( handle, unix_handle, event, apc, apc_user, io, buffer, length );
5930 if (needs_close) close( unix_handle );
5931 return status;
5934 for (;;)
5936 /* zero-length writes on sockets may not work with plain write(2) */
5937 if (!length && type == FD_TYPE_MAILSLOT)
5938 result = send( unix_handle, buffer, 0, 0 );
5939 else
5940 result = write( unix_handle, (const char *)buffer + total, length - total );
5942 if (result >= 0)
5944 total += result;
5945 if (total == length)
5947 status = STATUS_SUCCESS;
5948 goto done;
5950 if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5952 else if (errno != EAGAIN)
5954 if (errno == EINTR) continue;
5955 if (!total)
5957 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5958 else status = errno_to_status( errno );
5960 goto err;
5963 if (async_write)
5965 struct async_fileio_write *fileio;
5967 fileio = (struct async_fileio_write *)alloc_fileio( sizeof(*fileio), async_write_proc, handle );
5968 if (!fileio)
5970 status = STATUS_NO_MEMORY;
5971 goto err;
5973 fileio->already = total;
5974 fileio->count = length;
5975 fileio->buffer = buffer;
5977 SERVER_START_REQ( register_async )
5979 req->type = ASYNC_TYPE_WRITE;
5980 req->count = length;
5981 req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb_ptr );
5982 status = wine_server_call( req );
5984 SERVER_END_REQ;
5986 if (status != STATUS_PENDING) free( fileio );
5987 goto err;
5989 else /* synchronous write, wait for the fd to become ready */
5991 struct pollfd pfd;
5992 int ret, timeout;
5994 if (!timeout_init_done)
5996 timeout_init_done = TRUE;
5997 if ((status = get_io_timeouts( handle, type, length, FALSE, &timeouts )))
5998 goto err;
5999 if (event) NtResetEvent( event, NULL );
6001 timeout = get_next_io_timeout( &timeouts, total );
6003 pfd.fd = unix_handle;
6004 pfd.events = POLLOUT;
6006 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
6008 /* return with what we got so far */
6009 status = total ? STATUS_SUCCESS : STATUS_TIMEOUT;
6010 goto done;
6012 if (ret == -1 && errno != EINTR)
6014 status = errno_to_status( errno );
6015 goto done;
6017 /* will now restart the write */
6021 done:
6022 send_completion = cvalue != 0;
6024 err:
6025 if (needs_close) close( unix_handle );
6027 if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING))
6028 set_pending_write( handle );
6030 if (status == STATUS_SUCCESS)
6032 set_sync_iosb( io, status, total, options );
6033 TRACE("= SUCCESS (%u)\n", total);
6034 if (event) NtSetEvent( event, NULL );
6035 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, (ULONG_PTR)apc_user, iosb_ptr, 0 );
6037 else
6039 TRACE("= 0x%08x\n", status);
6040 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
6043 ret_status = async_write && type == FD_TYPE_FILE && status == STATUS_SUCCESS ? STATUS_PENDING : status;
6044 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
6045 return ret_status;
6049 /******************************************************************************
6050 * NtWriteFileGather (NTDLL.@)
6052 NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
6053 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
6054 ULONG length, LARGE_INTEGER *offset, ULONG *key )
6056 int result, unix_handle, needs_close;
6057 unsigned int options, status;
6058 UINT pos = 0, total = 0;
6059 enum server_fd_type type;
6061 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
6062 file, event, apc, apc_user, io, segments, (int)length, offset, key );
6064 if (length % page_size) return STATUS_INVALID_PARAMETER;
6065 if (!io) return STATUS_ACCESS_VIOLATION;
6067 status = server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
6068 if (status) return status;
6070 if ((type != FD_TYPE_FILE) ||
6071 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
6072 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
6074 status = STATUS_INVALID_PARAMETER;
6075 goto done;
6078 while (length)
6080 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
6081 result = pwrite( unix_handle, (char *)segments->Buffer + pos,
6082 page_size - pos, offset->QuadPart + total );
6083 else
6084 result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos );
6086 if (result == -1)
6088 if (errno == EINTR) continue;
6089 if (errno == EFAULT)
6091 status = STATUS_INVALID_USER_BUFFER;
6092 goto done;
6094 status = errno_to_status( errno );
6095 break;
6097 if (!result)
6099 status = STATUS_DISK_FULL;
6100 break;
6102 total += result;
6103 length -= result;
6104 if ((pos += result) == page_size)
6106 pos = 0;
6107 segments++;
6111 done:
6112 if (needs_close) close( unix_handle );
6113 if (status == STATUS_SUCCESS)
6115 file_complete_async( file, options, event, apc, apc_user, io, status, total );
6116 TRACE("= SUCCESS (%u)\n", total);
6118 else
6120 TRACE("= 0x%08x\n", status);
6121 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
6123 return status;
6127 /******************************************************************************
6128 * NtDeviceIoControlFile (NTDLL.@)
6130 NTSTATUS WINAPI NtDeviceIoControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
6131 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
6132 void *out_buffer, ULONG out_size )
6134 ULONG device = (code >> 16);
6135 NTSTATUS status = STATUS_NOT_SUPPORTED;
6137 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
6138 handle, event, apc, apc_context, io, (int)code,
6139 in_buffer, (int)in_size, out_buffer, (int)out_size );
6141 /* some broken applications call this frequently with INVALID_HANDLE_VALUE,
6142 * and run slowly if we make a server call every time */
6143 if (HandleToLong( handle ) == ~0)
6144 return STATUS_INVALID_HANDLE;
6146 switch (device)
6148 case FILE_DEVICE_BEEP:
6149 case FILE_DEVICE_NETWORK:
6150 status = sock_ioctl( handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
6151 break;
6152 case FILE_DEVICE_DISK:
6153 case FILE_DEVICE_CD_ROM:
6154 case FILE_DEVICE_DVD:
6155 case FILE_DEVICE_CONTROLLER:
6156 case FILE_DEVICE_MASS_STORAGE:
6157 status = cdrom_DeviceIoControl( handle, event, apc, apc_context, io, code,
6158 in_buffer, in_size, out_buffer, out_size );
6159 break;
6160 case FILE_DEVICE_SERIAL_PORT:
6161 status = serial_DeviceIoControl( handle, event, apc, apc_context, io, code,
6162 in_buffer, in_size, out_buffer, out_size );
6163 break;
6164 case FILE_DEVICE_TAPE:
6165 status = tape_DeviceIoControl( handle, event, apc, apc_context, io, code,
6166 in_buffer, in_size, out_buffer, out_size );
6167 break;
6170 if (status == STATUS_NOT_SUPPORTED || status == STATUS_BAD_DEVICE_TYPE)
6171 return server_ioctl_file( handle, event, apc, apc_context, io, code,
6172 in_buffer, in_size, out_buffer, out_size );
6173 return status;
6177 /* helper for internal ioctl calls */
6178 NTSTATUS sync_ioctl( HANDLE file, ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size )
6180 IO_STATUS_BLOCK32 io32;
6181 IO_STATUS_BLOCK io;
6183 /* the 32-bit iosb is filled for overlapped file handles */
6184 io.Pointer = &io32;
6185 return NtDeviceIoControlFile( file, NULL, NULL, NULL, &io, code, in_buffer, in_size, out_buffer, out_size );
6189 /* Tell Valgrind to ignore any holes in structs we will be passing to the
6190 * server */
6191 static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer, ULONG in_size )
6193 #ifdef VALGRIND_MAKE_MEM_DEFINED
6194 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
6195 do { \
6196 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
6197 if ((size) >= FIELD_OFFSET(t, f2)) \
6198 VALGRIND_MAKE_MEM_DEFINED( \
6199 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
6200 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
6201 } while (0)
6203 switch (code)
6205 case FSCTL_PIPE_WAIT:
6206 IGNORE_STRUCT_HOLE(in_buffer, in_size, FILE_PIPE_WAIT_FOR_BUFFER, TimeoutSpecified, Name);
6207 break;
6209 #endif
6213 /******************************************************************************
6214 * NtFsControlFile (NTDLL.@)
6216 NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
6217 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
6218 void *out_buffer, ULONG out_size )
6220 unsigned int options;
6221 int fd, needs_close;
6222 ULONG_PTR size = 0;
6223 NTSTATUS status;
6225 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
6226 handle, event, apc, apc_context, io, (int)code,
6227 in_buffer, (int)in_size, out_buffer, (int)out_size );
6229 if (!io) return STATUS_INVALID_PARAMETER;
6231 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options );
6232 if (status && status != STATUS_BAD_DEVICE_TYPE)
6233 return status;
6234 if (needs_close) close( fd );
6236 ignore_server_ioctl_struct_holes( code, in_buffer, in_size );
6238 switch (code)
6240 case FSCTL_DISMOUNT_VOLUME:
6241 status = server_ioctl_file( handle, event, apc, apc_context, io, code,
6242 in_buffer, in_size, out_buffer, out_size );
6243 if (!status) status = unmount_device( handle );
6244 return status;
6246 case FSCTL_PIPE_IMPERSONATE:
6247 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
6248 return server_ioctl_file( handle, event, apc, apc_context, io, code,
6249 in_buffer, in_size, out_buffer, out_size );
6251 case FSCTL_IS_VOLUME_MOUNTED:
6252 case FSCTL_LOCK_VOLUME:
6253 case FSCTL_UNLOCK_VOLUME:
6254 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
6255 (int)code, (int)code >> 16, ((int)code >> 14) & 3, ((int)code >> 2) & 0xfff, (int)code & 3);
6256 status = STATUS_SUCCESS;
6257 break;
6259 case FSCTL_GET_RETRIEVAL_POINTERS:
6261 RETRIEVAL_POINTERS_BUFFER *buffer = (RETRIEVAL_POINTERS_BUFFER *)out_buffer;
6263 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
6265 if (out_size >= sizeof(RETRIEVAL_POINTERS_BUFFER))
6267 buffer->ExtentCount = 1;
6268 buffer->StartingVcn.QuadPart = 1;
6269 buffer->Extents[0].NextVcn.QuadPart = 0;
6270 buffer->Extents[0].Lcn.QuadPart = 0;
6271 size = sizeof(RETRIEVAL_POINTERS_BUFFER);
6272 status = STATUS_SUCCESS;
6274 else
6276 status = STATUS_BUFFER_TOO_SMALL;
6278 break;
6281 case FSCTL_GET_REPARSE_POINT:
6282 if (out_buffer && out_size)
6284 FIXME("FSCTL_GET_REPARSE_POINT semi-stub\n");
6285 status = STATUS_NOT_A_REPARSE_POINT;
6287 else status = STATUS_INVALID_USER_BUFFER;
6288 break;
6290 case FSCTL_GET_OBJECT_ID:
6292 FILE_OBJECTID_BUFFER *info = out_buffer;
6293 int fd, needs_close;
6294 struct stat st;
6296 if (out_size >= sizeof(*info))
6298 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
6299 if (status) break;
6300 fstat( fd, &st );
6301 if (needs_close) close( fd );
6302 memset( info, 0, sizeof(*info) );
6303 memcpy( info->ObjectId, &st.st_dev, sizeof(st.st_dev) );
6304 memcpy( info->ObjectId + 8, &st.st_ino, sizeof(st.st_ino) );
6305 size = sizeof(*info);
6307 else status = STATUS_BUFFER_TOO_SMALL;
6308 break;
6311 case FSCTL_SET_SPARSE:
6312 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
6313 status = STATUS_SUCCESS;
6314 break;
6315 default:
6316 return server_ioctl_file( handle, event, apc, apc_context, io, code,
6317 in_buffer, in_size, out_buffer, out_size );
6320 if (!NT_ERROR(status) && status != STATUS_PENDING)
6321 file_complete_async( handle, options, event, apc, apc_context, io, status, size );
6322 return status;
6326 /******************************************************************************
6327 * NtFlushBuffersFile (NTDLL.@)
6329 NTSTATUS WINAPI NtFlushBuffersFile( HANDLE handle, IO_STATUS_BLOCK *io )
6331 NTSTATUS ret;
6332 HANDLE wait_handle;
6333 enum server_fd_type type;
6334 int fd, needs_close;
6336 if (!io || !virtual_check_buffer_for_write( io, sizeof(*io) )) return STATUS_ACCESS_VIOLATION;
6338 ret = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, &type, NULL );
6339 if (ret == STATUS_ACCESS_DENIED)
6340 ret = server_get_unix_fd( handle, FILE_APPEND_DATA, &fd, &needs_close, &type, NULL );
6342 if (!ret && (type == FD_TYPE_FILE || type == FD_TYPE_DIR || type == FD_TYPE_CHAR))
6344 if (fsync(fd)) ret = errno_to_status( errno );
6345 io->Status = ret;
6346 io->Information = 0;
6348 else if (!ret && type == FD_TYPE_SERIAL)
6350 ret = serial_FlushBuffersFile( fd );
6352 else if (ret != STATUS_ACCESS_DENIED)
6354 struct async_irp *async;
6356 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
6357 return STATUS_NO_MEMORY;
6358 async->buffer = NULL;
6359 async->size = 0;
6361 SERVER_START_REQ( flush )
6363 req->async = server_async( handle, &async->io, NULL, NULL, NULL, iosb_client_ptr(io) );
6364 ret = wine_server_call( req );
6365 wait_handle = wine_server_ptr_handle( reply->event );
6366 if (wait_handle && ret != STATUS_PENDING)
6368 io->Status = ret;
6369 io->Information = 0;
6372 SERVER_END_REQ;
6374 if (ret != STATUS_PENDING) free( async );
6376 if (wait_handle) ret = wait_async( wait_handle, FALSE );
6379 if (needs_close) close( fd );
6380 return ret;
6384 /**************************************************************************
6385 * NtCancelIoFile (NTDLL.@)
6387 NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status )
6389 unsigned int status;
6391 TRACE( "%p %p\n", handle, io_status );
6393 SERVER_START_REQ( cancel_async )
6395 req->handle = wine_server_obj_handle( handle );
6396 req->only_thread = TRUE;
6397 if (!(status = wine_server_call( req )))
6399 io_status->Status = status;
6400 io_status->Information = 0;
6403 SERVER_END_REQ;
6405 return status;
6409 /**************************************************************************
6410 * NtCancelIoFileEx (NTDLL.@)
6412 NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status )
6414 unsigned int status;
6416 TRACE( "%p %p %p\n", handle, io, io_status );
6418 SERVER_START_REQ( cancel_async )
6420 req->handle = wine_server_obj_handle( handle );
6421 req->iosb = wine_server_client_ptr( io );
6422 if (!(status = wine_server_call( req )))
6424 io_status->Status = status;
6425 io_status->Information = 0;
6428 SERVER_END_REQ;
6430 return status;
6434 /**************************************************************************
6435 * NtCancelSynchronousIoFile (NTDLL.@)
6437 NTSTATUS WINAPI NtCancelSynchronousIoFile( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status )
6439 unsigned int status;
6441 TRACE( "(%p %p %p)\n", handle, io, io_status );
6443 SERVER_START_REQ( cancel_sync )
6445 req->handle = wine_server_obj_handle( handle );
6446 req->iosb = wine_server_client_ptr( io );
6447 status = wine_server_call( req );
6449 SERVER_END_REQ;
6451 io_status->Status = status;
6452 io_status->Information = 0;
6453 return status;
6456 /******************************************************************
6457 * NtLockFile (NTDLL.@)
6459 NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void* apc_user,
6460 IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
6461 LARGE_INTEGER *count, ULONG *key, BOOLEAN dont_wait, BOOLEAN exclusive )
6463 static int warn;
6464 unsigned int ret;
6465 HANDLE handle;
6466 BOOLEAN async;
6468 if (apc || io_status || key)
6470 FIXME("Unimplemented yet parameter\n");
6471 return STATUS_NOT_IMPLEMENTED;
6473 if (apc_user && !warn++) FIXME("I/O completion on lock not implemented yet\n");
6475 for (;;)
6477 SERVER_START_REQ( lock_file )
6479 req->handle = wine_server_obj_handle( file );
6480 req->offset = offset->QuadPart;
6481 req->count = count->QuadPart;
6482 req->shared = !exclusive;
6483 req->wait = !dont_wait;
6484 ret = wine_server_call( req );
6485 handle = wine_server_ptr_handle( reply->handle );
6486 async = reply->overlapped;
6488 SERVER_END_REQ;
6489 if (ret != STATUS_PENDING)
6491 if (!ret && event) NtSetEvent( event, NULL );
6492 return ret;
6494 if (async)
6496 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6497 if (handle) NtClose( handle );
6498 return STATUS_PENDING;
6500 if (handle)
6502 NtWaitForSingleObject( handle, FALSE, NULL );
6503 NtClose( handle );
6505 else /* Unix lock conflict, sleep a bit and retry */
6507 LARGE_INTEGER time;
6508 time.QuadPart = -100 * (ULONGLONG)10000;
6509 NtDelayExecution( FALSE, &time );
6515 /******************************************************************
6516 * NtUnlockFile (NTDLL.@)
6518 NTSTATUS WINAPI NtUnlockFile( HANDLE handle, IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
6519 LARGE_INTEGER *count, ULONG *key )
6521 unsigned int status;
6523 TRACE( "%p %s %s\n",
6524 handle, wine_dbgstr_longlong(offset->QuadPart), wine_dbgstr_longlong(count->QuadPart) );
6526 if (io_status || key)
6528 FIXME("Unimplemented yet parameter\n");
6529 return STATUS_NOT_IMPLEMENTED;
6532 SERVER_START_REQ( unlock_file )
6534 req->handle = wine_server_obj_handle( handle );
6535 req->offset = offset->QuadPart;
6536 req->count = count->QuadPart;
6537 status = wine_server_call( req );
6539 SERVER_END_REQ;
6540 return status;
6544 static BOOL read_changes_apc( void *user, ULONG_PTR *info, unsigned int *status )
6546 struct async_fileio_read_changes *fileio = user;
6547 int size = 0;
6549 if (*status == STATUS_ALERTED)
6551 SERVER_START_REQ( read_change )
6553 req->handle = wine_server_obj_handle( fileio->io.handle );
6554 wine_server_set_reply( req, fileio->data, fileio->data_size );
6555 *status = wine_server_call( req );
6556 size = wine_server_reply_size( reply );
6558 SERVER_END_REQ;
6560 if (*status == STATUS_SUCCESS && fileio->buffer)
6562 FILE_NOTIFY_INFORMATION *pfni = fileio->buffer;
6563 int i, left = fileio->buffer_size;
6564 DWORD *last_entry_offset = NULL;
6565 struct filesystem_event *event = (struct filesystem_event*)fileio->data;
6567 while (size && left >= sizeof(*pfni))
6569 DWORD len = (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR);
6571 /* convert to an NT style path */
6572 for (i = 0; i < event->len; i++)
6573 if (event->name[i] == '/') event->name[i] = '\\';
6575 pfni->Action = event->action;
6576 pfni->FileNameLength = ntdll_umbstowcs( event->name, event->len, pfni->FileName, len );
6577 last_entry_offset = &pfni->NextEntryOffset;
6579 if (pfni->FileNameLength == len) break;
6581 i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
6582 pfni->FileNameLength *= sizeof(WCHAR);
6583 pfni->NextEntryOffset = i;
6584 pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
6585 left -= i;
6587 i = (offsetof(struct filesystem_event, name[event->len])
6588 + sizeof(int)-1) / sizeof(int) * sizeof(int);
6589 event = (struct filesystem_event*)((char*)event + i);
6590 size -= i;
6593 if (size)
6595 *status = STATUS_NOTIFY_ENUM_DIR;
6596 size = 0;
6598 else
6600 if (last_entry_offset) *last_entry_offset = 0;
6601 size = fileio->buffer_size - left;
6604 else
6606 *status = STATUS_NOTIFY_ENUM_DIR;
6607 size = 0;
6611 *info = size;
6612 release_fileio( &fileio->io );
6613 return TRUE;
6616 #define FILE_NOTIFY_ALL ( \
6617 FILE_NOTIFY_CHANGE_FILE_NAME | \
6618 FILE_NOTIFY_CHANGE_DIR_NAME | \
6619 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
6620 FILE_NOTIFY_CHANGE_SIZE | \
6621 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6622 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6623 FILE_NOTIFY_CHANGE_CREATION | \
6624 FILE_NOTIFY_CHANGE_SECURITY )
6626 /******************************************************************************
6627 * NtNotifyChangeDirectoryFile (NTDLL.@)
6629 NTSTATUS WINAPI NtNotifyChangeDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
6630 void *apc_context, IO_STATUS_BLOCK *iosb, void *buffer,
6631 ULONG buffer_size, ULONG filter, BOOLEAN subtree )
6633 struct async_fileio_read_changes *fileio;
6634 unsigned int status;
6635 ULONG size = max( 4096, buffer_size );
6637 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6638 handle, event, apc, apc_context, iosb, buffer, (int)buffer_size, (int)filter, subtree );
6640 if (!iosb) return STATUS_ACCESS_VIOLATION;
6641 if (filter == 0 || (filter & ~FILE_NOTIFY_ALL)) return STATUS_INVALID_PARAMETER;
6643 fileio = (struct async_fileio_read_changes *)alloc_fileio(
6644 offsetof(struct async_fileio_read_changes, data[size]), read_changes_apc, handle );
6645 if (!fileio) return STATUS_NO_MEMORY;
6647 fileio->buffer = buffer;
6648 fileio->buffer_size = buffer_size;
6649 fileio->data_size = size;
6651 SERVER_START_REQ( read_directory_changes )
6653 req->filter = filter;
6654 req->want_data = (buffer != NULL);
6655 req->subtree = subtree;
6656 req->async = server_async( handle, &fileio->io, event, apc, apc_context, iosb_client_ptr(iosb) );
6657 status = wine_server_call( req );
6659 SERVER_END_REQ;
6661 if (status != STATUS_PENDING) free( fileio );
6662 return status;
6666 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6667 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6668 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION *info, const char *fstypename,
6669 unsigned int flags )
6671 if (!strcmp("cd9660", fstypename) || !strcmp("udf", fstypename))
6673 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6674 /* Don't assume read-only, let the mount options set it below */
6675 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6677 else if (!strcmp("nfs", fstypename) || !strcmp("nwfs", fstypename) ||
6678 !strcmp("smbfs", fstypename) || !strcmp("afpfs", fstypename))
6680 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6681 info->Characteristics |= FILE_REMOTE_DEVICE;
6683 else if (!strcmp("procfs", fstypename))
6684 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6685 else
6686 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6688 if (flags & MNT_RDONLY)
6689 info->Characteristics |= FILE_READ_ONLY_DEVICE;
6691 if (!(flags & MNT_LOCAL))
6693 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6694 info->Characteristics |= FILE_REMOTE_DEVICE;
6697 #endif
6699 static inline BOOL is_device_placeholder( int fd )
6701 static const char wine_placeholder[] = "Wine device placeholder";
6702 char buffer[sizeof(wine_placeholder)-1];
6704 if (pread( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1)
6705 return FALSE;
6706 return !memcmp( buffer, wine_placeholder, sizeof(wine_placeholder) - 1 );
6709 NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info )
6711 struct stat st;
6713 info->Characteristics = 0;
6714 if (fstat( fd, &st ) < 0) return errno_to_status( errno );
6715 if (S_ISCHR( st.st_mode ))
6717 info->DeviceType = FILE_DEVICE_UNKNOWN;
6718 #ifdef linux
6719 switch(major(st.st_rdev))
6721 case MEM_MAJOR:
6722 info->DeviceType = FILE_DEVICE_NULL;
6723 break;
6724 case TTY_MAJOR:
6725 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6726 break;
6727 case LP_MAJOR:
6728 info->DeviceType = FILE_DEVICE_PARALLEL_PORT;
6729 break;
6730 case SCSI_TAPE_MAJOR:
6731 info->DeviceType = FILE_DEVICE_TAPE;
6732 break;
6734 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6736 int d_type;
6737 if (ioctl(fd, FIODTYPE, &d_type) == 0)
6739 switch(d_type)
6741 case D_TAPE:
6742 info->DeviceType = FILE_DEVICE_TAPE;
6743 break;
6744 case D_DISK:
6745 info->DeviceType = FILE_DEVICE_DISK;
6746 break;
6747 case D_TTY:
6748 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6749 break;
6750 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6751 case D_MEM:
6752 info->DeviceType = FILE_DEVICE_NULL;
6753 break;
6754 #endif
6756 /* no special d_type for parallel ports */
6759 #endif
6761 else if (S_ISBLK( st.st_mode ))
6763 info->DeviceType = FILE_DEVICE_DISK;
6765 else if (S_ISFIFO( st.st_mode ) || S_ISSOCK( st.st_mode ))
6767 info->DeviceType = FILE_DEVICE_NAMED_PIPE;
6769 else if (is_device_placeholder( fd ))
6771 info->DeviceType = FILE_DEVICE_DISK;
6773 else /* regular file or directory */
6775 #if defined(linux) && defined(HAVE_FSTATFS)
6776 struct statfs stfs;
6778 /* check for floppy disk */
6779 if (major(st.st_dev) == FLOPPY_MAJOR)
6780 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6782 if (fstatfs( fd, &stfs ) < 0) stfs.f_type = 0;
6783 switch (stfs.f_type)
6785 case 0x9660: /* iso9660 */
6786 case 0x9fa1: /* supermount */
6787 case 0x15013346: /* udf */
6788 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6789 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6790 break;
6791 case 0x6969: /* nfs */
6792 case 0xff534d42: /* cifs */
6793 case 0xfe534d42: /* smb2 */
6794 case 0x517b: /* smbfs */
6795 case 0x564c: /* ncpfs */
6796 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6797 info->Characteristics |= FILE_REMOTE_DEVICE;
6798 break;
6799 case 0x1373: /* devfs */
6800 case 0x9fa0: /* procfs */
6801 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6802 break;
6803 case 0x01021994: /* tmpfs */
6804 case 0x28cd3d45: /* cramfs */
6805 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6806 * filesystems are rare on Windows, and some programs refuse to
6807 * recognize them as valid. */
6808 default:
6809 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6810 break;
6812 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6813 struct statfs stfs;
6815 if (fstatfs( fd, &stfs ) < 0)
6816 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6817 else
6818 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flags );
6819 #elif defined(__NetBSD__)
6820 struct statvfs stfs;
6822 if (fstatvfs( fd, &stfs) < 0)
6823 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6824 else
6825 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flag );
6826 #elif defined(sun)
6827 /* Use dkio to work out device types */
6829 # include <sys/dkio.h>
6830 # include <sys/vtoc.h>
6831 struct dk_cinfo dkinf;
6832 int retval = ioctl(fd, DKIOCINFO, &dkinf);
6833 if(retval==-1){
6834 WARN("Unable to get disk device type information - assuming a disk like device\n");
6835 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6837 switch (dkinf.dki_ctype)
6839 case DKC_CDROM:
6840 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6841 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6842 break;
6843 case DKC_NCRFLOPPY:
6844 case DKC_SMSFLOPPY:
6845 case DKC_INTEL82072:
6846 case DKC_INTEL82077:
6847 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6848 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6849 break;
6850 case DKC_MD:
6851 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6852 * filesystems are rare on Windows, and some programs refuse to
6853 * recognize them as valid. */
6854 default:
6855 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6858 #else
6859 static int warned;
6860 if (!warned++) FIXME( "device info not properly supported on this platform\n" );
6861 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6862 #endif
6863 info->Characteristics |= FILE_DEVICE_IS_MOUNTED;
6865 return STATUS_SUCCESS;
6869 /******************************************************************************
6870 * NtQueryVolumeInformationFile (NTDLL.@)
6872 NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
6873 void *buffer, ULONG length,
6874 FS_INFORMATION_CLASS info_class )
6876 int fd, needs_close;
6877 unsigned int status;
6879 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
6880 if (status == STATUS_BAD_DEVICE_TYPE)
6882 struct async_irp *async;
6883 HANDLE wait_handle;
6885 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
6886 return STATUS_NO_MEMORY;
6887 async->buffer = buffer;
6888 async->size = length;
6890 SERVER_START_REQ( get_volume_info )
6892 req->async = server_async( handle, &async->io, NULL, NULL, NULL, iosb_client_ptr(io) );
6893 req->handle = wine_server_obj_handle( handle );
6894 req->info_class = info_class;
6895 wine_server_set_reply( req, buffer, length );
6896 status = wine_server_call( req );
6897 if (status != STATUS_PENDING)
6899 io->Status = status;
6900 io->Information = wine_server_reply_size( reply );
6902 wait_handle = wine_server_ptr_handle( reply->wait );
6904 SERVER_END_REQ;
6905 if (status != STATUS_PENDING) free( async );
6906 if (wait_handle) status = wait_async( wait_handle, FALSE );
6907 return status;
6909 else if (status) return io->Status = status;
6911 io->Information = 0;
6913 switch( info_class )
6915 case FileFsLabelInformation:
6916 FIXME( "%p: label info not supported\n", handle );
6917 status = STATUS_NOT_IMPLEMENTED;
6918 break;
6920 case FileFsSizeInformation:
6921 if (length < sizeof(FILE_FS_SIZE_INFORMATION))
6922 status = STATUS_BUFFER_TOO_SMALL;
6923 else
6925 FILE_FS_SIZE_INFORMATION *info = buffer;
6926 FILE_FS_FULL_SIZE_INFORMATION full_info;
6928 if ((status = get_full_size_info(fd, &full_info)) == STATUS_SUCCESS)
6930 info->TotalAllocationUnits = full_info.TotalAllocationUnits;
6931 info->AvailableAllocationUnits = full_info.CallerAvailableAllocationUnits;
6932 info->SectorsPerAllocationUnit = full_info.SectorsPerAllocationUnit;
6933 info->BytesPerSector = full_info.BytesPerSector;
6934 io->Information = sizeof(*info);
6937 break;
6939 case FileFsDeviceInformation:
6940 if (length < sizeof(FILE_FS_DEVICE_INFORMATION))
6941 status = STATUS_BUFFER_TOO_SMALL;
6942 else
6944 FILE_FS_DEVICE_INFORMATION *info = buffer;
6946 if ((status = get_device_info( fd, info )) == STATUS_SUCCESS)
6947 io->Information = sizeof(*info);
6949 break;
6951 case FileFsAttributeInformation:
6953 static const WCHAR fatW[] = {'F','A','T'};
6954 static const WCHAR fat32W[] = {'F','A','T','3','2'};
6955 static const WCHAR ntfsW[] = {'N','T','F','S'};
6956 static const WCHAR cdfsW[] = {'C','D','F','S'};
6957 static const WCHAR udfW[] = {'U','D','F'};
6959 FILE_FS_ATTRIBUTE_INFORMATION *info = buffer;
6960 struct mountmgr_unix_drive drive;
6961 enum mountmgr_fs_type fs_type = MOUNTMGR_FS_TYPE_NTFS;
6963 if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
6965 status = STATUS_INFO_LENGTH_MISMATCH;
6966 break;
6969 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) )) fs_type = drive.fs_type;
6970 else
6972 struct statfs stfs;
6974 if (!fstatfs( fd, &stfs ))
6976 #if defined(linux) && defined(HAVE_FSTATFS)
6977 switch (stfs.f_type)
6979 case 0x9660:
6980 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6981 break;
6982 case 0x15013346:
6983 fs_type = MOUNTMGR_FS_TYPE_UDF;
6984 break;
6985 case 0x4d44:
6986 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6987 break;
6989 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6990 if (!strcmp( stfs.f_fstypename, "cd9660" ))
6991 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6992 else if (!strcmp( stfs.f_fstypename, "udf" ))
6993 fs_type = MOUNTMGR_FS_TYPE_UDF;
6994 else if (!strcmp( stfs.f_fstypename, "msdos" )) /* FreeBSD < 5, Apple */
6995 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6996 else if (!strcmp( stfs.f_fstypename, "msdosfs" )) /* FreeBSD >= 5 */
6997 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6998 #endif
7002 switch (fs_type)
7004 case MOUNTMGR_FS_TYPE_ISO9660:
7005 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
7006 info->MaximumComponentNameLength = 221;
7007 info->FileSystemNameLength = min( sizeof(cdfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
7008 memcpy(info->FileSystemName, cdfsW, info->FileSystemNameLength);
7009 break;
7010 case MOUNTMGR_FS_TYPE_UDF:
7011 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
7012 info->MaximumComponentNameLength = 255;
7013 info->FileSystemNameLength = min( sizeof(udfW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
7014 memcpy(info->FileSystemName, udfW, info->FileSystemNameLength);
7015 break;
7016 case MOUNTMGR_FS_TYPE_FAT:
7017 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
7018 info->MaximumComponentNameLength = 255;
7019 info->FileSystemNameLength = min( sizeof(fatW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
7020 memcpy(info->FileSystemName, fatW, info->FileSystemNameLength);
7021 break;
7022 case MOUNTMGR_FS_TYPE_FAT32:
7023 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
7024 info->MaximumComponentNameLength = 255;
7025 info->FileSystemNameLength = min( sizeof(fat32W), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
7026 memcpy(info->FileSystemName, fat32W, info->FileSystemNameLength);
7027 break;
7028 default:
7029 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
7030 info->MaximumComponentNameLength = 255;
7031 info->FileSystemNameLength = min( sizeof(ntfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
7032 memcpy(info->FileSystemName, ntfsW, info->FileSystemNameLength);
7033 break;
7036 io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
7037 status = STATUS_SUCCESS;
7038 break;
7041 case FileFsVolumeInformation:
7043 FILE_FS_VOLUME_INFORMATION *info = buffer;
7044 ULONGLONG data[64];
7045 struct mountmgr_unix_drive *drive = (struct mountmgr_unix_drive *)data;
7046 const WCHAR *label;
7048 if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
7050 status = STATUS_INFO_LENGTH_MISMATCH;
7051 break;
7054 if (get_mountmgr_fs_info( handle, fd, drive, sizeof(data) ))
7056 status = STATUS_NOT_IMPLEMENTED;
7057 break;
7060 label = (WCHAR *)((char *)drive + drive->label_offset);
7061 info->VolumeCreationTime.QuadPart = 0; /* FIXME */
7062 info->VolumeSerialNumber = drive->serial;
7063 info->VolumeLabelLength = min( wcslen( label ) * sizeof(WCHAR),
7064 length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
7065 info->SupportsObjects = (drive->fs_type == MOUNTMGR_FS_TYPE_NTFS);
7066 memcpy( info->VolumeLabel, label, info->VolumeLabelLength );
7067 io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
7068 status = STATUS_SUCCESS;
7069 break;
7072 case FileFsControlInformation:
7073 FIXME( "%p: control info not supported\n", handle );
7074 status = STATUS_NOT_IMPLEMENTED;
7075 break;
7077 case FileFsFullSizeInformation:
7078 if (length < sizeof(FILE_FS_FULL_SIZE_INFORMATION))
7079 status = STATUS_BUFFER_TOO_SMALL;
7080 else
7082 FILE_FS_FULL_SIZE_INFORMATION *info = buffer;
7083 if ((status = get_full_size_info(fd, info)) == STATUS_SUCCESS)
7084 io->Information = sizeof(*info);
7086 break;
7088 case FileFsObjectIdInformation:
7089 FIXME( "%p: object id info not supported\n", handle );
7090 status = STATUS_NOT_IMPLEMENTED;
7091 break;
7093 case FileFsMaximumInformation:
7094 FIXME( "%p: maximum info not supported\n", handle );
7095 status = STATUS_NOT_IMPLEMENTED;
7096 break;
7098 default:
7099 status = STATUS_INVALID_PARAMETER;
7100 break;
7102 if (needs_close) close( fd );
7103 return io->Status = status;
7107 /******************************************************************************
7108 * NtSetVolumeInformationFile (NTDLL.@)
7110 NTSTATUS WINAPI NtSetVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, void *info,
7111 ULONG length, FS_INFORMATION_CLASS class )
7113 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle, io, info, (int)length, class );
7114 return STATUS_SUCCESS;
7118 /******************************************************************
7119 * NtQueryEaFile (NTDLL.@)
7121 NTSTATUS WINAPI NtQueryEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
7122 BOOLEAN single_entry, void *list, ULONG list_len,
7123 ULONG *index, BOOLEAN restart )
7125 int fd, needs_close;
7126 NTSTATUS status;
7128 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) semi-stub\n",
7129 handle, io, buffer, (int)length, single_entry, list, (int)list_len, index, restart );
7131 if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
7132 return status;
7134 if (buffer && length)
7135 memset( buffer, 0, length );
7137 if (needs_close) close( fd );
7138 return STATUS_NO_EAS_ON_FILE;
7142 /******************************************************************
7143 * NtSetEaFile (NTDLL.@)
7145 NTSTATUS WINAPI NtSetEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length )
7147 FIXME( "(%p,%p,%p,%d) stub\n", handle, io, buffer, (int)length );
7148 return STATUS_ACCESS_DENIED;
7152 /* convert type information from server format; helper for NtQueryObject */
7153 static void *put_object_type_info( OBJECT_TYPE_INFORMATION *p, struct object_type_info *info )
7155 const ULONG align = sizeof(DWORD_PTR) - 1;
7157 memset( p, 0, sizeof(*p) );
7158 p->TypeName.Buffer = (WCHAR *)(p + 1);
7159 p->TypeName.Length = info->name_len;
7160 p->TypeName.MaximumLength = info->name_len + sizeof(WCHAR);
7161 p->TotalNumberOfObjects = info->obj_count;
7162 p->TotalNumberOfHandles = info->handle_count;
7163 p->HighWaterNumberOfObjects = info->obj_max;
7164 p->HighWaterNumberOfHandles = info->handle_max;
7165 p->TypeIndex = info->index + 2;
7166 p->GenericMapping.GenericRead = info->mapping.read;
7167 p->GenericMapping.GenericWrite = info->mapping.write;
7168 p->GenericMapping.GenericExecute = info->mapping.exec;
7169 p->GenericMapping.GenericAll = info->mapping.all;
7170 p->ValidAccessMask = info->valid_access;
7171 memcpy( p->TypeName.Buffer, info + 1, info->name_len );
7172 p->TypeName.Buffer[info->name_len / sizeof(WCHAR)] = 0;
7173 return (char *)(p + 1) + ((p->TypeName.MaximumLength + align) & ~align);
7176 /**************************************************************************
7177 * NtQueryObject (NTDLL.@)
7179 NTSTATUS WINAPI NtQueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
7180 void *ptr, ULONG len, ULONG *used_len )
7182 unsigned int status;
7184 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle, info_class, ptr, (int)len, used_len);
7186 if (used_len) *used_len = 0;
7188 switch (info_class)
7190 case ObjectBasicInformation:
7192 OBJECT_BASIC_INFORMATION *p = ptr;
7194 if (len < sizeof(*p)) return STATUS_INFO_LENGTH_MISMATCH;
7196 SERVER_START_REQ( get_object_info )
7198 req->handle = wine_server_obj_handle( handle );
7199 status = wine_server_call( req );
7200 if (status == STATUS_SUCCESS)
7202 memset( p, 0, sizeof(*p) );
7203 p->GrantedAccess = reply->access;
7204 p->PointerCount = reply->ref_count;
7205 p->HandleCount = reply->handle_count;
7206 if (used_len) *used_len = sizeof(*p);
7209 SERVER_END_REQ;
7210 break;
7213 case ObjectNameInformation:
7215 OBJECT_NAME_INFORMATION *p = ptr;
7216 char *unix_name;
7217 WCHAR *nt_name;
7219 /* first try as a file object */
7221 if (!(status = server_get_unix_name( handle, &unix_name )))
7223 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
7225 ULONG size = (wcslen(nt_name) + 1) * sizeof(WCHAR);
7226 if (len < sizeof(*p)) status = STATUS_INFO_LENGTH_MISMATCH;
7227 else if (len < sizeof(*p) + size) status = STATUS_BUFFER_OVERFLOW;
7228 else
7230 p->Name.Buffer = (WCHAR *)(p + 1);
7231 p->Name.Length = size - sizeof(WCHAR);
7232 p->Name.MaximumLength = size;
7233 wcscpy( p->Name.Buffer, nt_name );
7235 if (used_len) *used_len = sizeof(*p) + size;
7236 free( nt_name );
7238 free( unix_name );
7239 break;
7241 else if (status != STATUS_OBJECT_TYPE_MISMATCH) break;
7243 /* not a file, treat as a generic object */
7245 SERVER_START_REQ( get_object_name )
7247 req->handle = wine_server_obj_handle( handle );
7248 if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
7249 status = wine_server_call( req );
7250 if (status == STATUS_SUCCESS)
7252 if (!reply->total) /* no name */
7254 if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
7255 else memset( p, 0, sizeof(*p) );
7256 if (used_len) *used_len = sizeof(*p);
7258 else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
7260 if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
7261 status = STATUS_INFO_LENGTH_MISMATCH;
7263 else
7265 ULONG res = wine_server_reply_size( reply );
7266 p->Name.Buffer = (WCHAR *)(p + 1);
7267 p->Name.Length = res;
7268 p->Name.MaximumLength = res + sizeof(WCHAR);
7269 p->Name.Buffer[res / sizeof(WCHAR)] = 0;
7270 if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
7274 SERVER_END_REQ;
7275 break;
7278 case ObjectTypeInformation:
7280 OBJECT_TYPE_INFORMATION *p = ptr;
7281 char buffer[sizeof(struct object_type_info) + 64];
7282 struct object_type_info *info = (struct object_type_info *)buffer;
7284 SERVER_START_REQ( get_object_type )
7286 req->handle = wine_server_obj_handle( handle );
7287 wine_server_set_reply( req, buffer, sizeof(buffer) );
7288 status = wine_server_call( req );
7290 SERVER_END_REQ;
7291 if (status) break;
7292 if (sizeof(*p) + info->name_len + sizeof(WCHAR) <= len)
7294 put_object_type_info( p, info );
7295 if (used_len) *used_len = sizeof(*p) + p->TypeName.MaximumLength;
7297 else
7299 if (used_len) *used_len = sizeof(*p) + info->name_len + sizeof(WCHAR);
7300 status = STATUS_INFO_LENGTH_MISMATCH;
7302 break;
7305 case ObjectTypesInformation:
7307 OBJECT_TYPES_INFORMATION *types = ptr;
7308 OBJECT_TYPE_INFORMATION *p;
7309 struct object_type_info *buffer;
7310 /* assume at most 32 types, with an average 16-char name */
7311 UINT size = 32 * (sizeof(struct object_type_info) + 16 * sizeof(WCHAR));
7312 UINT i, count, pos, total, align = sizeof(DWORD_PTR) - 1;
7314 buffer = malloc( size );
7315 SERVER_START_REQ( get_object_types )
7317 wine_server_set_reply( req, buffer, size );
7318 status = wine_server_call( req );
7319 count = reply->count;
7321 SERVER_END_REQ;
7322 if (!status)
7324 if (len >= sizeof(*types)) types->NumberOfTypes = count;
7325 total = (sizeof(*types) + align) & ~align;
7326 p = (OBJECT_TYPE_INFORMATION *)((char *)ptr + total);
7327 for (i = pos = 0; i < count; i++)
7329 struct object_type_info *info = (struct object_type_info *)((char *)buffer + pos);
7330 pos += sizeof(*info) + ((info->name_len + 3) & ~3);
7331 total += sizeof(*p) + ((info->name_len + sizeof(WCHAR) + align) & ~align);
7332 if (total <= len) p = put_object_type_info( p, info );
7334 if (used_len) *used_len = total;
7335 if (total > len) status = STATUS_INFO_LENGTH_MISMATCH;
7337 else if (status == STATUS_BUFFER_OVERFLOW) FIXME( "size %u too small\n", size );
7339 free( buffer );
7340 break;
7343 case ObjectHandleFlagInformation:
7345 OBJECT_HANDLE_FLAG_INFORMATION* p = ptr;
7347 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
7349 SERVER_START_REQ( set_handle_info )
7351 req->handle = wine_server_obj_handle( handle );
7352 req->flags = 0;
7353 req->mask = 0;
7354 status = wine_server_call( req );
7355 if (status == STATUS_SUCCESS)
7357 p->Inherit = (reply->old_flags & HANDLE_FLAG_INHERIT) != 0;
7358 p->ProtectFromClose = (reply->old_flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0;
7359 if (used_len) *used_len = sizeof(*p);
7362 SERVER_END_REQ;
7363 break;
7366 default:
7367 FIXME("Unsupported information class %u\n", info_class);
7368 status = STATUS_NOT_IMPLEMENTED;
7369 break;
7371 return status;
7375 /**************************************************************************
7376 * NtSetInformationObject (NTDLL.@)
7378 NTSTATUS WINAPI NtSetInformationObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
7379 void *ptr, ULONG len )
7381 unsigned int status;
7383 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle, info_class, ptr, (int)len);
7385 switch (info_class)
7387 case ObjectHandleFlagInformation:
7389 OBJECT_HANDLE_FLAG_INFORMATION* p = ptr;
7391 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
7393 SERVER_START_REQ( set_handle_info )
7395 req->handle = wine_server_obj_handle( handle );
7396 req->mask = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
7397 if (p->Inherit) req->flags |= HANDLE_FLAG_INHERIT;
7398 if (p->ProtectFromClose) req->flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
7399 status = wine_server_call( req );
7401 SERVER_END_REQ;
7402 break;
7405 default:
7406 FIXME("Unsupported information class %u\n", info_class);
7407 status = STATUS_NOT_IMPLEMENTED;
7408 break;
7410 return status;