ntdll: Use st_rdev for special devices in get_mountmgr_fs_info.
[wine.git] / dlls / ntdll / unix / file.c
blobe147fb47391444b660af768ba4763282dbc9ce86
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"
28 #include "wine/port.h"
30 #include <assert.h>
31 #include <sys/types.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <limits.h>
40 #ifdef HAVE_MNTENT_H
41 #include <mntent.h>
42 #endif
43 #ifdef HAVE_POLL_H
44 #include <poll.h>
45 #endif
46 #ifdef HAVE_SYS_STAT_H
47 # include <sys/stat.h>
48 #endif
49 #ifdef HAVE_SYS_STATVFS_H
50 # include <sys/statvfs.h>
51 #endif
52 #ifdef HAVE_SYS_SYSCALL_H
53 # include <sys/syscall.h>
54 #endif
55 #ifdef HAVE_SYS_SOCKET_H
56 #include <sys/socket.h>
57 #endif
58 #ifdef HAVE_SYS_TIME_H
59 # include <sys/time.h>
60 #endif
61 #ifdef HAVE_SYS_ATTR_H
62 #include <sys/attr.h>
63 #endif
64 #ifdef MAJOR_IN_MKDEV
65 # include <sys/mkdev.h>
66 #elif defined(MAJOR_IN_SYSMACROS)
67 # include <sys/sysmacros.h>
68 #endif
69 #ifdef HAVE_SYS_VNODE_H
70 # ifdef HAVE_STDINT_H
71 # include <stdint.h> /* needed for kfreebsd */
72 # endif
73 /* Work around a conflict with Solaris' system list defined in sys/list.h. */
74 #define list SYSLIST
75 #define list_next SYSLIST_NEXT
76 #define list_prev SYSLIST_PREV
77 #define list_head SYSLIST_HEAD
78 #define list_tail SYSLIST_TAIL
79 #define list_move_tail SYSLIST_MOVE_TAIL
80 #define list_remove SYSLIST_REMOVE
81 #include <sys/vnode.h>
82 #undef list
83 #undef list_next
84 #undef list_prev
85 #undef list_head
86 #undef list_tail
87 #undef list_move_tail
88 #undef list_remove
89 #endif
90 #ifdef HAVE_SYS_IOCTL_H
91 #include <sys/ioctl.h>
92 #endif
93 #ifdef HAVE_LINUX_IOCTL_H
94 #include <linux/ioctl.h>
95 #endif
96 #ifdef HAVE_LINUX_MAJOR_H
97 # include <linux/major.h>
98 #endif
99 #ifdef HAVE_SYS_PARAM_H
100 #include <sys/param.h>
101 #endif
102 #ifdef HAVE_SYS_CONF_H
103 #include <sys/conf.h>
104 #endif
105 #ifdef HAVE_SYS_MOUNT_H
106 #include <sys/mount.h>
107 #endif
108 #ifdef HAVE_SYS_STATFS_H
109 #include <sys/statfs.h>
110 #endif
111 #include <time.h>
112 #ifdef HAVE_UNISTD_H
113 # include <unistd.h>
114 #endif
116 #include "ntstatus.h"
117 #define WIN32_NO_STATUS
118 #define NONAMELESSUNION
119 #include "windef.h"
120 #include "winnt.h"
121 #include "winioctl.h"
122 #include "winternl.h"
123 #include "ddk/ntddk.h"
124 #include "ddk/ntddser.h"
125 #include "ddk/wdm.h"
126 #define WINE_MOUNTMGR_EXTENSIONS
127 #include "ddk/mountmgr.h"
128 #include "wine/server.h"
129 #include "wine/list.h"
130 #include "wine/debug.h"
131 #include "unix_private.h"
133 WINE_DEFAULT_DEBUG_CHANNEL(file);
134 WINE_DECLARE_DEBUG_CHANNEL(winediag);
136 #define MAX_DOS_DRIVES 26
138 #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1)
139 #define FILE_USE_FILE_POINTER_POSITION ((LONGLONG)-2)
141 /* just in case... */
142 #undef VFAT_IOCTL_READDIR_BOTH
143 #undef EXT2_IOC_GETFLAGS
144 #undef EXT4_CASEFOLD_FL
146 #ifdef linux
148 /* We want the real kernel dirent structure, not the libc one */
149 typedef struct
151 long d_ino;
152 long d_off;
153 unsigned short d_reclen;
154 char d_name[256];
155 } KERNEL_DIRENT;
157 /* Define the VFAT ioctl to get both short and long file names */
158 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
160 /* Define the ext2 ioctl for handling extra attributes */
161 #define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
163 /* Case-insensitivity attribute */
164 #define EXT4_CASEFOLD_FL 0x40000000
166 #ifndef O_DIRECTORY
167 # define O_DIRECTORY 0200000 /* must be directory */
168 #endif
170 #ifndef AT_NO_AUTOMOUNT
171 #define AT_NO_AUTOMOUNT 0x800
172 #endif
174 #endif /* linux */
176 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
178 #define INVALID_NT_CHARS '*','?','<','>','|','"'
179 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
181 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
183 #define MAX_IGNORED_FILES 4
185 struct file_identity
187 dev_t dev;
188 ino_t ino;
191 static struct file_identity ignored_files[MAX_IGNORED_FILES];
192 static unsigned int ignored_files_count;
194 union file_directory_info
196 ULONG next;
197 FILE_DIRECTORY_INFORMATION dir;
198 FILE_BOTH_DIRECTORY_INFORMATION both;
199 FILE_FULL_DIRECTORY_INFORMATION full;
200 FILE_ID_BOTH_DIRECTORY_INFORMATION id_both;
201 FILE_ID_FULL_DIRECTORY_INFORMATION id_full;
202 FILE_ID_GLOBAL_TX_DIR_INFORMATION id_tx;
203 FILE_NAMES_INFORMATION names;
206 struct dir_data_buffer
208 struct dir_data_buffer *next; /* next buffer in the list */
209 unsigned int size; /* total size of the buffer */
210 unsigned int pos; /* current position in the buffer */
211 char data[1];
214 struct dir_data_names
216 const WCHAR *long_name; /* long file name in Unicode */
217 const WCHAR *short_name; /* short file name in Unicode */
218 const char *unix_name; /* Unix file name in host encoding */
221 struct dir_data
223 unsigned int size; /* size of the names array */
224 unsigned int count; /* count of used entries in the names array */
225 unsigned int pos; /* current reading position in the names array */
226 struct file_identity id; /* directory file identity */
227 struct dir_data_names *names; /* directory file names */
228 struct dir_data_buffer *buffer; /* head of data buffers list */
231 static const unsigned int dir_data_buffer_initial_size = 4096;
232 static const unsigned int dir_data_cache_initial_size = 256;
233 static const unsigned int dir_data_names_initial_size = 64;
235 static struct dir_data **dir_data_cache;
236 static unsigned int dir_data_cache_size;
238 static BOOL show_dot_files;
239 static mode_t start_umask;
241 /* at some point we may want to allow Winelib apps to set this */
242 static const BOOL is_case_sensitive = FALSE;
244 static struct file_identity windir;
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 inline BOOL has_wildcard( const UNICODE_STRING *mask )
325 int i;
327 if (!mask) return TRUE;
328 for (i = 0; i < mask->Length / sizeof(WCHAR); i++)
329 if (mask->Buffer[i] == '*' || mask->Buffer[i] == '?') return TRUE;
330 return FALSE;
333 NTSTATUS errno_to_status( int err )
335 TRACE( "errno = %d\n", err );
336 switch (err)
338 case EAGAIN: return STATUS_SHARING_VIOLATION;
339 case EBADF: return STATUS_INVALID_HANDLE;
340 case EBUSY: return STATUS_DEVICE_BUSY;
341 case ENOSPC: return STATUS_DISK_FULL;
342 case EPERM:
343 case EROFS:
344 case EACCES: return STATUS_ACCESS_DENIED;
345 case ENOTDIR: return STATUS_OBJECT_PATH_NOT_FOUND;
346 case ENOENT: return STATUS_OBJECT_NAME_NOT_FOUND;
347 case EISDIR: return STATUS_INVALID_DEVICE_REQUEST;
348 case EMFILE:
349 case ENFILE: return STATUS_TOO_MANY_OPENED_FILES;
350 case EINVAL: return STATUS_INVALID_PARAMETER;
351 case ENOTEMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
352 case EPIPE: return STATUS_PIPE_DISCONNECTED;
353 case EIO: return STATUS_DEVICE_NOT_READY;
354 #ifdef ENOMEDIUM
355 case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE;
356 #endif
357 case ENXIO: return STATUS_NO_SUCH_DEVICE;
358 case ENOTTY:
359 case EOPNOTSUPP:return STATUS_NOT_SUPPORTED;
360 case ECONNRESET:return STATUS_PIPE_DISCONNECTED;
361 case EFAULT: return STATUS_ACCESS_VIOLATION;
362 case ESPIPE: return STATUS_ILLEGAL_FUNCTION;
363 case ELOOP: return STATUS_REPARSE_POINT_NOT_RESOLVED;
364 #ifdef ETIME /* Missing on FreeBSD */
365 case ETIME: return STATUS_IO_TIMEOUT;
366 #endif
367 case ENOEXEC: /* ?? */
368 case EEXIST: /* ?? */
369 default:
370 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
371 return STATUS_UNSUCCESSFUL;
375 /* get space from the current directory data buffer, allocating a new one if necessary */
376 static void *get_dir_data_space( struct dir_data *data, unsigned int size )
378 struct dir_data_buffer *buffer = data->buffer;
379 void *ret;
381 if (!buffer || size > buffer->size - buffer->pos)
383 unsigned int new_size = buffer ? buffer->size * 2 : dir_data_buffer_initial_size;
384 if (new_size < size) new_size = size;
385 if (!(buffer = malloc( offsetof( struct dir_data_buffer, data[new_size] ) ))) return NULL;
386 buffer->pos = 0;
387 buffer->size = new_size;
388 buffer->next = data->buffer;
389 data->buffer = buffer;
391 ret = buffer->data + buffer->pos;
392 buffer->pos += size;
393 return ret;
396 /* add a string to the directory data buffer */
397 static const char *add_dir_data_nameA( struct dir_data *data, const char *name )
399 /* keep buffer data WCHAR-aligned */
400 char *ptr = get_dir_data_space( data, (strlen( name ) + sizeof(WCHAR)) & ~(sizeof(WCHAR) - 1) );
401 if (ptr) strcpy( ptr, name );
402 return ptr;
405 /* add a Unicode string to the directory data buffer */
406 static const WCHAR *add_dir_data_nameW( struct dir_data *data, const WCHAR *name )
408 WCHAR *ptr = get_dir_data_space( data, (wcslen( name ) + 1) * sizeof(WCHAR) );
409 if (ptr) wcscpy( ptr, name );
410 return ptr;
413 /* add an entry to the directory names array */
414 static BOOL add_dir_data_names( struct dir_data *data, const WCHAR *long_name,
415 const WCHAR *short_name, const char *unix_name )
417 static const WCHAR empty[1];
418 struct dir_data_names *names = data->names;
420 if (data->count >= data->size)
422 unsigned int new_size = max( data->size * 2, dir_data_names_initial_size );
424 if (!(names = realloc( names, new_size * sizeof(*names) ))) return FALSE;
425 data->size = new_size;
426 data->names = names;
429 if (short_name[0])
431 if (!(names[data->count].short_name = add_dir_data_nameW( data, short_name ))) return FALSE;
433 else names[data->count].short_name = empty;
435 if (!(names[data->count].long_name = add_dir_data_nameW( data, long_name ))) return FALSE;
436 if (!(names[data->count].unix_name = add_dir_data_nameA( data, unix_name ))) return FALSE;
437 data->count++;
438 return TRUE;
441 /* free the complete directory data structure */
442 static void free_dir_data( struct dir_data *data )
444 struct dir_data_buffer *buffer, *next;
446 if (!data) return;
448 for (buffer = data->buffer; buffer; buffer = next)
450 next = buffer->next;
451 free( buffer );
453 free( data->names );
454 free( data );
458 /* support for a directory queue for filesystem searches */
460 struct dir_name
462 struct list entry;
463 char name[1];
466 static struct list dir_queue = LIST_INIT( dir_queue );
468 static NTSTATUS add_dir_to_queue( const char *name )
470 int len = strlen( name ) + 1;
471 struct dir_name *dir = malloc( offsetof( struct dir_name, name[len] ));
472 if (!dir) return STATUS_NO_MEMORY;
473 strcpy( dir->name, name );
474 list_add_tail( &dir_queue, &dir->entry );
475 return STATUS_SUCCESS;
478 static NTSTATUS next_dir_in_queue( char *name )
480 struct list *head = list_head( &dir_queue );
481 if (head)
483 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
484 strcpy( name, dir->name );
485 list_remove( &dir->entry );
486 free( dir );
487 return STATUS_SUCCESS;
489 return STATUS_OBJECT_NAME_NOT_FOUND;
492 static void flush_dir_queue(void)
494 struct list *head;
496 while ((head = list_head( &dir_queue )))
498 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
499 list_remove( &dir->entry );
500 free( dir );
505 #ifdef __ANDROID__
507 static char *unescape_field( char *str )
509 char *in, *out;
511 for (in = out = str; *in; in++, out++)
513 *out = *in;
514 if (in[0] == '\\')
516 if (in[1] == '\\')
518 out[0] = '\\';
519 in++;
521 else if (in[1] == '0' && in[2] == '4' && in[3] == '0')
523 out[0] = ' ';
524 in += 3;
526 else if (in[1] == '0' && in[2] == '1' && in[3] == '1')
528 out[0] = '\t';
529 in += 3;
531 else if (in[1] == '0' && in[2] == '1' && in[3] == '2')
533 out[0] = '\n';
534 in += 3;
536 else if (in[1] == '1' && in[2] == '3' && in[3] == '4')
538 out[0] = '\\';
539 in += 3;
543 *out = '\0';
545 return str;
548 static inline char *get_field( char **str )
550 char *ret;
552 ret = strsep( str, " \t" );
553 if (*str) *str += strspn( *str, " \t" );
555 return ret;
557 /************************************************************************
558 * getmntent_replacement
560 * getmntent replacement for Android.
562 * NB returned static buffer is not thread safe; protect with mnt_mutex.
564 static struct mntent *getmntent_replacement( FILE *f )
566 static struct mntent entry;
567 static char buf[4096];
568 char *p, *start;
572 if (!fgets( buf, sizeof(buf), f )) return NULL;
573 p = strchr( buf, '\n' );
574 if (p) *p = '\0';
575 else /* Partially unread line, move file ptr to end */
577 char tmp[1024];
578 while (fgets( tmp, sizeof(tmp), f ))
579 if (strchr( tmp, '\n' )) break;
581 start = buf + strspn( buf, " \t" );
582 } while (start[0] == '\0' || start[0] == '#');
584 p = get_field( &start );
585 entry.mnt_fsname = p ? unescape_field( p ) : (char *)"";
587 p = get_field( &start );
588 entry.mnt_dir = p ? unescape_field( p ) : (char *)"";
590 p = get_field( &start );
591 entry.mnt_type = p ? unescape_field( p ) : (char *)"";
593 p = get_field( &start );
594 entry.mnt_opts = p ? unescape_field( p ) : (char *)"";
596 p = get_field( &start );
597 entry.mnt_freq = p ? atoi(p) : 0;
599 p = get_field( &start );
600 entry.mnt_passno = p ? atoi(p) : 0;
602 return &entry;
604 #define getmntent getmntent_replacement
605 #endif
607 /***********************************************************************
608 * parse_mount_entries
610 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
613 #ifdef sun
614 #include <sys/vfstab.h>
615 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
617 struct vfstab entry;
618 struct stat st;
619 char *device;
621 while (! getvfsent( f, &entry ))
623 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
624 if (!strcmp( entry.vfs_fstype, "nfs" ) ||
625 !strcmp( entry.vfs_fstype, "smbfs" ) ||
626 !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
628 if (stat( entry.vfs_mountp, &st ) == -1) continue;
629 if (st.st_dev != dev || st.st_ino != ino) continue;
630 if (!strcmp( entry.vfs_fstype, "fd" ))
632 if ((device = strstr( entry.vfs_mntopts, "dev=" )))
634 char *p = strchr( device + 4, ',' );
635 if (p) *p = 0;
636 return device + 4;
639 else
640 return entry.vfs_special;
642 return NULL;
644 #endif
646 #ifdef linux
647 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
649 struct mntent *entry;
650 struct stat st;
651 char *device;
653 while ((entry = getmntent( f )))
655 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
656 if (!strcmp( entry->mnt_type, "nfs" ) ||
657 !strcmp( entry->mnt_type, "cifs" ) ||
658 !strcmp( entry->mnt_type, "smbfs" ) ||
659 !strcmp( entry->mnt_type, "ncpfs" )) continue;
661 if (stat( entry->mnt_dir, &st ) == -1) continue;
662 if (st.st_dev != dev || st.st_ino != ino) continue;
663 if (!strcmp( entry->mnt_type, "supermount" ))
665 if ((device = strstr( entry->mnt_opts, "dev=" )))
667 char *p = strchr( device + 4, ',' );
668 if (p) *p = 0;
669 return device + 4;
672 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
674 /* if device is a regular file check for a loop mount */
675 if ((device = strstr( entry->mnt_opts, "loop=" )))
677 char *p = strchr( device + 5, ',' );
678 if (p) *p = 0;
679 return device + 5;
682 else
683 return entry->mnt_fsname;
685 return NULL;
687 #endif
689 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
690 #include <fstab.h>
691 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
693 struct fstab *entry;
694 struct stat st;
696 while ((entry = getfsent()))
698 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
699 if (!strcmp( entry->fs_vfstype, "nfs" ) ||
700 !strcmp( entry->fs_vfstype, "smbfs" ) ||
701 !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
703 if (stat( entry->fs_file, &st ) == -1) continue;
704 if (st.st_dev != dev || st.st_ino != ino) continue;
705 return entry->fs_spec;
707 return NULL;
709 #endif
711 #ifdef sun
712 #include <sys/mnttab.h>
713 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
715 struct mnttab entry;
716 struct stat st;
717 char *device;
720 while (( ! getmntent( f, &entry) ))
722 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
723 if (!strcmp( entry.mnt_fstype, "nfs" ) ||
724 !strcmp( entry.mnt_fstype, "smbfs" ) ||
725 !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
727 if (stat( entry.mnt_mountp, &st ) == -1) continue;
728 if (st.st_dev != dev || st.st_ino != ino) continue;
729 if (!strcmp( entry.mnt_fstype, "fd" ))
731 if ((device = strstr( entry.mnt_mntopts, "dev=" )))
733 char *p = strchr( device + 4, ',' );
734 if (p) *p = 0;
735 return device + 4;
738 else
739 return entry.mnt_special;
741 return NULL;
743 #endif
745 /***********************************************************************
746 * get_default_drive_device
748 * Return the default device to use for a given drive mount point.
750 static char *get_default_drive_device( const char *root )
752 char *ret = NULL;
754 #ifdef linux
755 FILE *f;
756 char *device = NULL;
757 int fd, res = -1;
758 struct stat st;
760 /* try to open it first to force it to get mounted */
761 if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
763 res = fstat( fd, &st );
764 close( fd );
766 /* now try normal stat just in case */
767 if (res == -1) res = stat( root, &st );
768 if (res == -1) return NULL;
770 mutex_lock( &mnt_mutex );
772 #ifdef __ANDROID__
773 if ((f = fopen( "/proc/mounts", "r" )))
775 device = parse_mount_entries( f, st.st_dev, st.st_ino );
776 fclose( f );
778 #else
779 if ((f = fopen( "/etc/mtab", "r" )))
781 device = parse_mount_entries( f, st.st_dev, st.st_ino );
782 fclose( f );
784 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
785 if (!device && (f = fopen( "/etc/fstab", "r" )))
787 device = parse_mount_entries( f, st.st_dev, st.st_ino );
788 fclose( f );
790 #endif
791 if (device) ret = strdup( device );
792 mutex_unlock( &mnt_mutex );
794 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
795 char *device = NULL;
796 int fd, res = -1;
797 struct stat st;
799 /* try to open it first to force it to get mounted */
800 if ((fd = open( root, O_RDONLY )) != -1)
802 res = fstat( fd, &st );
803 close( fd );
805 /* now try normal stat just in case */
806 if (res == -1) res = stat( root, &st );
807 if (res == -1) return NULL;
809 mutex_lock( &mnt_mutex );
811 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
812 * pass NULL. Leave the argument in for symmetry.
814 device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
815 if (device) ret = strdup( device );
816 mutex_unlock( &mnt_mutex );
818 #elif defined( sun )
819 FILE *f;
820 char *device = NULL;
821 int fd, res = -1;
822 struct stat st;
824 /* try to open it first to force it to get mounted */
825 if ((fd = open( root, O_RDONLY )) != -1)
827 res = fstat( fd, &st );
828 close( fd );
830 /* now try normal stat just in case */
831 if (res == -1) res = stat( root, &st );
832 if (res == -1) return NULL;
834 mutex_lock( &mnt_mutex );
836 if ((f = fopen( "/etc/mnttab", "r" )))
838 device = parse_mount_entries( f, st.st_dev, st.st_ino);
839 fclose( f );
841 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
842 if (!device && (f = fopen( "/etc/vfstab", "r" )))
844 device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
845 fclose( f );
847 if (device) ret = strdup( device );
848 mutex_unlock( &mnt_mutex );
850 #elif defined(__APPLE__)
851 struct statfs *mntStat;
852 struct stat st;
853 int i;
854 int mntSize;
855 dev_t dev;
856 ino_t ino;
857 static const char path_bsd_device[] = "/dev/disk";
858 int res;
860 res = stat( root, &st );
861 if (res == -1) return NULL;
863 dev = st.st_dev;
864 ino = st.st_ino;
866 mutex_lock( &mnt_mutex );
868 mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
870 for (i = 0; i < mntSize && !ret; i++)
872 if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
873 if (st.st_dev != dev || st.st_ino != ino) continue;
875 /* FIXME add support for mounted network drive */
876 if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
878 /* set return value to the corresponding raw BSD node */
879 ret = malloc( strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
880 if (ret)
882 strcpy(ret, "/dev/r");
883 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
887 mutex_unlock( &mnt_mutex );
888 #else
889 static int warned;
890 if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
891 #endif
892 return ret;
896 /***********************************************************************
897 * get_device_mount_point
899 * Return the current mount point for a device.
901 static char *get_device_mount_point( dev_t dev )
903 char *ret = NULL;
905 #ifdef linux
906 FILE *f;
908 mutex_lock( &mnt_mutex );
910 #ifdef __ANDROID__
911 if ((f = fopen( "/proc/mounts", "r" )))
912 #else
913 if ((f = fopen( "/etc/mtab", "r" )))
914 #endif
916 struct mntent *entry;
917 struct stat st;
918 char *p, *device;
920 while ((entry = getmntent( f )))
922 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
923 if (!strcmp( entry->mnt_type, "nfs" ) ||
924 !strcmp( entry->mnt_type, "cifs" ) ||
925 !strcmp( entry->mnt_type, "smbfs" ) ||
926 !strcmp( entry->mnt_type, "ncpfs" )) continue;
928 if (!strcmp( entry->mnt_type, "supermount" ))
930 if ((device = strstr( entry->mnt_opts, "dev=" )))
932 device += 4;
933 if ((p = strchr( device, ',' ))) *p = 0;
936 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
938 /* if device is a regular file check for a loop mount */
939 if ((device = strstr( entry->mnt_opts, "loop=" )))
941 device += 5;
942 if ((p = strchr( device, ',' ))) *p = 0;
945 else device = entry->mnt_fsname;
947 if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
949 ret = strdup( entry->mnt_dir );
950 break;
953 fclose( f );
955 mutex_unlock( &mnt_mutex );
956 #elif defined(__APPLE__)
957 struct statfs *entry;
958 struct stat st;
959 int i, size;
961 mutex_lock( &mnt_mutex );
963 size = getmntinfo( &entry, MNT_NOWAIT );
964 for (i = 0; i < size; i++)
966 if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
967 if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
969 ret = strdup( entry[i].f_mntonname );
970 break;
973 mutex_unlock( &mnt_mutex );
974 #else
975 static int warned;
976 if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
977 #endif
978 return ret;
982 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
983 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
985 struct get_fsid
987 ULONG size;
988 dev_t dev;
989 fsid_t fsid;
992 struct fs_cache
994 dev_t dev;
995 fsid_t fsid;
996 BOOLEAN case_sensitive;
997 } fs_cache[64];
999 struct vol_caps
1001 ULONG size;
1002 vol_capabilities_attr_t caps;
1005 /***********************************************************************
1006 * look_up_fs_cache
1008 * Checks if the specified file system is in the cache.
1010 static struct fs_cache *look_up_fs_cache( dev_t dev )
1012 int i;
1013 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1014 if (fs_cache[i].dev == dev)
1015 return fs_cache+i;
1016 return NULL;
1019 /***********************************************************************
1020 * add_fs_cache
1022 * Adds the specified file system to the cache.
1024 static void add_fs_cache( dev_t dev, fsid_t fsid, BOOLEAN case_sensitive )
1026 int i;
1027 struct fs_cache *entry = look_up_fs_cache( dev );
1028 static int once = 0;
1029 if (entry)
1031 /* Update the cache */
1032 entry->fsid = fsid;
1033 entry->case_sensitive = case_sensitive;
1034 return;
1037 /* Add a new entry */
1038 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1039 if (fs_cache[i].dev == 0)
1041 /* This entry is empty, use it */
1042 fs_cache[i].dev = dev;
1043 fs_cache[i].fsid = fsid;
1044 fs_cache[i].case_sensitive = case_sensitive;
1045 return;
1048 /* Cache is out of space, warn */
1049 if (!once++)
1050 WARN( "FS cache is out of space, expect performance problems\n" );
1053 /***********************************************************************
1054 * get_dir_case_sensitivity_attr
1056 * Checks if the volume containing the specified directory is case
1057 * sensitive or not. Uses getattrlist(2).
1059 static int get_dir_case_sensitivity_attr( const char *dir )
1061 char *mntpoint;
1062 struct attrlist attr;
1063 struct vol_caps caps;
1064 struct get_fsid get_fsid;
1065 struct fs_cache *entry;
1067 /* First get the FS ID of the volume */
1068 attr.bitmapcount = ATTR_BIT_MAP_COUNT;
1069 attr.reserved = 0;
1070 attr.commonattr = ATTR_CMN_DEVID|ATTR_CMN_FSID;
1071 attr.volattr = attr.dirattr = attr.fileattr = attr.forkattr = 0;
1072 get_fsid.size = 0;
1073 if (getattrlist( dir, &attr, &get_fsid, sizeof(get_fsid), 0 ) != 0 ||
1074 get_fsid.size != sizeof(get_fsid))
1075 return -1;
1076 /* Try to look it up in the cache */
1077 entry = look_up_fs_cache( get_fsid.dev );
1078 if (entry && !memcmp( &entry->fsid, &get_fsid.fsid, sizeof(fsid_t) ))
1079 /* Cache lookup succeeded */
1080 return entry->case_sensitive;
1081 /* Cache is stale at this point, we have to update it */
1083 mntpoint = get_device_mount_point( get_fsid.dev );
1084 /* Now look up the case-sensitivity */
1085 attr.commonattr = 0;
1086 attr.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES;
1087 if (getattrlist( mntpoint, &attr, &caps, sizeof(caps), 0 ) < 0)
1089 free( mntpoint );
1090 add_fs_cache( get_fsid.dev, get_fsid.fsid, TRUE );
1091 return TRUE;
1093 free( mntpoint );
1094 if (caps.size == sizeof(caps) &&
1095 (caps.caps.valid[VOL_CAPABILITIES_FORMAT] &
1096 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING)) ==
1097 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING))
1099 BOOLEAN ret;
1101 if ((caps.caps.capabilities[VOL_CAPABILITIES_FORMAT] &
1102 VOL_CAP_FMT_CASE_SENSITIVE) != VOL_CAP_FMT_CASE_SENSITIVE)
1103 ret = FALSE;
1104 else
1105 ret = TRUE;
1106 /* Update the cache */
1107 add_fs_cache( get_fsid.dev, get_fsid.fsid, ret );
1108 return ret;
1110 return FALSE;
1112 #endif
1114 /***********************************************************************
1115 * get_dir_case_sensitivity_stat
1117 * Checks if the volume containing the specified directory is case
1118 * sensitive or not. Uses (f)statfs(2), statvfs(2), fstatat(2), or ioctl(2).
1120 static BOOLEAN get_dir_case_sensitivity_stat( const char *dir )
1122 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1123 struct statfs stfs;
1125 if (statfs( dir, &stfs ) == -1) return TRUE;
1126 /* Assume these file systems are always case insensitive.*/
1127 if (!strcmp( stfs.f_fstypename, "fusefs" ) &&
1128 !strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1129 return FALSE;
1130 /* msdosfs was case-insensitive since FreeBSD 8, if not earlier */
1131 if (!strcmp( stfs.f_fstypename, "msdosfs" ) ||
1132 /* older CIFS protocol versions uppercase filename on the client,
1133 * newer versions should be case-insensitive on the server anyway */
1134 !strcmp( stfs.f_fstypename, "smbfs" ))
1135 return FALSE;
1136 /* no ntfs-3g: modern fusefs has no way to report the filesystem on FreeBSD
1137 * no cd9660 or udf, they're case-sensitive on FreeBSD
1139 #ifdef __APPLE__
1140 if (!strcmp( stfs.f_fstypename, "msdos" ) ||
1141 !strcmp( stfs.f_fstypename, "cd9660" ) ||
1142 !strcmp( stfs.f_fstypename, "udf" ) ||
1143 !strcmp( stfs.f_fstypename, "ntfs" ))
1144 return FALSE;
1145 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1146 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_fssubtype == 0 ||
1147 stfs.f_fssubtype == 1 ||
1148 stfs.f_fssubtype == 128))
1149 return FALSE;
1150 #else
1151 /* The field says "reserved", but a quick look at the kernel source
1152 * tells us that this "reserved" field is really the same as the
1153 * "fssubtype" field from the inode64 structure (see munge_statfs()
1154 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1156 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_reserved1 == 0 ||
1157 stfs.f_reserved1 == 1 ||
1158 stfs.f_reserved1 == 128))
1159 return FALSE;
1160 #endif
1161 #endif
1162 return TRUE;
1164 #elif defined(__NetBSD__)
1165 struct statvfs stfs;
1167 if (statvfs( dir, &stfs ) == -1) return TRUE;
1168 /* Only assume CIOPFS is case insensitive. */
1169 if (strcmp( stfs.f_fstypename, "fusefs" ) ||
1170 strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1171 return FALSE;
1172 return TRUE;
1174 #elif defined(__linux__)
1175 BOOLEAN sens = TRUE;
1176 struct statfs stfs;
1177 struct stat st;
1178 int fd, flags;
1180 if ((fd = open( dir, O_RDONLY | O_NONBLOCK | O_LARGEFILE )) == -1)
1181 return TRUE;
1183 if (ioctl( fd, EXT2_IOC_GETFLAGS, &flags ) != -1 && (flags & EXT4_CASEFOLD_FL))
1185 sens = FALSE;
1187 else if (fstatfs( fd, &stfs ) == 0 && /* CIOPFS is case insensitive. Instead of */
1188 stfs.f_type == 0x65735546 /* FUSE_SUPER_MAGIC */ && /* parsing mtab to discover if the FUSE FS */
1189 fstatat( fd, ".ciopfs", &st, AT_NO_AUTOMOUNT ) == 0) /* is CIOPFS, look for .ciopfs in the dir. */
1191 sens = FALSE;
1194 close( fd );
1195 return sens;
1196 #else
1197 return TRUE;
1198 #endif
1202 /***********************************************************************
1203 * get_dir_case_sensitivity
1205 * Checks if the volume containing the specified directory is case
1206 * sensitive or not. Uses multiple methods, depending on platform.
1208 static BOOLEAN get_dir_case_sensitivity( const char *dir )
1210 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1211 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1212 int case_sensitive = get_dir_case_sensitivity_attr( dir );
1213 if (case_sensitive != -1) return case_sensitive;
1214 #endif
1215 return get_dir_case_sensitivity_stat( dir );
1219 /***********************************************************************
1220 * is_hidden_file
1222 * Check if the specified file should be hidden based on its name and the show dot files option.
1224 static BOOL is_hidden_file( const UNICODE_STRING *name )
1226 WCHAR *p, *end;
1228 if (show_dot_files) return FALSE;
1230 end = p = name->Buffer + name->Length/sizeof(WCHAR);
1231 while (p > name->Buffer && IS_SEPARATOR(p[-1])) p--;
1232 while (p > name->Buffer && !IS_SEPARATOR(p[-1])) p--;
1233 if (p == end || *p != '.') return FALSE;
1234 /* make sure it isn't '.' or '..' */
1235 if (p + 1 == end) return FALSE;
1236 if (p[1] == '.' && p + 2 == end) return FALSE;
1237 return TRUE;
1241 /***********************************************************************
1242 * hash_short_file_name
1244 * Transform a Unix file name into a hashed DOS name. If the name is not a valid
1245 * DOS name, it is replaced by a hashed version that fits in 8.3 format.
1246 * 'buffer' must be at least 12 characters long.
1247 * Returns length of short name in bytes; short name is NOT null-terminated.
1249 static ULONG hash_short_file_name( const WCHAR *name, int length, LPWSTR buffer )
1251 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1253 LPCWSTR p, ext, end = name + length;
1254 LPWSTR dst;
1255 unsigned short hash;
1256 int i;
1258 /* Compute the hash code of the file name */
1259 /* If you know something about hash functions, feel free to */
1260 /* insert a better algorithm here... */
1261 if (!is_case_sensitive)
1263 for (p = name, hash = 0xbeef; p < end - 1; p++)
1264 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p) ^ (towlower(p[1]) << 8);
1265 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p); /* Last character */
1267 else
1269 for (p = name, hash = 0xbeef; p < end - 1; p++)
1270 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
1271 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
1274 /* Find last dot for start of the extension */
1275 for (p = name + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
1277 /* Copy first 4 chars, replacing invalid chars with '_' */
1278 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
1280 if (p == end || p == ext) break;
1281 *dst++ = is_invalid_dos_char(*p) ? '_' : *p;
1283 /* Pad to 5 chars with '~' */
1284 while (i-- >= 0) *dst++ = '~';
1286 /* Insert hash code converted to 3 ASCII chars */
1287 *dst++ = hash_chars[(hash >> 10) & 0x1f];
1288 *dst++ = hash_chars[(hash >> 5) & 0x1f];
1289 *dst++ = hash_chars[hash & 0x1f];
1291 /* Copy the first 3 chars of the extension (if any) */
1292 if (ext)
1294 *dst++ = '.';
1295 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
1296 *dst++ = is_invalid_dos_char(*ext) ? '_' : *ext;
1298 return dst - buffer;
1302 /***********************************************************************
1303 * match_filename
1305 * Check a long file name against a mask.
1307 * Tests (done in W95 DOS shell - case insensitive):
1308 * *.txt test1.test.txt *
1309 * *st1* test1.txt *
1310 * *.t??????.t* test1.ta.tornado.txt *
1311 * *tornado* test1.ta.tornado.txt *
1312 * t*t test1.ta.tornado.txt *
1313 * ?est* test1.txt *
1314 * ?est??? test1.txt -
1315 * *test1.txt* test1.txt *
1316 * h?l?o*t.dat hellothisisatest.dat *
1318 static BOOLEAN match_filename( const WCHAR *name, int length, const UNICODE_STRING *mask_str )
1320 BOOL mismatch;
1321 const WCHAR *mask = mask_str->Buffer;
1322 const WCHAR *name_end = name + length;
1323 const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
1324 const WCHAR *lastjoker = NULL;
1325 const WCHAR *next_to_retry = NULL;
1327 while (name < name_end && mask < mask_end)
1329 switch(*mask)
1331 case '*':
1332 mask++;
1333 while (mask < mask_end && *mask == '*') mask++; /* Skip consecutive '*' */
1334 if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
1335 lastjoker = mask;
1337 /* skip to the next match after the joker(s) */
1338 if (is_case_sensitive)
1339 while (name < name_end && (*name != *mask)) name++;
1340 else
1341 while (name < name_end && (towupper(*name) != towupper(*mask))) name++;
1342 next_to_retry = name;
1343 break;
1344 case '?':
1345 case '>':
1346 mask++;
1347 name++;
1348 break;
1349 default:
1350 if (is_case_sensitive) mismatch = (*mask != *name);
1351 else mismatch = (towupper(*mask) != towupper(*name));
1353 if (!mismatch)
1355 mask++;
1356 name++;
1357 if (mask == mask_end)
1359 if (name == name_end) return TRUE;
1360 if (lastjoker) mask = lastjoker;
1363 else /* mismatch ! */
1365 if (lastjoker) /* we had an '*', so we can try unlimitedly */
1367 mask = lastjoker;
1369 /* this scan sequence was a mismatch, so restart
1370 * 1 char after the first char we checked last time */
1371 next_to_retry++;
1372 name = next_to_retry;
1374 else return FALSE; /* bad luck */
1376 break;
1379 while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
1380 mask++; /* Ignore trailing '.' or '*' in mask */
1381 return (name == name_end && mask == mask_end);
1385 /***********************************************************************
1386 * is_legal_8dot3_name
1388 * Simplified version of RtlIsNameLegalDOS8Dot3.
1390 static BOOLEAN is_legal_8dot3_name( const WCHAR *name, int len )
1392 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,':','/','\\',0 };
1393 int i, dot = -1;
1395 if (len > 12) return FALSE;
1397 /* a starting . is invalid, except for . and .. */
1398 if (len > 0 && name[0] == '.') return (len == 1 || (len == 2 && name[1] == '.'));
1400 for (i = 0; i < len; i++)
1402 if (name[i] > 0x7f) return FALSE;
1403 if (wcschr( invalid_chars, name[i] )) return FALSE;
1404 if (name[i] == '.')
1406 if (dot != -1) return FALSE;
1407 dot = i;
1411 if (dot == -1) return (len <= 8);
1412 if (dot > 8) return FALSE;
1413 return (len - dot > 1 && len - dot < 5);
1417 /***********************************************************************
1418 * append_entry
1420 * Add a file to the directory data if it matches the mask.
1422 static BOOL append_entry( struct dir_data *data, const char *long_name,
1423 const char *short_name, const UNICODE_STRING *mask )
1425 int long_len, short_len;
1426 WCHAR long_nameW[MAX_DIR_ENTRY_LEN + 1];
1427 WCHAR short_nameW[13];
1429 long_len = ntdll_umbstowcs( long_name, strlen(long_name), long_nameW, ARRAY_SIZE(long_nameW) );
1430 if (long_len == ARRAY_SIZE(long_nameW)) return TRUE;
1431 long_nameW[long_len] = 0;
1433 if (short_name)
1435 short_len = ntdll_umbstowcs( short_name, strlen(short_name),
1436 short_nameW, ARRAY_SIZE( short_nameW ) - 1 );
1438 else /* generate a short name if necessary */
1440 short_len = 0;
1441 if (!is_legal_8dot3_name( long_nameW, long_len ))
1442 short_len = hash_short_file_name( long_nameW, long_len, short_nameW );
1444 short_nameW[short_len] = 0;
1445 wcsupr( short_nameW );
1447 TRACE( "long %s short %s mask %s\n",
1448 debugstr_w( long_nameW ), debugstr_w( short_nameW ), debugstr_us( mask ));
1450 if (mask && !match_filename( long_nameW, long_len, mask ))
1452 if (!short_len) return TRUE; /* no short name to match */
1453 if (!match_filename( short_nameW, short_len, mask )) return TRUE;
1456 return add_dir_data_names( data, long_nameW, short_nameW, long_name );
1460 /* fetch the attributes of a file */
1461 static inline ULONG get_file_attributes( const struct stat *st )
1463 ULONG attr;
1465 if (S_ISDIR(st->st_mode))
1466 attr = FILE_ATTRIBUTE_DIRECTORY;
1467 else
1468 attr = FILE_ATTRIBUTE_ARCHIVE;
1469 if (!(st->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
1470 attr |= FILE_ATTRIBUTE_READONLY;
1471 return attr;
1475 static BOOL fd_is_mount_point( int fd, const struct stat *st )
1477 struct stat parent;
1478 return S_ISDIR( st->st_mode ) && !fstatat( fd, "..", &parent, 0 )
1479 && (parent.st_dev != st->st_dev || parent.st_ino == st->st_ino);
1483 /* get the stat info and file attributes for a file (by file descriptor) */
1484 static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr )
1486 int ret;
1488 *attr = 0;
1489 ret = fstat( fd, st );
1490 if (ret == -1) return ret;
1491 *attr |= get_file_attributes( st );
1492 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1493 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st ))
1494 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1495 return ret;
1499 /* get the stat info and file attributes for a file (by name) */
1500 static int get_file_info( const char *path, struct stat *st, ULONG *attr )
1502 char *parent_path;
1503 int ret;
1505 *attr = 0;
1506 ret = lstat( path, st );
1507 if (ret == -1) return ret;
1508 if (S_ISLNK( st->st_mode ))
1510 ret = stat( path, st );
1511 if (ret == -1) return ret;
1512 /* is a symbolic link and a directory, consider these "reparse points" */
1513 if (S_ISDIR( st->st_mode )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1515 else if (S_ISDIR( st->st_mode ) && (parent_path = malloc( strlen(path) + 4 )))
1517 struct stat parent_st;
1519 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1520 strcpy( parent_path, path );
1521 strcat( parent_path, "/.." );
1522 if (!stat( parent_path, &parent_st )
1523 && (st->st_dev != parent_st.st_dev || st->st_ino == parent_st.st_ino))
1524 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1526 free( parent_path );
1528 *attr |= get_file_attributes( st );
1529 return ret;
1533 #if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
1534 static int futimens( int fd, const struct timespec spec[2] )
1536 return syscall( __NR_utimensat, fd, NULL, spec, 0 );
1538 #define HAVE_FUTIMENS
1539 #endif /* __ANDROID__ */
1541 #ifndef UTIME_OMIT
1542 #define UTIME_OMIT ((1 << 30) - 2)
1543 #endif
1545 static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
1546 const LARGE_INTEGER *atime, NTSTATUS *status )
1548 #ifdef HAVE_FUTIMENS
1549 struct timespec tv[2];
1551 tv[0].tv_sec = tv[1].tv_sec = 0;
1552 tv[0].tv_nsec = tv[1].tv_nsec = UTIME_OMIT;
1553 if (atime->QuadPart)
1555 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1556 tv[0].tv_nsec = (atime->QuadPart % 10000000) * 100;
1558 if (mtime->QuadPart)
1560 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1561 tv[1].tv_nsec = (mtime->QuadPart % 10000000) * 100;
1563 #ifdef __APPLE__
1564 if (!&futimens) return FALSE;
1565 #endif
1566 if (futimens( fd, tv ) == -1) *status = errno_to_status( errno );
1567 else *status = STATUS_SUCCESS;
1568 return TRUE;
1569 #else
1570 return FALSE;
1571 #endif
1575 static NTSTATUS set_file_times( int fd, const LARGE_INTEGER *mtime, const LARGE_INTEGER *atime )
1577 NTSTATUS status = STATUS_SUCCESS;
1578 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1579 struct timeval tv[2];
1580 struct stat st;
1581 #endif
1583 if (set_file_times_precise( fd, mtime, atime, &status ))
1584 return status;
1586 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1587 if (!atime->QuadPart || !mtime->QuadPart)
1590 tv[0].tv_sec = tv[0].tv_usec = 0;
1591 tv[1].tv_sec = tv[1].tv_usec = 0;
1592 if (!fstat( fd, &st ))
1594 tv[0].tv_sec = st.st_atime;
1595 tv[1].tv_sec = st.st_mtime;
1596 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1597 tv[0].tv_usec = st.st_atim.tv_nsec / 1000;
1598 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1599 tv[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
1600 #endif
1601 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1602 tv[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1603 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1604 tv[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
1605 #endif
1608 if (atime->QuadPart)
1610 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1611 tv[0].tv_usec = (atime->QuadPart % 10000000) / 10;
1613 if (mtime->QuadPart)
1615 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1616 tv[1].tv_usec = (mtime->QuadPart % 10000000) / 10;
1618 #ifdef HAVE_FUTIMES
1619 if (futimes( fd, tv ) == -1) status = errno_to_status( errno );
1620 #elif defined(HAVE_FUTIMESAT)
1621 if (futimesat( fd, NULL, tv ) == -1) status = errno_to_status( errno );
1622 #endif
1624 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1625 FIXME( "setting file times not supported\n" );
1626 status = STATUS_NOT_IMPLEMENTED;
1627 #endif
1628 return status;
1632 static inline void get_file_times( const struct stat *st, LARGE_INTEGER *mtime, LARGE_INTEGER *ctime,
1633 LARGE_INTEGER *atime, LARGE_INTEGER *creation )
1635 mtime->QuadPart = ticks_from_time_t( st->st_mtime );
1636 ctime->QuadPart = ticks_from_time_t( st->st_ctime );
1637 atime->QuadPart = ticks_from_time_t( st->st_atime );
1638 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1639 mtime->QuadPart += st->st_mtim.tv_nsec / 100;
1640 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1641 mtime->QuadPart += st->st_mtimespec.tv_nsec / 100;
1642 #endif
1643 #ifdef HAVE_STRUCT_STAT_ST_CTIM
1644 ctime->QuadPart += st->st_ctim.tv_nsec / 100;
1645 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1646 ctime->QuadPart += st->st_ctimespec.tv_nsec / 100;
1647 #endif
1648 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1649 atime->QuadPart += st->st_atim.tv_nsec / 100;
1650 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1651 atime->QuadPart += st->st_atimespec.tv_nsec / 100;
1652 #endif
1653 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1654 creation->QuadPart = ticks_from_time_t( st->st_birthtime );
1655 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
1656 creation->QuadPart += st->st_birthtim.tv_nsec / 100;
1657 #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1658 creation->QuadPart += st->st_birthtimespec.tv_nsec / 100;
1659 #endif
1660 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
1661 creation->QuadPart = ticks_from_time_t( st->__st_birthtime );
1662 #ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
1663 creation->QuadPart += st->__st_birthtim.tv_nsec / 100;
1664 #endif
1665 #else
1666 *creation = *mtime;
1667 #endif
1671 /* fill in the file information that depends on the stat and attribute info */
1672 static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr,
1673 FILE_INFORMATION_CLASS class )
1675 switch (class)
1677 case FileBasicInformation:
1679 FILE_BASIC_INFORMATION *info = ptr;
1681 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1682 &info->LastAccessTime, &info->CreationTime );
1683 info->FileAttributes = attr;
1685 break;
1686 case FileStandardInformation:
1688 FILE_STANDARD_INFORMATION *info = ptr;
1690 if ((info->Directory = S_ISDIR(st->st_mode)))
1692 info->AllocationSize.QuadPart = 0;
1693 info->EndOfFile.QuadPart = 0;
1694 info->NumberOfLinks = 1;
1696 else
1698 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1699 info->EndOfFile.QuadPart = st->st_size;
1700 info->NumberOfLinks = st->st_nlink;
1703 break;
1704 case FileInternalInformation:
1706 FILE_INTERNAL_INFORMATION *info = ptr;
1707 info->IndexNumber.QuadPart = st->st_ino;
1709 break;
1710 case FileEndOfFileInformation:
1712 FILE_END_OF_FILE_INFORMATION *info = ptr;
1713 info->EndOfFile.QuadPart = S_ISDIR(st->st_mode) ? 0 : st->st_size;
1715 break;
1716 case FileAllInformation:
1718 FILE_ALL_INFORMATION *info = ptr;
1719 fill_file_info( st, attr, &info->BasicInformation, FileBasicInformation );
1720 fill_file_info( st, attr, &info->StandardInformation, FileStandardInformation );
1721 fill_file_info( st, attr, &info->InternalInformation, FileInternalInformation );
1723 break;
1724 /* all directory structures start with the FileDirectoryInformation layout */
1725 case FileBothDirectoryInformation:
1726 case FileFullDirectoryInformation:
1727 case FileDirectoryInformation:
1729 FILE_DIRECTORY_INFORMATION *info = ptr;
1731 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1732 &info->LastAccessTime, &info->CreationTime );
1733 if (S_ISDIR(st->st_mode))
1735 info->AllocationSize.QuadPart = 0;
1736 info->EndOfFile.QuadPart = 0;
1738 else
1740 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1741 info->EndOfFile.QuadPart = st->st_size;
1743 info->FileAttributes = attr;
1745 break;
1746 case FileIdFullDirectoryInformation:
1748 FILE_ID_FULL_DIRECTORY_INFORMATION *info = ptr;
1749 info->FileId.QuadPart = st->st_ino;
1750 fill_file_info( st, attr, info, FileDirectoryInformation );
1752 break;
1753 case FileIdBothDirectoryInformation:
1755 FILE_ID_BOTH_DIRECTORY_INFORMATION *info = ptr;
1756 info->FileId.QuadPart = st->st_ino;
1757 fill_file_info( st, attr, info, FileDirectoryInformation );
1759 break;
1760 case FileIdGlobalTxDirectoryInformation:
1762 FILE_ID_GLOBAL_TX_DIR_INFORMATION *info = ptr;
1763 info->FileId.QuadPart = st->st_ino;
1764 fill_file_info( st, attr, info, FileDirectoryInformation );
1766 break;
1768 default:
1769 return STATUS_INVALID_INFO_CLASS;
1771 return STATUS_SUCCESS;
1775 static NTSTATUS server_get_unix_name( HANDLE handle, char **unix_name )
1777 data_size_t size = 1024;
1778 NTSTATUS ret;
1779 char *name;
1781 for (;;)
1783 if (!(name = malloc( size + 1 ))) return STATUS_NO_MEMORY;
1785 SERVER_START_REQ( get_handle_unix_name )
1787 req->handle = wine_server_obj_handle( handle );
1788 wine_server_set_reply( req, name, size );
1789 ret = wine_server_call( req );
1790 size = reply->name_len;
1792 SERVER_END_REQ;
1794 if (!ret)
1796 name[size] = 0;
1797 *unix_name = name;
1798 break;
1800 free( name );
1801 if (ret != STATUS_BUFFER_OVERFLOW) break;
1803 return ret;
1806 static NTSTATUS fill_name_info( const char *unix_name, FILE_NAME_INFORMATION *info, LONG *name_len )
1808 WCHAR *nt_name;
1809 NTSTATUS status;
1811 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
1813 const WCHAR *ptr = nt_name;
1814 const WCHAR *end = ptr + wcslen( nt_name );
1816 /* Skip the volume mount point. */
1817 while (ptr != end && *ptr == '\\') ++ptr;
1818 while (ptr != end && *ptr != '\\') ++ptr;
1819 while (ptr != end && *ptr == '\\') ++ptr;
1820 while (ptr != end && *ptr != '\\') ++ptr;
1822 info->FileNameLength = (end - ptr) * sizeof(WCHAR);
1823 if (*name_len < info->FileNameLength) status = STATUS_BUFFER_OVERFLOW;
1824 else *name_len = info->FileNameLength;
1826 memcpy( info->FileName, ptr, *name_len );
1827 free( nt_name );
1830 return status;
1834 static NTSTATUS server_get_file_info( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer,
1835 ULONG length, FILE_INFORMATION_CLASS info_class )
1837 SERVER_START_REQ( get_file_info )
1839 req->handle = wine_server_obj_handle( handle );
1840 req->info_class = info_class;
1841 wine_server_set_reply( req, buffer, length );
1842 io->u.Status = wine_server_call( req );
1843 io->Information = wine_server_reply_size( reply );
1845 SERVER_END_REQ;
1846 if (io->u.Status == STATUS_NOT_IMPLEMENTED)
1847 FIXME( "Unsupported info class %x\n", info_class );
1848 return io->u.Status;
1853 /* retrieve device/inode number for all the drives */
1854 static unsigned int get_drives_info( struct file_identity info[MAX_DOS_DRIVES] )
1856 static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
1857 static struct file_identity cache[MAX_DOS_DRIVES];
1858 static time_t last_update;
1859 static unsigned int nb_drives;
1860 unsigned int ret;
1861 time_t now = time(NULL);
1863 mutex_lock( &cache_mutex );
1864 if (now != last_update)
1866 char *buffer, *p;
1867 struct stat st;
1868 unsigned int i;
1870 if ((buffer = malloc( strlen(config_dir) + sizeof("/dosdevices/a:") )))
1872 strcpy( buffer, config_dir );
1873 strcat( buffer, "/dosdevices/a:" );
1874 p = buffer + strlen(buffer) - 2;
1876 for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
1878 *p = 'a' + i;
1879 if (!stat( buffer, &st ))
1881 cache[i].dev = st.st_dev;
1882 cache[i].ino = st.st_ino;
1883 nb_drives++;
1885 else
1887 cache[i].dev = 0;
1888 cache[i].ino = 0;
1891 free( buffer );
1893 last_update = now;
1895 memcpy( info, cache, sizeof(cache) );
1896 ret = nb_drives;
1897 mutex_unlock( &cache_mutex );
1898 return ret;
1902 /* Find a DOS device which can act as the root of "path".
1903 * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
1904 static int find_dos_device( const char *path )
1906 int len = strlen(path);
1907 int drive;
1908 char *buffer;
1909 struct stat st;
1910 struct file_identity info[MAX_DOS_DRIVES];
1911 dev_t dev_id;
1913 if (!get_drives_info( info )) return -1;
1915 if (stat( path, &st ) < 0) return -1;
1916 dev_id = st.st_dev;
1918 /* strip off trailing slashes */
1919 while (len > 1 && path[len - 1] == '/') len--;
1921 /* make a copy of the path */
1922 if (!(buffer = malloc( len + 1 ))) return -1;
1923 memcpy( buffer, path, len );
1924 buffer[len] = 0;
1926 for (;;)
1928 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
1930 if (st.st_dev != dev_id) break;
1932 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1934 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
1936 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
1937 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
1938 free( buffer );
1939 return drive;
1943 if (len <= 1) break; /* reached root */
1944 while (len > 1 && path[len - 1] != '/') len--;
1945 while (len > 1 && path[len - 1] == '/') len--;
1946 buffer[len] = 0;
1948 free( buffer );
1949 return -1;
1952 static NTSTATUS get_mountmgr_fs_info( HANDLE handle, int fd, struct mountmgr_unix_drive *drive, ULONG size )
1954 OBJECT_ATTRIBUTES attr;
1955 UNICODE_STRING string;
1956 char *unix_name;
1957 IO_STATUS_BLOCK io;
1958 HANDLE mountmgr;
1959 NTSTATUS status;
1960 int letter;
1962 if ((status = server_get_unix_name( handle, &unix_name ))) return status;
1963 letter = find_dos_device( unix_name );
1964 free( unix_name );
1966 if (letter == -1)
1968 struct stat st;
1970 fstat( fd, &st );
1971 drive->unix_dev = st.st_rdev ? st.st_rdev : st.st_dev;
1972 drive->letter = 0;
1974 else
1975 drive->letter = 'a' + letter;
1977 string.Buffer = (WCHAR *)MOUNTMGR_DEVICE_NAME;
1978 string.Length = sizeof(MOUNTMGR_DEVICE_NAME) - sizeof(WCHAR);
1979 InitializeObjectAttributes( &attr, &string, 0, NULL, NULL );
1980 status = NtOpenFile( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr, &io,
1981 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT );
1982 if (status) return status;
1984 status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE,
1985 drive, sizeof(*drive), drive, size );
1986 NtClose( mountmgr );
1987 if (status == STATUS_BUFFER_OVERFLOW) status = STATUS_SUCCESS;
1988 else if (status) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status);
1989 return status;
1993 /***********************************************************************
1994 * get_dir_data_entry
1996 * Return a directory entry from the cached data.
1998 static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, IO_STATUS_BLOCK *io,
1999 ULONG max_length, FILE_INFORMATION_CLASS class,
2000 union file_directory_info **last_info )
2002 const struct dir_data_names *names = &dir_data->names[dir_data->pos];
2003 union file_directory_info *info;
2004 struct stat st;
2005 ULONG name_len, start, dir_size, attributes;
2007 if (get_file_info( names->unix_name, &st, &attributes ) == -1)
2009 TRACE( "file no longer exists %s\n", names->unix_name );
2010 return STATUS_SUCCESS;
2012 if (is_ignored_file( &st ))
2014 TRACE( "ignoring file %s\n", names->unix_name );
2015 return STATUS_SUCCESS;
2017 start = dir_info_align( io->Information );
2018 dir_size = dir_info_size( class, 0 );
2019 if (start + dir_size > max_length) return STATUS_MORE_ENTRIES;
2021 max_length -= start + dir_size;
2022 name_len = wcslen( names->long_name ) * sizeof(WCHAR);
2023 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2024 if (*last_info && name_len > max_length) return STATUS_MORE_ENTRIES;
2026 info = (union file_directory_info *)((char *)info_ptr + start);
2027 info->dir.NextEntryOffset = 0;
2028 info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
2030 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2031 if (class != FileNamesInformation)
2033 if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */
2035 if (!show_dot_files && names->long_name[0] == '.' && names->long_name[1] &&
2036 (names->long_name[1] != '.' || names->long_name[2]))
2037 attributes |= FILE_ATTRIBUTE_HIDDEN;
2039 fill_file_info( &st, attributes, info, class );
2042 switch (class)
2044 case FileDirectoryInformation:
2045 info->dir.FileNameLength = name_len;
2046 break;
2048 case FileFullDirectoryInformation:
2049 info->full.EaSize = 0; /* FIXME */
2050 info->full.FileNameLength = name_len;
2051 break;
2053 case FileIdFullDirectoryInformation:
2054 info->id_full.EaSize = 0; /* FIXME */
2055 info->id_full.FileNameLength = name_len;
2056 break;
2058 case FileBothDirectoryInformation:
2059 info->both.EaSize = 0; /* FIXME */
2060 info->both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2061 memcpy( info->both.ShortName, names->short_name, info->both.ShortNameLength );
2062 info->both.FileNameLength = name_len;
2063 break;
2065 case FileIdBothDirectoryInformation:
2066 info->id_both.EaSize = 0; /* FIXME */
2067 info->id_both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2068 memcpy( info->id_both.ShortName, names->short_name, info->id_both.ShortNameLength );
2069 info->id_both.FileNameLength = name_len;
2070 break;
2072 case FileIdGlobalTxDirectoryInformation:
2073 info->id_tx.TxInfoFlags = 0;
2074 info->id_tx.FileNameLength = name_len;
2075 break;
2077 case FileNamesInformation:
2078 info->names.FileNameLength = name_len;
2079 break;
2081 default:
2082 assert(0);
2083 return 0;
2086 memcpy( (char *)info + dir_size, names->long_name, min( name_len, max_length ) );
2087 io->Information = start + dir_size + min( name_len, max_length );
2088 if (*last_info) (*last_info)->next = (char *)info - (char *)*last_info;
2089 *last_info = info;
2090 return name_len > max_length ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
2093 #ifdef VFAT_IOCTL_READDIR_BOTH
2095 /***********************************************************************
2096 * read_directory_vfat
2098 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2100 static NTSTATUS read_directory_data_vfat( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2102 char *short_name, *long_name;
2103 KERNEL_DIRENT de[2];
2104 NTSTATUS status = STATUS_NO_MEMORY;
2105 off_t old_pos = lseek( fd, 0, SEEK_CUR );
2107 lseek( fd, 0, SEEK_SET );
2109 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
2111 if (errno != ENOENT)
2113 status = STATUS_NOT_SUPPORTED;
2114 goto done;
2116 de[0].d_reclen = 0;
2119 if (!append_entry( data, ".", NULL, mask )) goto done;
2120 if (!append_entry( data, "..", NULL, mask )) goto done;
2122 while (de[0].d_reclen)
2124 if (strcmp( de[0].d_name, "." ) && strcmp( de[0].d_name, ".." ))
2126 if (de[1].d_name[0])
2128 short_name = de[0].d_name;
2129 long_name = de[1].d_name;
2131 else
2133 long_name = de[0].d_name;
2134 short_name = NULL;
2136 if (!append_entry( data, long_name, short_name, mask )) goto done;
2138 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
2140 status = STATUS_SUCCESS;
2141 done:
2142 lseek( fd, old_pos, SEEK_SET );
2143 return status;
2145 #endif /* VFAT_IOCTL_READDIR_BOTH */
2148 #ifdef HAVE_GETATTRLIST
2149 /***********************************************************************
2150 * read_directory_getattrlist
2152 * Read a single file from a directory by determining whether the file
2153 * identified by mask exists using getattrlist.
2155 static NTSTATUS read_directory_data_getattrlist( struct dir_data *data, const char *unix_name )
2157 struct attrlist attrlist;
2158 #include "pshpack4.h"
2159 struct
2161 u_int32_t length;
2162 struct attrreference name_reference;
2163 fsobj_type_t type;
2164 char name[NAME_MAX * 3 + 1];
2165 } buffer;
2166 #include "poppack.h"
2168 memset( &attrlist, 0, sizeof(attrlist) );
2169 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
2170 attrlist.commonattr = ATTR_CMN_NAME | ATTR_CMN_OBJTYPE;
2171 if (getattrlist( unix_name, &attrlist, &buffer, sizeof(buffer), FSOPT_NOFOLLOW ) == -1)
2172 return STATUS_NO_SUCH_FILE;
2173 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2174 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2175 if (buffer.type == VLNK)
2177 u_int32_t dummy;
2178 attrlist.commonattr = 0;
2179 if (getattrlist( unix_name, &attrlist, &dummy, sizeof(dummy), 0 ) == -1)
2180 return STATUS_NO_SUCH_FILE;
2183 TRACE( "found %s\n", buffer.name );
2185 if (!append_entry( data, buffer.name, NULL, NULL )) return STATUS_NO_MEMORY;
2187 return STATUS_SUCCESS;
2189 #endif /* HAVE_GETATTRLIST */
2192 /***********************************************************************
2193 * read_directory_stat
2195 * Read a single file from a directory by determining whether the file
2196 * identified by mask exists using stat.
2198 static NTSTATUS read_directory_data_stat( struct dir_data *data, const char *unix_name )
2200 struct stat st;
2202 /* if the file system is not case sensitive we can't find the actual name through stat() */
2203 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE;
2204 if (stat( unix_name, &st ) == -1) return STATUS_NO_SUCH_FILE;
2206 TRACE( "found %s\n", unix_name );
2208 if (!append_entry( data, unix_name, NULL, NULL )) return STATUS_NO_MEMORY;
2210 return STATUS_SUCCESS;
2214 /***********************************************************************
2215 * read_directory_readdir
2217 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2219 static NTSTATUS read_directory_data_readdir( struct dir_data *data, const UNICODE_STRING *mask )
2221 struct dirent *de;
2222 NTSTATUS status = STATUS_NO_MEMORY;
2223 DIR *dir = opendir( "." );
2225 if (!dir) return STATUS_NO_SUCH_FILE;
2227 if (!append_entry( data, ".", NULL, mask )) goto done;
2228 if (!append_entry( data, "..", NULL, mask )) goto done;
2229 while ((de = readdir( dir )))
2231 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2232 if (!append_entry( data, de->d_name, NULL, mask )) goto done;
2234 status = STATUS_SUCCESS;
2236 done:
2237 closedir( dir );
2238 return status;
2242 /***********************************************************************
2243 * read_directory_data
2245 * Read the full contents of a directory, using one of the above helper functions.
2247 static NTSTATUS read_directory_data( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2249 NTSTATUS status;
2251 #ifdef VFAT_IOCTL_READDIR_BOTH
2252 if (!(status = read_directory_data_vfat( data, fd, mask ))) return status;
2253 #endif
2255 if (!has_wildcard( mask ))
2257 /* convert the mask to a Unix name and check for it */
2258 char unix_name[MAX_DIR_ENTRY_LEN * 3 + 1];
2259 int ret = ntdll_wcstoumbs( mask->Buffer, mask->Length / sizeof(WCHAR),
2260 unix_name, sizeof(unix_name) - 1, TRUE );
2261 if (ret > 0)
2263 unix_name[ret] = 0;
2264 #ifdef HAVE_GETATTRLIST
2265 if (!(status = read_directory_data_getattrlist( data, unix_name ))) return status;
2266 #endif
2267 if (!(status = read_directory_data_stat( data, unix_name ))) return status;
2271 return read_directory_data_readdir( data, mask );
2275 /* compare file names for directory sorting */
2276 static int name_compare( const void *a, const void *b )
2278 const struct dir_data_names *file_a = (const struct dir_data_names *)a;
2279 const struct dir_data_names *file_b = (const struct dir_data_names *)b;
2280 int ret = wcsicmp( file_a->long_name, file_b->long_name );
2281 if (!ret) ret = wcscmp( file_a->long_name, file_b->long_name );
2282 return ret;
2286 /***********************************************************************
2287 * init_cached_dir_data
2289 * Initialize the cached directory contents.
2291 static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const UNICODE_STRING *mask )
2293 struct dir_data *data;
2294 struct stat st;
2295 NTSTATUS status;
2296 unsigned int i;
2298 if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY;
2300 if ((status = read_directory_data( data, fd, mask )))
2302 free_dir_data( data );
2303 return status;
2306 /* sort filenames, but not "." and ".." */
2307 i = 0;
2308 if (i < data->count && !strcmp( data->names[i].unix_name, "." )) i++;
2309 if (i < data->count && !strcmp( data->names[i].unix_name, ".." )) i++;
2310 if (i < data->count) qsort( data->names + i, data->count - i, sizeof(*data->names), name_compare );
2312 if (data->count)
2314 fstat( fd, &st );
2315 data->id.dev = st.st_dev;
2316 data->id.ino = st.st_ino;
2319 TRACE( "mask %s found %u files\n", debugstr_us( mask ), data->count );
2320 for (i = 0; i < data->count; i++)
2321 TRACE( "%s %s\n", debugstr_w(data->names[i].long_name), debugstr_w(data->names[i].short_name) );
2323 *data_ret = data;
2324 return data->count ? STATUS_SUCCESS : STATUS_NO_SUCH_FILE;
2328 /***********************************************************************
2329 * get_cached_dir_data
2331 * Retrieve the cached directory data, or initialize it if necessary.
2333 static NTSTATUS get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd,
2334 const UNICODE_STRING *mask )
2336 unsigned int i;
2337 int entry = -1, free_entries[16];
2338 NTSTATUS status;
2340 SERVER_START_REQ( get_directory_cache_entry )
2342 req->handle = wine_server_obj_handle( handle );
2343 wine_server_set_reply( req, free_entries, sizeof(free_entries) );
2344 if (!(status = wine_server_call( req ))) entry = reply->entry;
2346 for (i = 0; i < wine_server_reply_size( reply ) / sizeof(*free_entries); i++)
2348 int free_idx = free_entries[i];
2349 if (free_idx < dir_data_cache_size)
2351 free_dir_data( dir_data_cache[free_idx] );
2352 dir_data_cache[free_idx] = NULL;
2356 SERVER_END_REQ;
2358 if (status)
2360 if (status == STATUS_SHARING_VIOLATION) FIXME( "shared directory handle not supported yet\n" );
2361 return status;
2364 if (entry >= dir_data_cache_size)
2366 unsigned int size = max( dir_data_cache_initial_size, max( dir_data_cache_size * 2, entry + 1 ) );
2367 struct dir_data **new_cache = realloc( dir_data_cache, size * sizeof(*new_cache) );
2369 if (!new_cache) return STATUS_NO_MEMORY;
2370 memset( new_cache + dir_data_cache_size, 0, (size - dir_data_cache_size) * sizeof(*new_cache) );
2371 dir_data_cache = new_cache;
2372 dir_data_cache_size = size;
2375 if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask );
2377 *data_ret = dir_data_cache[entry];
2378 return status;
2382 /******************************************************************************
2383 * NtQueryDirectoryFile (NTDLL.@)
2385 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine,
2386 void *apc_context, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
2387 FILE_INFORMATION_CLASS info_class, BOOLEAN single_entry,
2388 UNICODE_STRING *mask, BOOLEAN restart_scan )
2390 int cwd, fd, needs_close;
2391 enum server_fd_type type;
2392 struct dir_data *data;
2393 NTSTATUS status;
2395 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2396 handle, event, apc_routine, apc_context, io, buffer,
2397 length, info_class, single_entry, debugstr_us(mask),
2398 restart_scan);
2400 if (event || apc_routine)
2402 FIXME( "Unsupported yet option\n" );
2403 return STATUS_NOT_IMPLEMENTED;
2405 switch (info_class)
2407 case FileDirectoryInformation:
2408 case FileBothDirectoryInformation:
2409 case FileFullDirectoryInformation:
2410 case FileIdBothDirectoryInformation:
2411 case FileIdFullDirectoryInformation:
2412 case FileIdGlobalTxDirectoryInformation:
2413 case FileNamesInformation:
2414 if (length < dir_info_align( dir_info_size( info_class, 1 ))) return STATUS_INFO_LENGTH_MISMATCH;
2415 break;
2416 case FileObjectIdInformation:
2417 if (length != sizeof(FILE_OBJECTID_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2418 return STATUS_INVALID_INFO_CLASS;
2419 case FileQuotaInformation:
2420 if (length != sizeof(FILE_QUOTA_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2421 return STATUS_INVALID_INFO_CLASS;
2422 case FileReparsePointInformation:
2423 if (length != sizeof(FILE_REPARSE_POINT_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2424 return STATUS_INVALID_INFO_CLASS;
2425 default:
2426 return STATUS_INVALID_INFO_CLASS;
2428 if (!buffer) return STATUS_ACCESS_VIOLATION;
2430 if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, &type, NULL )))
2431 return status;
2433 if (type != FD_TYPE_DIR)
2435 if (needs_close) close( fd );
2436 return STATUS_INVALID_PARAMETER;
2439 io->Information = 0;
2441 mutex_lock( &dir_mutex );
2443 cwd = open( ".", O_RDONLY );
2444 if (fchdir( fd ) != -1)
2446 if (!(status = get_cached_dir_data( handle, &data, fd, mask )))
2448 union file_directory_info *last_info = NULL;
2450 if (restart_scan) data->pos = 0;
2452 while (!status && data->pos < data->count)
2454 status = get_dir_data_entry( data, buffer, io, length, info_class, &last_info );
2455 if (!status || status == STATUS_BUFFER_OVERFLOW) data->pos++;
2456 if (single_entry && last_info) break;
2459 if (!last_info) status = STATUS_NO_MORE_FILES;
2460 else if (status == STATUS_MORE_ENTRIES) status = STATUS_SUCCESS;
2462 io->u.Status = status;
2464 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
2466 else status = errno_to_status( errno );
2468 mutex_unlock( &dir_mutex );
2470 if (needs_close) close( fd );
2471 if (cwd != -1) close( cwd );
2472 TRACE( "=> %x (%ld)\n", status, io->Information );
2473 return status;
2477 /***********************************************************************
2478 * find_file_in_dir
2480 * Find a file in a directory the hard way, by doing a case-insensitive search.
2481 * The file found is appended to unix_name at pos.
2482 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2484 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
2485 BOOLEAN check_case, BOOLEAN *is_win_dir )
2487 WCHAR buffer[MAX_DIR_ENTRY_LEN];
2488 BOOLEAN is_name_8_dot_3;
2489 DIR *dir;
2490 struct dirent *de;
2491 struct stat st;
2492 int ret;
2494 /* try a shortcut for this directory */
2496 unix_name[pos++] = '/';
2497 ret = ntdll_wcstoumbs( name, length, unix_name + pos, MAX_DIR_ENTRY_LEN + 1, TRUE );
2498 if (ret >= 0 && ret <= MAX_DIR_ENTRY_LEN)
2500 unix_name[pos + ret] = 0;
2501 if (!stat( unix_name, &st ))
2503 if (is_win_dir) *is_win_dir = is_same_file( &windir, &st );
2504 return STATUS_SUCCESS;
2507 if (check_case) goto not_found; /* we want an exact match */
2509 if (pos > 1) unix_name[pos - 1] = 0;
2510 else unix_name[1] = 0; /* keep the initial slash */
2512 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2514 is_name_8_dot_3 = is_legal_8dot3_name( name, length );
2515 #ifndef VFAT_IOCTL_READDIR_BOTH
2516 is_name_8_dot_3 = is_name_8_dot_3 && length >= 8 && name[4] == '~';
2517 #endif
2519 if (!is_name_8_dot_3 && !get_dir_case_sensitivity( unix_name )) goto not_found;
2521 /* now look for it through the directory */
2523 #ifdef VFAT_IOCTL_READDIR_BOTH
2524 if (is_name_8_dot_3)
2526 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
2527 if (fd != -1)
2529 KERNEL_DIRENT kde[2];
2531 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) != -1)
2533 unix_name[pos - 1] = '/';
2534 while (kde[0].d_reclen)
2536 if (kde[1].d_name[0])
2538 ret = ntdll_umbstowcs( kde[1].d_name, strlen(kde[1].d_name),
2539 buffer, MAX_DIR_ENTRY_LEN );
2540 if (ret == length && !wcsnicmp( buffer, name, ret ))
2542 strcpy( unix_name + pos, kde[1].d_name );
2543 close( fd );
2544 goto success;
2547 ret = ntdll_umbstowcs( kde[0].d_name, strlen(kde[0].d_name),
2548 buffer, MAX_DIR_ENTRY_LEN );
2549 if (ret == length && !wcsnicmp( buffer, name, ret ))
2551 strcpy( unix_name + pos,
2552 kde[1].d_name[0] ? kde[1].d_name : kde[0].d_name );
2553 close( fd );
2554 goto success;
2556 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) == -1)
2558 close( fd );
2559 goto not_found;
2562 /* if that did not work, restore previous state of unix_name */
2563 unix_name[pos - 1] = 0;
2565 close( fd );
2567 /* fall through to normal handling */
2569 #endif /* VFAT_IOCTL_READDIR_BOTH */
2571 if (!(dir = opendir( unix_name ))) return errno_to_status( errno );
2573 unix_name[pos - 1] = '/';
2574 while ((de = readdir( dir )))
2576 ret = ntdll_umbstowcs( de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
2577 if (ret == length && !wcsnicmp( buffer, name, ret ))
2579 strcpy( unix_name + pos, de->d_name );
2580 closedir( dir );
2581 goto success;
2584 if (!is_name_8_dot_3) continue;
2586 if (!is_legal_8dot3_name( buffer, ret ))
2588 WCHAR short_nameW[12];
2589 ret = hash_short_file_name( buffer, ret, short_nameW );
2590 if (ret == length && !wcsnicmp( short_nameW, name, length ))
2592 strcpy( unix_name + pos, de->d_name );
2593 closedir( dir );
2594 goto success;
2598 closedir( dir );
2600 not_found:
2601 unix_name[pos - 1] = 0;
2602 return STATUS_OBJECT_PATH_NOT_FOUND;
2604 success:
2605 if (is_win_dir && !stat( unix_name, &st )) *is_win_dir = is_same_file( &windir, &st );
2606 return STATUS_SUCCESS;
2610 #ifndef _WIN64
2612 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2613 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2614 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};
2615 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2616 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2617 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2618 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
2619 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
2620 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2622 static struct
2624 const WCHAR *source;
2625 const char *unix_target;
2626 } redirects[] =
2628 { catrootW, NULL },
2629 { catroot2W, NULL },
2630 { driversstoreW, NULL },
2631 { driversetcW, NULL },
2632 { logfilesW, NULL },
2633 { spoolW, NULL },
2634 { system32W, "syswow64" },
2635 { sysnativeW, "system32" },
2636 { regeditW, "syswow64/regedit.exe" }
2639 static unsigned int nb_redirects;
2642 /***********************************************************************
2643 * init_redirects
2645 static void init_redirects(void)
2647 static const char windows_dir[] = "/dosdevices/c:/windows";
2648 char *dir;
2649 struct stat st;
2651 if (!(dir = malloc( strlen(config_dir) + sizeof(windows_dir) ))) return;
2652 strcpy( dir, config_dir );
2653 strcat( dir, windows_dir );
2654 if (!stat( dir, &st ))
2656 windir.dev = st.st_dev;
2657 windir.ino = st.st_ino;
2658 nb_redirects = ARRAY_SIZE( redirects );
2660 else ERR( "%s: %s\n", dir, strerror(errno) );
2661 free( dir );
2666 /***********************************************************************
2667 * match_redirect
2669 * Check if path matches a redirect name. If yes, return matched length.
2671 static int match_redirect( const WCHAR *path, int len, const WCHAR *redir, BOOLEAN check_case )
2673 int i = 0;
2675 while (i < len)
2677 int start = i;
2678 while (i < len && !IS_SEPARATOR(path[i])) i++;
2679 if (check_case)
2681 if (wcsncmp( path + start, redir, i - start )) return 0;
2683 else
2685 if (wcsnicmp( path + start, redir, i - start )) return 0;
2687 redir += i - start;
2688 while (i < len && IS_SEPARATOR(path[i])) i++;
2689 if (!*redir) return i;
2690 if (*redir++ != '\\') return 0;
2692 return 0;
2696 /***********************************************************************
2697 * get_redirect_path
2699 * Retrieve the Unix path corresponding to a redirected path if any.
2701 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, BOOLEAN check_case )
2703 unsigned int i;
2704 int len;
2706 for (i = 0; i < nb_redirects; i++)
2708 if ((len = match_redirect( name, length, redirects[i].source, check_case )))
2710 if (!redirects[i].unix_target) break;
2711 unix_name[pos++] = '/';
2712 strcpy( unix_name + pos, redirects[i].unix_target );
2713 return len;
2716 return 0;
2719 #else /* _WIN64 */
2721 /* there are no redirects on 64-bit */
2722 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, BOOLEAN check_case )
2724 return 0;
2727 #endif
2729 /***********************************************************************
2730 * init_files
2732 void init_files(void)
2734 #ifndef _WIN64
2735 if (is_wow64) init_redirects();
2736 #endif
2737 /* a couple of directories that we don't want to return in directory searches */
2738 ignore_file( config_dir );
2739 ignore_file( "/dev" );
2740 ignore_file( "/proc" );
2741 #ifdef linux
2742 ignore_file( "/sys" );
2743 #endif
2744 /* retrieve initial umask */
2745 start_umask = umask( 0777 );
2746 umask( start_umask );
2750 /******************************************************************************
2751 * get_dos_device
2753 * Get the Unix path of a DOS device.
2755 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, char **unix_name_ret )
2757 struct stat st;
2758 char *unix_name, *new_name, *dev;
2759 unsigned int i;
2760 int unix_len;
2762 /* make sure the device name is ASCII */
2763 for (i = 0; i < name_len; i++)
2764 if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
2766 unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
2768 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
2770 strcpy( unix_name, config_dir );
2771 strcat( unix_name, "/dosdevices/" );
2772 dev = unix_name + strlen(unix_name);
2774 for (i = 0; i < name_len; i++) dev[i] = (name[i] >= 'A' && name[i] <= 'Z' ? name[i] + 32 : name[i]);
2775 dev[i] = 0;
2777 /* special case for drive devices */
2778 if (name_len == 2 && dev[1] == ':')
2780 dev[i++] = ':';
2781 dev[i] = 0;
2784 for (;;)
2786 if (!stat( unix_name, &st ))
2788 TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
2789 *unix_name_ret = unix_name;
2790 return STATUS_SUCCESS;
2792 if (!dev) break;
2794 /* now try some defaults for it */
2795 if (!strcmp( dev, "aux" ))
2797 strcpy( dev, "com1" );
2798 continue;
2800 if (!strcmp( dev, "prn" ))
2802 strcpy( dev, "lpt1" );
2803 continue;
2806 new_name = NULL;
2807 if (dev[1] == ':' && dev[2] == ':') /* drive device */
2809 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
2810 new_name = get_default_drive_device( unix_name );
2813 if (!new_name) break;
2814 free( unix_name );
2815 unix_name = new_name;
2816 dev = NULL; /* last try */
2818 free( unix_name );
2819 return STATUS_BAD_DEVICE_TYPE;
2823 /* return the length of the DOS namespace prefix if any */
2824 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
2826 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
2827 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2829 if (name->Length >= sizeof(nt_prefixW) &&
2830 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
2831 return ARRAY_SIZE( nt_prefixW );
2833 if (name->Length >= sizeof(dosdev_prefixW) &&
2834 !wcsnicmp( name->Buffer, dosdev_prefixW, ARRAY_SIZE( dosdev_prefixW )))
2835 return ARRAY_SIZE( dosdev_prefixW );
2837 return 0;
2841 /***********************************************************************
2842 * remove_last_componentA
2844 * Remove the last component of the path. Helper for find_drive_rootA.
2846 static inline unsigned int remove_last_componentA( const char *path, unsigned int len )
2848 int level = 0;
2850 while (level < 1)
2852 /* find start of the last path component */
2853 unsigned int prev = len;
2854 if (prev <= 1) break; /* reached root */
2855 while (prev > 1 && path[prev - 1] != '/') prev--;
2856 /* does removing it take us up a level? */
2857 if (len - prev != 1 || path[prev] != '.') /* not '.' */
2859 if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.') /* is it '..'? */
2860 level--;
2861 else
2862 level++;
2864 /* strip off trailing slashes */
2865 while (prev > 1 && path[prev - 1] == '/') prev--;
2866 len = prev;
2868 return len;
2872 /***********************************************************************
2873 * find_drive_rootA
2875 * Find a drive for which the root matches the beginning of the given path.
2876 * This can be used to translate a Unix path into a drive + DOS path.
2877 * Return value is the drive, or -1 on error. On success, ppath is modified
2878 * to point to the beginning of the DOS path.
2880 static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_ret )
2882 /* Starting with the full path, check if the device and inode match any of
2883 * the wine 'drives'. If not then remove the last path component and try
2884 * again. If the last component was a '..' then skip a normal component
2885 * since it's a directory that's ascended back out of.
2887 int drive;
2888 char *buffer;
2889 const char *path = *ppath;
2890 struct stat st;
2891 struct file_identity info[MAX_DOS_DRIVES];
2893 /* get device and inode of all drives */
2894 if (!get_drives_info( info )) return STATUS_OBJECT_PATH_NOT_FOUND;
2896 /* strip off trailing slashes */
2897 while (len > 1 && path[len - 1] == '/') len--;
2899 /* make a copy of the path */
2900 if (!(buffer = malloc( len + 1 ))) return STATUS_NO_MEMORY;
2901 memcpy( buffer, path, len );
2902 buffer[len] = 0;
2904 for (;;)
2906 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
2908 /* Find the drive */
2909 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
2911 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
2913 if (len == 1) len = 0; /* preserve root slash in returned path */
2914 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2915 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
2916 *ppath += len;
2917 *drive_ret = drive;
2918 free( buffer );
2919 return STATUS_SUCCESS;
2923 if (len <= 1) break; /* reached root */
2924 len = remove_last_componentA( buffer, len );
2925 buffer[len] = 0;
2927 free( buffer );
2928 return STATUS_OBJECT_PATH_NOT_FOUND;
2932 /******************************************************************************
2933 * rebuild_nt_name
2935 static void rebuild_nt_name( const UNICODE_STRING *nameW, DWORD prefix_len,
2936 const char *unix_name, UNICODE_STRING *nt_name )
2938 WCHAR *buf;
2939 DWORD len;
2941 while (*unix_name == '/') unix_name++;
2942 nt_name->MaximumLength = (prefix_len + strlen(unix_name) + 2) * sizeof(WCHAR);
2943 if (!(buf = malloc( nt_name->MaximumLength ))) return;
2944 nt_name->Buffer = buf;
2945 memcpy( buf, nameW->Buffer, prefix_len * sizeof(WCHAR) );
2946 if (prefix_len && buf[prefix_len - 1] != '\\') buf[prefix_len++] = '\\';
2947 buf += prefix_len;
2948 len = ntdll_umbstowcs( unix_name, strlen(unix_name), buf, strlen(unix_name) );
2949 for (; len; len--, buf++) if (*buf == '/') *buf = '\\';
2950 *buf = 0;
2951 nt_name->Length = (buf - nt_name->Buffer) * sizeof(WCHAR);
2955 /******************************************************************************
2956 * find_file_id
2958 * Recursively search directories from the dir queue for a given inode.
2960 static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev )
2962 unsigned int pos;
2963 DIR *dir;
2964 struct dirent *de;
2965 NTSTATUS status;
2966 struct stat st;
2967 char *name = *unix_name;
2969 while (!(status = next_dir_in_queue( name )))
2971 if (!(dir = opendir( name ))) continue;
2972 TRACE( "searching %s for %s\n", debugstr_a(name), wine_dbgstr_longlong(file_id) );
2973 pos = strlen( name );
2974 if (pos + MAX_DIR_ENTRY_LEN >= *len / sizeof(WCHAR))
2976 if (!(name = realloc( name, *len * 2 )))
2978 closedir( dir );
2979 return STATUS_NO_MEMORY;
2981 *len *= 2;
2982 *unix_name = name;
2984 name[pos++] = '/';
2985 while ((de = readdir( dir )))
2987 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2988 strcpy( name + pos, de->d_name );
2989 if (lstat( name, &st ) == -1) continue;
2990 if (st.st_dev != dev) continue;
2991 if (st.st_ino == file_id)
2993 closedir( dir );
2994 return STATUS_SUCCESS;
2996 if (!S_ISDIR( st.st_mode )) continue;
2997 if ((status = add_dir_to_queue( name )) != STATUS_SUCCESS)
2999 closedir( dir );
3000 return status;
3003 closedir( dir );
3005 return status;
3009 /******************************************************************************
3010 * file_id_to_unix_file_name
3012 * Lookup a file from its file id instead of its name.
3014 static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **unix_name_ret,
3015 UNICODE_STRING *nt_name )
3017 enum server_fd_type type;
3018 int old_cwd, root_fd, needs_close;
3019 char *unix_name;
3020 ULONG len;
3021 NTSTATUS status;
3022 ULONGLONG file_id;
3023 struct stat st, root_st;
3025 if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
3026 if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
3027 memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
3029 len = 2 * MAX_DIR_ENTRY_LEN + 4;
3030 if (!(unix_name = malloc( len ))) return STATUS_NO_MEMORY;
3031 strcpy( unix_name, "." );
3033 if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3034 goto done;
3036 if (type != FD_TYPE_DIR)
3038 status = STATUS_OBJECT_TYPE_MISMATCH;
3039 goto done;
3042 fstat( root_fd, &root_st );
3043 if (root_st.st_ino == file_id) /* shortcut for "." */
3045 status = STATUS_SUCCESS;
3046 goto done;
3049 mutex_lock( &dir_mutex );
3050 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3052 /* shortcut for ".." */
3053 if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
3055 strcpy( unix_name, ".." );
3056 status = STATUS_SUCCESS;
3058 else
3060 status = add_dir_to_queue( "." );
3061 if (!status)
3062 status = find_file_id( &unix_name, &len, file_id, root_st.st_dev );
3063 if (!status) /* get rid of "./" prefix */
3064 memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 );
3065 flush_dir_queue();
3067 if (fchdir( old_cwd ) == -1) chdir( "/" );
3069 else status = errno_to_status( errno );
3070 mutex_unlock( &dir_mutex );
3071 if (old_cwd != -1) close( old_cwd );
3073 done:
3074 if (status == STATUS_SUCCESS)
3076 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name) );
3077 *unix_name_ret = unix_name;
3078 if (nt_name) rebuild_nt_name( attr->ObjectName, 0, unix_name, nt_name );
3080 else
3082 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
3083 free( unix_name );
3085 if (needs_close) close( root_fd );
3086 return status;
3090 /******************************************************************************
3091 * lookup_unix_name
3093 * Helper for nt_to_unix_file_name
3095 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
3096 UINT disposition, BOOLEAN check_case )
3098 NTSTATUS status;
3099 int ret, len;
3100 struct stat st;
3101 char *unix_name = *buffer;
3102 #ifdef _WIN64
3103 const BOOL redirect = FALSE;
3104 #else
3105 const BOOL redirect = NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR];
3106 #endif
3108 /* try a shortcut first */
3110 while (name_len && IS_SEPARATOR(*name))
3112 name++;
3113 name_len--;
3116 unix_name[pos] = '/';
3117 ret = ntdll_wcstoumbs( name, name_len, unix_name + pos + 1, unix_len - pos - 1, TRUE );
3118 if (ret >= 0 && ret < unix_len - pos - 1)
3120 char *p;
3121 unix_name[pos + 1 + ret] = 0;
3122 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
3123 if (!name_len || !redirect || (!strstr( unix_name, "/windows/") && strncmp( unix_name, "windows/", 8 )))
3125 if (!stat( unix_name, &st ))
3127 if (disposition == FILE_CREATE)
3128 return STATUS_OBJECT_NAME_COLLISION;
3129 return STATUS_SUCCESS;
3134 if (!name_len) /* empty name -> drive root doesn't exist */
3135 return STATUS_OBJECT_PATH_NOT_FOUND;
3136 if (check_case && !redirect && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
3137 return STATUS_OBJECT_NAME_NOT_FOUND;
3139 /* now do it component by component */
3141 while (name_len)
3143 const WCHAR *end, *next;
3144 BOOLEAN is_win_dir = FALSE;
3146 end = name;
3147 while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
3148 next = end;
3149 while (next < name + name_len && IS_SEPARATOR(*next)) next++;
3150 name_len -= next - name;
3152 /* grow the buffer if needed */
3154 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
3156 char *new_name;
3157 unix_len += 2 * MAX_DIR_ENTRY_LEN;
3158 if (!(new_name = realloc( unix_name, unix_len ))) return STATUS_NO_MEMORY;
3159 unix_name = *buffer = new_name;
3162 status = find_file_in_dir( unix_name, pos, name, end - name,
3163 check_case, redirect ? &is_win_dir : NULL );
3165 /* if this is the last element, not finding it is not necessarily fatal */
3166 if (!name_len)
3168 if (status == STATUS_OBJECT_PATH_NOT_FOUND)
3170 status = STATUS_OBJECT_NAME_NOT_FOUND;
3171 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3173 ret = ntdll_wcstoumbs( name, end - name, unix_name + pos + 1, MAX_DIR_ENTRY_LEN + 1, TRUE );
3174 if (ret > 0 && ret <= MAX_DIR_ENTRY_LEN)
3176 unix_name[pos] = '/';
3177 unix_name[pos + 1 + ret] = 0;
3178 status = STATUS_NO_SUCH_FILE;
3179 break;
3183 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
3185 status = STATUS_OBJECT_NAME_COLLISION;
3189 if (status != STATUS_SUCCESS) break;
3191 pos += strlen( unix_name + pos );
3192 name = next;
3194 if (is_win_dir && (len = get_redirect_path( unix_name, pos, name, name_len, check_case )))
3196 name += len;
3197 name_len -= len;
3198 pos += strlen( unix_name + pos );
3199 TRACE( "redirecting -> %s + %s\n", debugstr_a(unix_name), debugstr_w(name) );
3203 return status;
3207 /******************************************************************************
3208 * nt_to_unix_file_name_attr
3210 static NTSTATUS nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, char **name_ret,
3211 UNICODE_STRING *nt_name, UINT disposition )
3213 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
3214 enum server_fd_type type;
3215 int old_cwd, root_fd, needs_close;
3216 const WCHAR *name, *p;
3217 char *unix_name;
3218 int name_len, unix_len;
3219 NTSTATUS status;
3221 if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
3222 return nt_to_unix_file_name( attr->ObjectName, name_ret, nt_name, disposition );
3224 name = attr->ObjectName->Buffer;
3225 name_len = attr->ObjectName->Length / sizeof(WCHAR);
3227 if (name_len && IS_SEPARATOR(name[0])) return STATUS_INVALID_PARAMETER;
3229 /* check for invalid characters */
3230 for (p = name; p < name + name_len; p++)
3231 if (*p < 32 || wcschr( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
3233 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3234 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3235 unix_name[0] = '.';
3237 if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3239 if (type != FD_TYPE_DIR)
3241 if (needs_close) close( root_fd );
3242 status = STATUS_BAD_DEVICE_TYPE;
3244 else
3246 mutex_lock( &dir_mutex );
3247 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3249 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1,
3250 disposition, FALSE );
3251 if (fchdir( old_cwd ) == -1) chdir( "/" );
3253 else status = errno_to_status( errno );
3254 mutex_unlock( &dir_mutex );
3255 if (old_cwd != -1) close( old_cwd );
3256 if (needs_close) close( root_fd );
3259 else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
3261 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3263 TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
3264 *name_ret = unix_name;
3265 if (nt_name) rebuild_nt_name( attr->ObjectName, 0, unix_name, nt_name );
3267 else
3269 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3270 free( unix_name );
3272 return status;
3276 /******************************************************************************
3277 * nt_to_unix_file_name
3279 * Convert a file name from NT namespace to Unix namespace.
3281 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3282 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3283 * returned, but the unix name is still filled in properly.
3285 NTSTATUS nt_to_unix_file_name( const UNICODE_STRING *nameW, char **unix_name_ret,
3286 UNICODE_STRING *nt_name, UINT disposition )
3288 static const WCHAR unixW[] = {'u','n','i','x'};
3289 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
3291 NTSTATUS status = STATUS_SUCCESS;
3292 const WCHAR *name, *p;
3293 struct stat st;
3294 char *unix_name;
3295 int pos, ret, name_len, unix_len, prefix_len;
3296 WCHAR prefix[MAX_DIR_ENTRY_LEN + 1];
3297 BOOLEAN check_case = FALSE;
3298 BOOLEAN is_unix = FALSE;
3300 name = nameW->Buffer;
3301 name_len = nameW->Length / sizeof(WCHAR);
3303 if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
3305 if (!(pos = get_dos_prefix_len( nameW )))
3306 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
3308 name += pos;
3309 name_len -= pos;
3311 if (!name_len) return STATUS_OBJECT_NAME_INVALID;
3313 /* check for sub-directory */
3314 for (pos = 0; pos < name_len && pos <= MAX_DIR_ENTRY_LEN; pos++)
3316 if (IS_SEPARATOR(name[pos])) break;
3317 if (name[pos] < 32 || wcschr( invalid_charsW, name[pos] ))
3318 return STATUS_OBJECT_NAME_INVALID;
3319 prefix[pos] = (name[pos] >= 'A' && name[pos] <= 'Z') ? name[pos] + 'a' - 'A' : name[pos];
3321 if (pos > MAX_DIR_ENTRY_LEN) return STATUS_OBJECT_NAME_INVALID;
3323 if (pos == name_len) /* no subdir, plain DOS device */
3324 return get_dos_device( name, name_len, unix_name_ret );
3326 prefix_len = pos;
3327 prefix[prefix_len] = 0;
3329 name += prefix_len;
3330 name_len -= prefix_len;
3332 /* check for invalid characters (all chars except 0 are valid for unix) */
3333 is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
3334 if (is_unix)
3336 for (p = name; p < name + name_len; p++)
3337 if (!*p) return STATUS_OBJECT_NAME_INVALID;
3338 check_case = TRUE;
3340 else
3342 for (p = name; p < name + name_len; p++)
3343 if (*p < 32 || wcschr( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
3346 unix_len = (prefix_len + name_len) * 3 + MAX_DIR_ENTRY_LEN + 3;
3347 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
3348 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3349 strcpy( unix_name, config_dir );
3350 strcat( unix_name, "/dosdevices/" );
3351 pos = strlen(unix_name);
3353 ret = ntdll_wcstoumbs( prefix, prefix_len, unix_name + pos, unix_len - pos - 1, TRUE );
3354 if (ret <= 0)
3356 free( unix_name );
3357 return STATUS_OBJECT_NAME_INVALID;
3359 pos += ret;
3361 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3363 if (prefix_len != 2 || prefix[1] != ':')
3365 unix_name[pos] = 0;
3366 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
3368 if (!is_unix)
3370 free( unix_name );
3371 return STATUS_BAD_DEVICE_TYPE;
3373 pos = 0; /* fall back to unix root */
3377 status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, check_case );
3378 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3380 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
3381 *unix_name_ret = unix_name;
3382 if (nt_name) rebuild_nt_name( nameW, name - nameW->Buffer, unix_name + pos, nt_name );
3384 else
3386 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3387 free( unix_name );
3389 return status;
3393 /******************************************************************************
3394 * wine_nt_to_unix_file_name
3396 * Convert a file name from NT namespace to Unix namespace.
3398 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3399 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3400 * returned, but the unix name is still filled in properly.
3402 NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, char *nameA, SIZE_T *size,
3403 UINT disposition )
3405 char *buffer = NULL;
3406 NTSTATUS status = nt_to_unix_file_name( nameW, &buffer, NULL, disposition );
3408 if (buffer)
3410 if (*size > strlen(buffer)) strcpy( nameA, buffer );
3411 else status = STATUS_BUFFER_TOO_SMALL;
3412 *size = strlen(buffer) + 1;
3413 free( buffer );
3415 return status;
3419 /******************************************************************
3420 * unix_to_nt_file_name
3422 NTSTATUS unix_to_nt_file_name( const char *name, WCHAR **nt )
3424 static const WCHAR unix_prefixW[] = {'\\','?','?','\\','u','n','i','x',0};
3425 WCHAR dos_prefixW[] = {'\\','?','?','\\','A',':','\\',0};
3426 const WCHAR *prefix = unix_prefixW;
3427 unsigned int lenW, lenA = strlen(name);
3428 const char *path = name;
3429 NTSTATUS status;
3430 WCHAR *p, *buffer;
3431 int drive;
3433 status = find_drive_rootA( &path, lenA, &drive );
3434 lenA -= path - name;
3436 if (status == STATUS_SUCCESS)
3438 while (lenA && path[0] == '/') { lenA--; path++; }
3439 dos_prefixW[4] += drive;
3440 prefix = dos_prefixW;
3442 else if (status != STATUS_OBJECT_PATH_NOT_FOUND) return status;
3444 lenW = wcslen( prefix );
3445 if (!(buffer = malloc( (lenA + lenW + 1) * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
3446 memcpy( buffer, prefix, lenW * sizeof(WCHAR) );
3447 lenW += ntdll_umbstowcs( path, lenA, buffer + lenW, lenA );
3448 buffer[lenW] = 0;
3449 for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
3450 *nt = buffer;
3451 return STATUS_SUCCESS;
3455 /******************************************************************
3456 * wine_unix_to_nt_file_name
3458 NTSTATUS CDECL wine_unix_to_nt_file_name( const char *name, WCHAR *buffer, SIZE_T *size )
3460 WCHAR *nt_name = NULL;
3461 NTSTATUS status;
3463 if (name[0] != '/') return STATUS_INVALID_PARAMETER; /* relative paths are not supported */
3465 status = unix_to_nt_file_name( name, &nt_name );
3466 if (nt_name)
3468 if (*size > wcslen(nt_name)) wcscpy( buffer, nt_name );
3469 else status = STATUS_BUFFER_TOO_SMALL;
3470 *size = wcslen(nt_name) + 1;
3471 free( nt_name );
3473 return status;
3477 /***********************************************************************
3478 * unmount_device
3480 * Unmount the specified device.
3482 static NTSTATUS unmount_device( HANDLE handle )
3484 NTSTATUS status;
3485 int unix_fd, needs_close;
3487 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
3489 struct stat st;
3490 char *mount_point = NULL;
3492 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
3493 status = STATUS_INVALID_PARAMETER;
3494 else
3496 if ((mount_point = get_device_mount_point( st.st_rdev )))
3498 #ifdef __APPLE__
3499 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
3500 #else
3501 static const char umount[] = "umount >/dev/null 2>&1 ";
3502 #endif
3503 char *cmd = malloc( strlen(mount_point)+sizeof(umount));
3504 if (cmd)
3506 strcpy( cmd, umount );
3507 strcat( cmd, mount_point );
3508 system( cmd );
3509 free( cmd );
3510 #ifdef linux
3511 /* umount will fail to release the loop device since we still have
3512 a handle to it, so we release it here */
3513 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3514 #endif
3516 free( mount_point );
3519 if (needs_close) close( unix_fd );
3521 return status;
3525 /***********************************************************************
3526 * set_show_dot_files
3528 void CDECL set_show_dot_files( BOOL enable )
3530 show_dot_files = enable;
3534 /******************************************************************************
3535 * open_unix_file
3537 * Helper for NtCreateFile that takes a Unix path.
3539 NTSTATUS open_unix_file( HANDLE *handle, const char *unix_name, ACCESS_MASK access,
3540 OBJECT_ATTRIBUTES *attr, ULONG attributes, ULONG sharing, ULONG disposition,
3541 ULONG options, void *ea_buffer, ULONG ea_length )
3543 struct object_attributes *objattr;
3544 NTSTATUS status;
3545 data_size_t len;
3547 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3549 SERVER_START_REQ( create_file )
3551 req->access = access;
3552 req->sharing = sharing;
3553 req->create = disposition;
3554 req->options = options;
3555 req->attrs = attributes;
3556 wine_server_add_data( req, objattr, len );
3557 wine_server_add_data( req, unix_name, strlen(unix_name) );
3558 status = wine_server_call( req );
3559 *handle = wine_server_ptr_handle( reply->handle );
3561 SERVER_END_REQ;
3562 free( objattr );
3563 return status;
3567 /******************************************************************************
3568 * NtCreateFile (NTDLL.@)
3570 NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
3571 IO_STATUS_BLOCK *io, LARGE_INTEGER *alloc_size,
3572 ULONG attributes, ULONG sharing, ULONG disposition,
3573 ULONG options, void *ea_buffer, ULONG ea_length )
3575 UNICODE_STRING nt_name = { 0 };
3576 char *unix_name;
3577 BOOL created = FALSE;
3579 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
3580 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
3581 handle, access, debugstr_us(attr->ObjectName), attr->Attributes,
3582 attr->RootDirectory, attr->SecurityDescriptor, io, alloc_size,
3583 attributes, sharing, disposition, options, ea_buffer, ea_length );
3585 if (!attr || !attr->ObjectName) return STATUS_INVALID_PARAMETER;
3587 if (alloc_size) FIXME( "alloc_size not supported\n" );
3589 if (options & FILE_OPEN_BY_FILE_ID)
3590 io->u.Status = file_id_to_unix_file_name( attr, &unix_name, &nt_name );
3591 else
3592 io->u.Status = nt_to_unix_file_name_attr( attr, &unix_name, &nt_name, disposition );
3594 if (io->u.Status == STATUS_BAD_DEVICE_TYPE)
3596 SERVER_START_REQ( open_file_object )
3598 req->access = access;
3599 req->attributes = attr->Attributes;
3600 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
3601 req->sharing = sharing;
3602 req->options = options;
3603 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
3604 io->u.Status = wine_server_call( req );
3605 *handle = wine_server_ptr_handle( reply->handle );
3607 SERVER_END_REQ;
3608 if (io->u.Status == STATUS_SUCCESS) io->Information = FILE_OPENED;
3609 return io->u.Status;
3612 if (io->u.Status == STATUS_NO_SUCH_FILE && disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3614 created = TRUE;
3615 io->u.Status = STATUS_SUCCESS;
3618 if (io->u.Status == STATUS_SUCCESS)
3620 OBJECT_ATTRIBUTES nt_attr = *attr;
3622 if (nt_name.Buffer) nt_attr.ObjectName = &nt_name;
3623 io->u.Status = open_unix_file( handle, unix_name, access, &nt_attr, attributes,
3624 sharing, disposition, options, ea_buffer, ea_length );
3625 free( nt_name.Buffer );
3626 free( unix_name );
3628 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), io->u.Status );
3630 if (io->u.Status == STATUS_SUCCESS)
3632 if (created) io->Information = FILE_CREATED;
3633 else switch(disposition)
3635 case FILE_SUPERSEDE:
3636 io->Information = FILE_SUPERSEDED;
3637 break;
3638 case FILE_CREATE:
3639 io->Information = FILE_CREATED;
3640 break;
3641 case FILE_OPEN:
3642 case FILE_OPEN_IF:
3643 io->Information = FILE_OPENED;
3644 break;
3645 case FILE_OVERWRITE:
3646 case FILE_OVERWRITE_IF:
3647 io->Information = FILE_OVERWRITTEN;
3648 break;
3651 else if (io->u.Status == STATUS_TOO_MANY_OPENED_FILES)
3653 static int once;
3654 if (!once++) ERR_(winediag)( "Too many open files, ulimit -n probably needs to be increased\n" );
3657 return io->u.Status;
3661 /******************************************************************************
3662 * NtOpenFile (NTDLL.@)
3664 NTSTATUS WINAPI NtOpenFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
3665 IO_STATUS_BLOCK *io, ULONG sharing, ULONG options )
3667 return NtCreateFile( handle, access, attr, io, NULL, 0, sharing, FILE_OPEN, options, NULL, 0 );
3671 /******************************************************************************
3672 * NtCreateMailslotFile (NTDLL.@)
3674 NTSTATUS WINAPI NtCreateMailslotFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
3675 IO_STATUS_BLOCK *io, ULONG options, ULONG quota, ULONG msg_size,
3676 LARGE_INTEGER *timeout )
3678 NTSTATUS status;
3679 data_size_t len;
3680 struct object_attributes *objattr;
3682 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
3683 handle, access, attr, io, options, quota, msg_size, timeout );
3685 if (!handle) return STATUS_ACCESS_VIOLATION;
3686 if (!attr) return STATUS_INVALID_PARAMETER;
3688 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3690 SERVER_START_REQ( create_mailslot )
3692 req->access = access;
3693 req->max_msgsize = msg_size;
3694 req->read_timeout = timeout ? timeout->QuadPart : -1;
3695 wine_server_add_data( req, objattr, len );
3696 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
3698 SERVER_END_REQ;
3700 free( objattr );
3701 return status;
3705 /******************************************************************
3706 * NtCreateNamedPipeFile (NTDLL.@)
3708 NTSTATUS WINAPI NtCreateNamedPipeFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
3709 IO_STATUS_BLOCK *io, ULONG sharing, ULONG dispo, ULONG options,
3710 ULONG pipe_type, ULONG read_mode, ULONG completion_mode,
3711 ULONG max_inst, ULONG inbound_quota, ULONG outbound_quota,
3712 LARGE_INTEGER *timeout )
3714 NTSTATUS status;
3715 data_size_t len;
3716 struct object_attributes *objattr;
3718 if (!attr) return STATUS_INVALID_PARAMETER;
3720 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
3721 handle, access, debugstr_us(attr->ObjectName), io, sharing, dispo,
3722 options, pipe_type, read_mode, completion_mode, max_inst, inbound_quota,
3723 outbound_quota, timeout );
3725 /* assume we only get relative timeout */
3726 if (timeout->QuadPart > 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout->QuadPart) );
3728 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3730 SERVER_START_REQ( create_named_pipe )
3732 req->access = access;
3733 req->options = options;
3734 req->sharing = sharing;
3735 req->flags =
3736 (pipe_type ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0) |
3737 (read_mode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0) |
3738 (completion_mode ? NAMED_PIPE_NONBLOCKING_MODE : 0);
3739 req->maxinstances = max_inst;
3740 req->outsize = outbound_quota;
3741 req->insize = inbound_quota;
3742 req->timeout = timeout->QuadPart;
3743 wine_server_add_data( req, objattr, len );
3744 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
3746 SERVER_END_REQ;
3748 free( objattr );
3749 return status;
3753 /******************************************************************
3754 * NtDeleteFile (NTDLL.@)
3756 NTSTATUS WINAPI NtDeleteFile( OBJECT_ATTRIBUTES *attr )
3758 HANDLE handle;
3759 NTSTATUS status;
3760 IO_STATUS_BLOCK io;
3762 status = NtCreateFile( &handle, GENERIC_READ | GENERIC_WRITE | DELETE, attr, &io, NULL, 0,
3763 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN,
3764 FILE_DELETE_ON_CLOSE, NULL, 0 );
3765 if (status == STATUS_SUCCESS) NtClose( handle );
3766 return status;
3770 /******************************************************************************
3771 * NtQueryFullAttributesFile (NTDLL.@)
3773 NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr,
3774 FILE_NETWORK_OPEN_INFORMATION *info )
3776 char *unix_name;
3777 NTSTATUS status;
3779 if (!(status = nt_to_unix_file_name_attr( attr, &unix_name, NULL, FILE_OPEN )))
3781 ULONG attributes;
3782 struct stat st;
3784 if (get_file_info( unix_name, &st, &attributes ) == -1)
3785 status = errno_to_status( errno );
3786 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3787 status = STATUS_INVALID_INFO_CLASS;
3788 else
3790 FILE_BASIC_INFORMATION basic;
3791 FILE_STANDARD_INFORMATION std;
3793 fill_file_info( &st, attributes, &basic, FileBasicInformation );
3794 fill_file_info( &st, attributes, &std, FileStandardInformation );
3796 info->CreationTime = basic.CreationTime;
3797 info->LastAccessTime = basic.LastAccessTime;
3798 info->LastWriteTime = basic.LastWriteTime;
3799 info->ChangeTime = basic.ChangeTime;
3800 info->AllocationSize = std.AllocationSize;
3801 info->EndOfFile = std.EndOfFile;
3802 info->FileAttributes = basic.FileAttributes;
3803 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
3805 free( unix_name );
3807 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
3808 return status;
3812 /******************************************************************************
3813 * NtQueryAttributesFile (NTDLL.@)
3815 NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC_INFORMATION *info )
3817 char *unix_name;
3818 NTSTATUS status;
3820 if (!(status = nt_to_unix_file_name_attr( attr, &unix_name, NULL, FILE_OPEN )))
3822 ULONG attributes;
3823 struct stat st;
3825 if (get_file_info( unix_name, &st, &attributes ) == -1)
3826 status = errno_to_status( errno );
3827 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3828 status = STATUS_INVALID_INFO_CLASS;
3829 else
3831 status = fill_file_info( &st, attributes, info, FileBasicInformation );
3832 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
3834 free( unix_name );
3836 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
3837 return status;
3841 /******************************************************************************
3842 * NtQueryInformationFile (NTDLL.@)
3844 NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
3845 void *ptr, LONG len, FILE_INFORMATION_CLASS class )
3847 static const size_t info_sizes[] =
3850 sizeof(FILE_DIRECTORY_INFORMATION), /* FileDirectoryInformation */
3851 sizeof(FILE_FULL_DIRECTORY_INFORMATION), /* FileFullDirectoryInformation */
3852 sizeof(FILE_BOTH_DIRECTORY_INFORMATION), /* FileBothDirectoryInformation */
3853 sizeof(FILE_BASIC_INFORMATION), /* FileBasicInformation */
3854 sizeof(FILE_STANDARD_INFORMATION), /* FileStandardInformation */
3855 sizeof(FILE_INTERNAL_INFORMATION), /* FileInternalInformation */
3856 sizeof(FILE_EA_INFORMATION), /* FileEaInformation */
3857 0, /* FileAccessInformation */
3858 sizeof(FILE_NAME_INFORMATION), /* FileNameInformation */
3859 sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
3860 0, /* FileLinkInformation */
3861 sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR), /* FileNamesInformation */
3862 sizeof(FILE_DISPOSITION_INFORMATION), /* FileDispositionInformation */
3863 sizeof(FILE_POSITION_INFORMATION), /* FilePositionInformation */
3864 sizeof(FILE_FULL_EA_INFORMATION), /* FileFullEaInformation */
3865 0, /* FileModeInformation */
3866 sizeof(FILE_ALIGNMENT_INFORMATION), /* FileAlignmentInformation */
3867 sizeof(FILE_ALL_INFORMATION), /* FileAllInformation */
3868 sizeof(FILE_ALLOCATION_INFORMATION), /* FileAllocationInformation */
3869 sizeof(FILE_END_OF_FILE_INFORMATION), /* FileEndOfFileInformation */
3870 0, /* FileAlternateNameInformation */
3871 sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
3872 sizeof(FILE_PIPE_INFORMATION), /* FilePipeInformation */
3873 sizeof(FILE_PIPE_LOCAL_INFORMATION), /* FilePipeLocalInformation */
3874 0, /* FilePipeRemoteInformation */
3875 sizeof(FILE_MAILSLOT_QUERY_INFORMATION), /* FileMailslotQueryInformation */
3876 0, /* FileMailslotSetInformation */
3877 0, /* FileCompressionInformation */
3878 0, /* FileObjectIdInformation */
3879 0, /* FileCompletionInformation */
3880 0, /* FileMoveClusterInformation */
3881 0, /* FileQuotaInformation */
3882 0, /* FileReparsePointInformation */
3883 sizeof(FILE_NETWORK_OPEN_INFORMATION), /* FileNetworkOpenInformation */
3884 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), /* FileAttributeTagInformation */
3885 0, /* FileTrackingInformation */
3886 0, /* FileIdBothDirectoryInformation */
3887 0, /* FileIdFullDirectoryInformation */
3888 0, /* FileValidDataLengthInformation */
3889 0, /* FileShortNameInformation */
3890 0, /* FileIoCompletionNotificationInformation, */
3891 0, /* FileIoStatusBlockRangeInformation */
3892 0, /* FileIoPriorityHintInformation */
3893 0, /* FileSfioReserveInformation */
3894 0, /* FileSfioVolumeInformation */
3895 0, /* FileHardLinkInformation */
3896 0, /* FileProcessIdsUsingFileInformation */
3897 0, /* FileNormalizedNameInformation */
3898 0, /* FileNetworkPhysicalNameInformation */
3899 0, /* FileIdGlobalTxDirectoryInformation */
3900 0, /* FileIsRemoteDeviceInformation */
3901 0, /* FileAttributeCacheInformation */
3902 0, /* FileNumaNodeInformation */
3903 0, /* FileStandardLinkInformation */
3904 0, /* FileRemoteProtocolInformation */
3905 0, /* FileRenameInformationBypassAccessCheck */
3906 0, /* FileLinkInformationBypassAccessCheck */
3907 0, /* FileVolumeNameInformation */
3908 sizeof(FILE_ID_INFORMATION), /* FileIdInformation */
3909 0, /* FileIdExtdDirectoryInformation */
3910 0, /* FileReplaceCompletionInformation */
3911 0, /* FileHardLinkFullIdInformation */
3912 0, /* FileIdExtdBothDirectoryInformation */
3915 struct stat st;
3916 int fd, needs_close = FALSE;
3917 ULONG attr;
3918 unsigned int options;
3920 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class);
3922 io->Information = 0;
3924 if (class <= 0 || class >= FileMaximumInformation)
3925 return io->u.Status = STATUS_INVALID_INFO_CLASS;
3926 if (!info_sizes[class])
3927 return server_get_file_info( handle, io, ptr, len, class );
3928 if (len < info_sizes[class])
3929 return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
3931 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options )))
3933 if (io->u.Status != STATUS_BAD_DEVICE_TYPE) return io->u.Status;
3934 return server_get_file_info( handle, io, ptr, len, class );
3937 switch (class)
3939 case FileBasicInformation:
3940 if (fd_get_file_info( fd, options, &st, &attr ) == -1)
3941 io->u.Status = errno_to_status( errno );
3942 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3943 io->u.Status = STATUS_INVALID_INFO_CLASS;
3944 else
3945 fill_file_info( &st, attr, ptr, class );
3946 break;
3947 case FileStandardInformation:
3949 FILE_STANDARD_INFORMATION *info = ptr;
3951 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
3952 else
3954 fill_file_info( &st, attr, info, class );
3955 info->DeletePending = FALSE; /* FIXME */
3958 break;
3959 case FilePositionInformation:
3961 FILE_POSITION_INFORMATION *info = ptr;
3962 off_t res = lseek( fd, 0, SEEK_CUR );
3963 if (res == (off_t)-1) io->u.Status = errno_to_status( errno );
3964 else info->CurrentByteOffset.QuadPart = res;
3966 break;
3967 case FileInternalInformation:
3968 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
3969 else fill_file_info( &st, attr, ptr, class );
3970 break;
3971 case FileEaInformation:
3973 FILE_EA_INFORMATION *info = ptr;
3974 info->EaSize = 0;
3976 break;
3977 case FileEndOfFileInformation:
3978 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
3979 else fill_file_info( &st, attr, ptr, class );
3980 break;
3981 case FileAllInformation:
3983 FILE_ALL_INFORMATION *info = ptr;
3984 char *unix_name;
3986 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
3987 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3988 io->u.Status = STATUS_INVALID_INFO_CLASS;
3989 else if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
3991 LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
3993 fill_file_info( &st, attr, info, FileAllInformation );
3994 info->StandardInformation.DeletePending = FALSE; /* FIXME */
3995 info->EaInformation.EaSize = 0;
3996 info->AccessInformation.AccessFlags = 0; /* FIXME */
3997 info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
3998 info->ModeInformation.Mode = 0; /* FIXME */
3999 info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
4001 io->u.Status = fill_name_info( unix_name, &info->NameInformation, &name_len );
4002 free( unix_name );
4003 io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len;
4006 break;
4007 case FileMailslotQueryInformation:
4009 FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;
4011 SERVER_START_REQ( set_mailslot_info )
4013 req->handle = wine_server_obj_handle( handle );
4014 req->flags = 0;
4015 io->u.Status = wine_server_call( req );
4016 if( io->u.Status == STATUS_SUCCESS )
4018 info->MaximumMessageSize = reply->max_msgsize;
4019 info->MailslotQuota = 0;
4020 info->NextMessageSize = 0;
4021 info->MessagesAvailable = 0;
4022 info->ReadTimeout.QuadPart = reply->read_timeout;
4025 SERVER_END_REQ;
4026 if (!io->u.Status)
4028 char *tmpbuf;
4029 ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
4030 if (size > 0x10000) size = 0x10000;
4031 if ((tmpbuf = malloc( size )))
4033 if (!server_get_unix_fd( handle, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
4035 int res = recv( fd, tmpbuf, size, MSG_PEEK );
4036 info->MessagesAvailable = (res > 0);
4037 info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
4038 if (needs_close) close( fd );
4040 free( tmpbuf );
4044 break;
4045 case FileNameInformation:
4047 FILE_NAME_INFORMATION *info = ptr;
4048 char *unix_name;
4050 if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4052 LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
4053 io->u.Status = fill_name_info( unix_name, info, &name_len );
4054 free( unix_name );
4055 io->Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + name_len;
4058 break;
4059 case FileNetworkOpenInformation:
4061 FILE_NETWORK_OPEN_INFORMATION *info = ptr;
4062 char *unix_name;
4064 if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4066 ULONG attributes;
4067 struct stat st;
4069 if (get_file_info( unix_name, &st, &attributes ) == -1)
4070 io->u.Status = errno_to_status( errno );
4071 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4072 io->u.Status = STATUS_INVALID_INFO_CLASS;
4073 else
4075 FILE_BASIC_INFORMATION basic;
4076 FILE_STANDARD_INFORMATION std;
4078 fill_file_info( &st, attributes, &basic, FileBasicInformation );
4079 fill_file_info( &st, attributes, &std, FileStandardInformation );
4081 info->CreationTime = basic.CreationTime;
4082 info->LastAccessTime = basic.LastAccessTime;
4083 info->LastWriteTime = basic.LastWriteTime;
4084 info->ChangeTime = basic.ChangeTime;
4085 info->AllocationSize = std.AllocationSize;
4086 info->EndOfFile = std.EndOfFile;
4087 info->FileAttributes = basic.FileAttributes;
4089 free( unix_name );
4092 break;
4093 case FileIdInformation:
4094 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4095 else
4097 struct mountmgr_unix_drive drive;
4098 FILE_ID_INFORMATION *info = ptr;
4100 info->VolumeSerialNumber = 0;
4101 if (!(io->u.Status = get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) )))
4102 info->VolumeSerialNumber = drive.serial;
4103 memset( &info->FileId, 0, sizeof(info->FileId) );
4104 *(ULONGLONG *)&info->FileId = st.st_ino;
4106 break;
4107 case FileAttributeTagInformation:
4108 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4109 else
4111 FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
4112 info->FileAttributes = attr;
4113 info->ReparseTag = 0; /* FIXME */
4114 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
4115 info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
4117 break;
4118 default:
4119 FIXME("Unsupported class (%d)\n", class);
4120 io->u.Status = STATUS_NOT_IMPLEMENTED;
4121 break;
4123 if (needs_close) close( fd );
4124 if (io->u.Status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
4125 return io->u.Status;
4129 /******************************************************************************
4130 * NtSetInformationFile (NTDLL.@)
4132 NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4133 void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
4135 int fd, needs_close;
4137 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class );
4139 io->u.Status = STATUS_SUCCESS;
4140 switch (class)
4142 case FileBasicInformation:
4143 if (len >= sizeof(FILE_BASIC_INFORMATION))
4145 struct stat st;
4146 const FILE_BASIC_INFORMATION *info = ptr;
4147 LARGE_INTEGER mtime, atime;
4149 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4150 return io->u.Status;
4152 mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart;
4153 atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
4155 if (atime.QuadPart || mtime.QuadPart)
4156 io->u.Status = set_file_times( fd, &mtime, &atime );
4158 if (io->u.Status == STATUS_SUCCESS && info->FileAttributes)
4160 if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
4161 else
4163 if (info->FileAttributes & FILE_ATTRIBUTE_READONLY)
4165 if (S_ISDIR( st.st_mode))
4166 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
4167 else
4168 st.st_mode &= ~0222; /* clear write permission bits */
4170 else
4172 /* add write permission only where we already have read permission */
4173 st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~start_umask);
4175 if (fchmod( fd, st.st_mode ) == -1) io->u.Status = errno_to_status( errno );
4179 if (needs_close) close( fd );
4181 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4182 break;
4184 case FilePositionInformation:
4185 if (len >= sizeof(FILE_POSITION_INFORMATION))
4187 const FILE_POSITION_INFORMATION *info = ptr;
4189 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4190 return io->u.Status;
4192 if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
4193 io->u.Status = errno_to_status( errno );
4195 if (needs_close) close( fd );
4197 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4198 break;
4200 case FileEndOfFileInformation:
4201 if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
4203 struct stat st;
4204 const FILE_END_OF_FILE_INFORMATION *info = ptr;
4206 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4207 return io->u.Status;
4209 /* first try normal truncate */
4210 if (ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
4212 /* now check for the need to extend the file */
4213 if (fstat( fd, &st ) != -1 && (off_t)info->EndOfFile.QuadPart > st.st_size)
4215 static const char zero;
4217 /* extend the file one byte beyond the requested size and then truncate it */
4218 /* this should work around ftruncate implementations that can't extend files */
4219 if (pwrite( fd, &zero, 1, (off_t)info->EndOfFile.QuadPart ) != -1 &&
4220 ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
4222 io->u.Status = errno_to_status( errno );
4224 if (needs_close) close( fd );
4226 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4227 break;
4229 case FilePipeInformation:
4230 if (len >= sizeof(FILE_PIPE_INFORMATION))
4232 FILE_PIPE_INFORMATION *info = ptr;
4234 if ((info->CompletionMode | info->ReadMode) & ~1)
4236 io->u.Status = STATUS_INVALID_PARAMETER;
4237 break;
4240 SERVER_START_REQ( set_named_pipe_info )
4242 req->handle = wine_server_obj_handle( handle );
4243 req->flags = (info->CompletionMode ? NAMED_PIPE_NONBLOCKING_MODE : 0) |
4244 (info->ReadMode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0);
4245 io->u.Status = wine_server_call( req );
4247 SERVER_END_REQ;
4249 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4250 break;
4252 case FileMailslotSetInformation:
4254 FILE_MAILSLOT_SET_INFORMATION *info = ptr;
4256 SERVER_START_REQ( set_mailslot_info )
4258 req->handle = wine_server_obj_handle( handle );
4259 req->flags = MAILSLOT_SET_READ_TIMEOUT;
4260 req->read_timeout = info->ReadTimeout.QuadPart;
4261 io->u.Status = wine_server_call( req );
4263 SERVER_END_REQ;
4265 break;
4267 case FileCompletionInformation:
4268 if (len >= sizeof(FILE_COMPLETION_INFORMATION))
4270 FILE_COMPLETION_INFORMATION *info = ptr;
4272 SERVER_START_REQ( set_completion_info )
4274 req->handle = wine_server_obj_handle( handle );
4275 req->chandle = wine_server_obj_handle( info->CompletionPort );
4276 req->ckey = info->CompletionKey;
4277 io->u.Status = wine_server_call( req );
4279 SERVER_END_REQ;
4280 } else
4281 io->u.Status = STATUS_INVALID_PARAMETER_3;
4282 break;
4284 case FileIoCompletionNotificationInformation:
4285 if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION))
4287 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr;
4289 if (info->Flags & FILE_SKIP_SET_USER_EVENT_ON_FAST_IO)
4290 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4292 SERVER_START_REQ( set_fd_completion_mode )
4294 req->handle = wine_server_obj_handle( handle );
4295 req->flags = info->Flags;
4296 io->u.Status = wine_server_call( req );
4298 SERVER_END_REQ;
4299 } else
4300 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4301 break;
4303 case FileIoPriorityHintInformation:
4304 if (len >= sizeof(FILE_IO_PRIORITY_HINT_INFO))
4306 FILE_IO_PRIORITY_HINT_INFO *info = ptr;
4307 if (info->PriorityHint < MaximumIoPriorityHintType)
4308 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info->PriorityHint );
4309 else
4310 io->u.Status = STATUS_INVALID_PARAMETER;
4312 else io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4313 break;
4315 case FileAllInformation:
4316 io->u.Status = STATUS_INVALID_INFO_CLASS;
4317 break;
4319 case FileValidDataLengthInformation:
4320 if (len >= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION))
4322 struct stat st;
4323 const FILE_VALID_DATA_LENGTH_INFORMATION *info = ptr;
4325 if ((io->u.Status = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL )))
4326 return io->u.Status;
4328 if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
4329 else if (info->ValidDataLength.QuadPart <= 0 || (off_t)info->ValidDataLength.QuadPart > st.st_size)
4330 io->u.Status = STATUS_INVALID_PARAMETER;
4331 else
4333 #ifdef HAVE_FALLOCATE
4334 if (fallocate( fd, 0, 0, (off_t)info->ValidDataLength.QuadPart ) == -1)
4336 NTSTATUS status = errno_to_status( errno );
4337 if (status == STATUS_NOT_SUPPORTED) WARN( "fallocate not supported on this filesystem\n" );
4338 else io->u.Status = status;
4340 #else
4341 FIXME( "setting valid data length not supported\n" );
4342 #endif
4344 if (needs_close) close( fd );
4346 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4347 break;
4349 case FileDispositionInformation:
4350 if (len >= sizeof(FILE_DISPOSITION_INFORMATION))
4352 FILE_DISPOSITION_INFORMATION *info = ptr;
4354 SERVER_START_REQ( set_fd_disp_info )
4356 req->handle = wine_server_obj_handle( handle );
4357 req->unlink = info->DoDeleteFile;
4358 io->u.Status = wine_server_call( req );
4360 SERVER_END_REQ;
4361 } else
4362 io->u.Status = STATUS_INVALID_PARAMETER_3;
4363 break;
4365 case FileRenameInformation:
4366 if (len >= sizeof(FILE_RENAME_INFORMATION))
4368 FILE_RENAME_INFORMATION *info = ptr;
4369 UNICODE_STRING name_str, nt_name = { 0 };
4370 OBJECT_ATTRIBUTES attr;
4371 char *unix_name;
4373 name_str.Buffer = info->FileName;
4374 name_str.Length = info->FileNameLength;
4375 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4377 attr.Length = sizeof(attr);
4378 attr.ObjectName = &name_str;
4379 attr.RootDirectory = info->RootDirectory;
4380 attr.Attributes = OBJ_CASE_INSENSITIVE;
4382 io->u.Status = nt_to_unix_file_name_attr( &attr, &unix_name, &nt_name, FILE_OPEN_IF );
4383 if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
4384 break;
4386 SERVER_START_REQ( set_fd_name_info )
4388 req->handle = wine_server_obj_handle( handle );
4389 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4390 req->namelen = nt_name.Length;
4391 req->link = FALSE;
4392 req->replace = info->ReplaceIfExists;
4393 wine_server_add_data( req, nt_name.Buffer, nt_name.Length );
4394 wine_server_add_data( req, unix_name, strlen(unix_name) );
4395 io->u.Status = wine_server_call( req );
4397 SERVER_END_REQ;
4399 free( unix_name );
4400 free( nt_name.Buffer );
4402 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4403 break;
4405 case FileLinkInformation:
4406 if (len >= sizeof(FILE_LINK_INFORMATION))
4408 FILE_LINK_INFORMATION *info = ptr;
4409 UNICODE_STRING name_str, nt_name = { 0 };
4410 OBJECT_ATTRIBUTES attr;
4411 char *unix_name;
4413 name_str.Buffer = info->FileName;
4414 name_str.Length = info->FileNameLength;
4415 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4417 attr.Length = sizeof(attr);
4418 attr.ObjectName = &name_str;
4419 attr.RootDirectory = info->RootDirectory;
4420 attr.Attributes = OBJ_CASE_INSENSITIVE;
4422 io->u.Status = nt_to_unix_file_name_attr( &attr, &unix_name, &nt_name, FILE_OPEN_IF );
4423 if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
4424 break;
4426 SERVER_START_REQ( set_fd_name_info )
4428 req->handle = wine_server_obj_handle( handle );
4429 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4430 req->namelen = nt_name.Length;
4431 req->link = TRUE;
4432 req->replace = info->ReplaceIfExists;
4433 wine_server_add_data( req, nt_name.Buffer, nt_name.Length );
4434 wine_server_add_data( req, unix_name, strlen(unix_name) );
4435 io->u.Status = wine_server_call( req );
4437 SERVER_END_REQ;
4439 free( unix_name );
4440 free( nt_name.Buffer );
4442 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4443 break;
4445 default:
4446 FIXME("Unsupported class (%d)\n", class);
4447 io->u.Status = STATUS_NOT_IMPLEMENTED;
4448 break;
4450 io->Information = 0;
4451 return io->u.Status;
4455 /***********************************************************************
4456 * Asynchronous file I/O *
4459 typedef NTSTATUS async_callback_t( void *user, IO_STATUS_BLOCK *io, NTSTATUS status );
4461 struct async_fileio
4463 async_callback_t *callback; /* must be the first field */
4464 struct async_fileio *next;
4465 HANDLE handle;
4468 struct async_fileio_read
4470 struct async_fileio io;
4471 char *buffer;
4472 unsigned int already;
4473 unsigned int count;
4474 BOOL avail_mode;
4477 struct async_fileio_write
4479 struct async_fileio io;
4480 const char *buffer;
4481 unsigned int already;
4482 unsigned int count;
4485 struct async_fileio_read_changes
4487 struct async_fileio io;
4488 void *buffer;
4489 ULONG buffer_size;
4490 ULONG data_size;
4491 char data[1];
4494 struct async_irp
4496 struct async_fileio io;
4497 void *buffer; /* buffer for output */
4498 ULONG size; /* size of buffer */
4501 static struct async_fileio *fileio_freelist;
4503 static void release_fileio( struct async_fileio *io )
4505 for (;;)
4507 struct async_fileio *next = fileio_freelist;
4508 io->next = next;
4509 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist, io, next ) == next) return;
4513 static struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle )
4515 /* first free remaining previous fileinfos */
4516 struct async_fileio *io = InterlockedExchangePointer( (void **)&fileio_freelist, NULL );
4518 while (io)
4520 struct async_fileio *next = io->next;
4521 free( io );
4522 io = next;
4525 if ((io = malloc( size )))
4527 io->callback = callback;
4528 io->handle = handle;
4530 return io;
4533 static async_data_t server_async( HANDLE handle, struct async_fileio *user, HANDLE event,
4534 PIO_APC_ROUTINE apc, void *apc_context, IO_STATUS_BLOCK *io )
4536 async_data_t async;
4537 async.handle = wine_server_obj_handle( handle );
4538 async.user = wine_server_client_ptr( user );
4539 async.iosb = wine_server_client_ptr( io );
4540 async.event = wine_server_obj_handle( event );
4541 async.apc = wine_server_client_ptr( apc );
4542 async.apc_context = wine_server_client_ptr( apc_context );
4543 return async;
4546 static NTSTATUS wait_async( HANDLE handle, BOOL alertable, IO_STATUS_BLOCK *io )
4548 if (NtWaitForSingleObject( handle, alertable, NULL )) return STATUS_PENDING;
4549 return io->u.Status;
4552 /* callback for irp async I/O completion */
4553 static NTSTATUS irp_completion( void *user, IO_STATUS_BLOCK *io, NTSTATUS status )
4555 struct async_irp *async = user;
4556 ULONG information = 0;
4558 if (status == STATUS_ALERTED)
4560 SERVER_START_REQ( get_async_result )
4562 req->user_arg = wine_server_client_ptr( async );
4563 wine_server_set_reply( req, async->buffer, async->size );
4564 status = virtual_locked_server_call( req );
4565 information = reply->size;
4567 SERVER_END_REQ;
4569 if (status != STATUS_PENDING)
4571 io->u.Status = status;
4572 io->Information = information;
4573 release_fileio( &async->io );
4575 return status;
4578 static NTSTATUS async_read_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
4580 struct async_fileio_read *fileio = user;
4581 int fd, needs_close, result;
4583 switch (status)
4585 case STATUS_ALERTED: /* got some new data */
4586 /* check to see if the data is ready (non-blocking) */
4587 if ((status = server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd,
4588 &needs_close, NULL, NULL )))
4589 break;
4591 result = virtual_locked_read(fd, &fileio->buffer[fileio->already], fileio->count-fileio->already);
4592 if (needs_close) close( fd );
4594 if (result < 0)
4596 if (errno == EAGAIN || errno == EINTR)
4597 status = STATUS_PENDING;
4598 else /* check to see if the transfer is complete */
4599 status = errno_to_status( errno );
4601 else if (result == 0)
4603 status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN;
4605 else
4607 fileio->already += result;
4608 if (fileio->already >= fileio->count || fileio->avail_mode)
4609 status = STATUS_SUCCESS;
4610 else
4611 status = STATUS_PENDING;
4613 break;
4615 case STATUS_TIMEOUT:
4616 case STATUS_IO_TIMEOUT:
4617 if (fileio->already) status = STATUS_SUCCESS;
4618 break;
4620 if (status != STATUS_PENDING)
4622 iosb->u.Status = status;
4623 iosb->Information = fileio->already;
4624 release_fileio( &fileio->io );
4626 return status;
4629 static NTSTATUS async_write_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
4631 struct async_fileio_write *fileio = user;
4632 int result, fd, needs_close;
4633 enum server_fd_type type;
4635 switch (status)
4637 case STATUS_ALERTED:
4638 /* write some data (non-blocking) */
4639 if ((status = server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd,
4640 &needs_close, &type, NULL )))
4641 break;
4643 if (!fileio->count && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET))
4644 result = send( fd, fileio->buffer, 0, 0 );
4645 else
4646 result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already );
4648 if (needs_close) close( fd );
4650 if (result < 0)
4652 if (errno == EAGAIN || errno == EINTR) status = STATUS_PENDING;
4653 else status = errno_to_status( errno );
4655 else
4657 fileio->already += result;
4658 status = (fileio->already < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS;
4660 break;
4662 case STATUS_TIMEOUT:
4663 case STATUS_IO_TIMEOUT:
4664 if (fileio->already) status = STATUS_SUCCESS;
4665 break;
4667 if (status != STATUS_PENDING)
4669 iosb->u.Status = status;
4670 iosb->Information = fileio->already;
4671 release_fileio( &fileio->io );
4673 return status;
4676 /* do a read call through the server */
4677 static NTSTATUS server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
4678 IO_STATUS_BLOCK *io, void *buffer, ULONG size,
4679 LARGE_INTEGER *offset, ULONG *key )
4681 struct async_irp *async;
4682 NTSTATUS status;
4683 HANDLE wait_handle;
4684 ULONG options;
4686 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4687 return STATUS_NO_MEMORY;
4689 async->buffer = buffer;
4690 async->size = size;
4692 SERVER_START_REQ( read )
4694 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4695 req->pos = offset ? offset->QuadPart : 0;
4696 wine_server_set_reply( req, buffer, size );
4697 status = virtual_locked_server_call( req );
4698 wait_handle = wine_server_ptr_handle( reply->wait );
4699 options = reply->options;
4700 if (wait_handle && status != STATUS_PENDING)
4702 io->u.Status = status;
4703 io->Information = wine_server_reply_size( reply );
4706 SERVER_END_REQ;
4708 if (status != STATUS_PENDING) free( async );
4710 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io );
4711 return status;
4714 /* do a write call through the server */
4715 static NTSTATUS server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
4716 IO_STATUS_BLOCK *io, const void *buffer, ULONG size,
4717 LARGE_INTEGER *offset, ULONG *key )
4719 struct async_irp *async;
4720 NTSTATUS status;
4721 HANDLE wait_handle;
4722 ULONG options;
4724 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4725 return STATUS_NO_MEMORY;
4727 async->buffer = NULL;
4728 async->size = 0;
4730 SERVER_START_REQ( write )
4732 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4733 req->pos = offset ? offset->QuadPart : 0;
4734 wine_server_add_data( req, buffer, size );
4735 status = wine_server_call( req );
4736 wait_handle = wine_server_ptr_handle( reply->wait );
4737 options = reply->options;
4738 if (wait_handle && status != STATUS_PENDING)
4740 io->u.Status = status;
4741 io->Information = reply->size;
4744 SERVER_END_REQ;
4746 if (status != STATUS_PENDING) free( async );
4748 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io );
4749 return status;
4752 /* do an ioctl call through the server */
4753 static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
4754 PIO_APC_ROUTINE apc, PVOID apc_context,
4755 IO_STATUS_BLOCK *io, ULONG code,
4756 const void *in_buffer, ULONG in_size,
4757 PVOID out_buffer, ULONG out_size )
4759 struct async_irp *async;
4760 NTSTATUS status;
4761 HANDLE wait_handle;
4762 ULONG options;
4764 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4765 return STATUS_NO_MEMORY;
4766 async->buffer = out_buffer;
4767 async->size = out_size;
4769 SERVER_START_REQ( ioctl )
4771 req->code = code;
4772 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4773 wine_server_add_data( req, in_buffer, in_size );
4774 if ((code & 3) != METHOD_BUFFERED) wine_server_add_data( req, out_buffer, out_size );
4775 wine_server_set_reply( req, out_buffer, out_size );
4776 status = virtual_locked_server_call( req );
4777 wait_handle = wine_server_ptr_handle( reply->wait );
4778 options = reply->options;
4779 if (wait_handle && status != STATUS_PENDING)
4781 io->u.Status = status;
4782 io->Information = wine_server_reply_size( reply );
4785 SERVER_END_REQ;
4787 if (status == STATUS_NOT_SUPPORTED)
4788 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
4789 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
4791 if (status != STATUS_PENDING) free( async );
4793 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io );
4794 return status;
4798 struct io_timeouts
4800 int interval; /* max interval between two bytes */
4801 int total; /* total timeout for the whole operation */
4802 int end_time; /* absolute time of end of operation */
4805 /* retrieve the I/O timeouts to use for a given handle */
4806 static NTSTATUS get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read,
4807 struct io_timeouts *timeouts )
4809 NTSTATUS status = STATUS_SUCCESS;
4811 timeouts->interval = timeouts->total = -1;
4813 switch(type)
4815 case FD_TYPE_SERIAL:
4817 /* GetCommTimeouts */
4818 SERIAL_TIMEOUTS st;
4819 IO_STATUS_BLOCK io;
4821 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
4822 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
4823 if (status) break;
4825 if (is_read)
4827 if (st.ReadIntervalTimeout)
4828 timeouts->interval = st.ReadIntervalTimeout;
4830 if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant)
4832 timeouts->total = st.ReadTotalTimeoutConstant;
4833 if (st.ReadTotalTimeoutMultiplier != MAXDWORD)
4834 timeouts->total += count * st.ReadTotalTimeoutMultiplier;
4836 else if (st.ReadIntervalTimeout == MAXDWORD)
4837 timeouts->interval = timeouts->total = 0;
4839 else /* write */
4841 if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant)
4843 timeouts->total = st.WriteTotalTimeoutConstant;
4844 if (st.WriteTotalTimeoutMultiplier != MAXDWORD)
4845 timeouts->total += count * st.WriteTotalTimeoutMultiplier;
4848 break;
4850 case FD_TYPE_MAILSLOT:
4851 if (is_read)
4853 timeouts->interval = 0; /* return as soon as we got something */
4854 SERVER_START_REQ( set_mailslot_info )
4856 req->handle = wine_server_obj_handle( handle );
4857 req->flags = 0;
4858 if (!(status = wine_server_call( req )) &&
4859 reply->read_timeout != TIMEOUT_INFINITE)
4860 timeouts->total = reply->read_timeout / -10000;
4862 SERVER_END_REQ;
4864 break;
4865 case FD_TYPE_SOCKET:
4866 case FD_TYPE_CHAR:
4867 if (is_read) timeouts->interval = 0; /* return as soon as we got something */
4868 break;
4869 default:
4870 break;
4872 if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total;
4873 return STATUS_SUCCESS;
4877 /* retrieve the timeout for the next wait, in milliseconds */
4878 static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already )
4880 int ret = -1;
4882 if (timeouts->total != -1)
4884 ret = timeouts->end_time - NtGetTickCount();
4885 if (ret < 0) ret = 0;
4887 if (already && timeouts->interval != -1)
4889 if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval;
4891 return ret;
4895 /* retrieve the avail_mode flag for async reads */
4896 static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode )
4898 NTSTATUS status = STATUS_SUCCESS;
4900 switch(type)
4902 case FD_TYPE_SERIAL:
4904 /* GetCommTimeouts */
4905 SERIAL_TIMEOUTS st;
4906 IO_STATUS_BLOCK io;
4908 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
4909 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
4910 if (status) break;
4911 *avail_mode = (!st.ReadTotalTimeoutMultiplier &&
4912 !st.ReadTotalTimeoutConstant &&
4913 st.ReadIntervalTimeout == MAXDWORD);
4914 break;
4916 case FD_TYPE_MAILSLOT:
4917 case FD_TYPE_SOCKET:
4918 case FD_TYPE_CHAR:
4919 *avail_mode = TRUE;
4920 break;
4921 default:
4922 *avail_mode = FALSE;
4923 break;
4925 return status;
4928 /* register an async I/O for a file read; helper for NtReadFile */
4929 static NTSTATUS register_async_file_read( HANDLE handle, HANDLE event,
4930 PIO_APC_ROUTINE apc, void *apc_user,
4931 IO_STATUS_BLOCK *iosb, void *buffer,
4932 ULONG already, ULONG length, BOOL avail_mode )
4934 struct async_fileio_read *fileio;
4935 NTSTATUS status;
4937 if (!(fileio = (struct async_fileio_read *)alloc_fileio( sizeof(*fileio), async_read_proc, handle )))
4938 return STATUS_NO_MEMORY;
4940 fileio->already = already;
4941 fileio->count = length;
4942 fileio->buffer = buffer;
4943 fileio->avail_mode = avail_mode;
4945 SERVER_START_REQ( register_async )
4947 req->type = ASYNC_TYPE_READ;
4948 req->count = length;
4949 req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb );
4950 status = wine_server_call( req );
4952 SERVER_END_REQ;
4954 if (status != STATUS_PENDING) free( fileio );
4955 return status;
4958 static void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async )
4960 SERVER_START_REQ( add_fd_completion )
4962 req->handle = wine_server_obj_handle( handle );
4963 req->cvalue = value;
4964 req->status = status;
4965 req->information = info;
4966 req->async = async;
4967 wine_server_call( req );
4969 SERVER_END_REQ;
4972 static NTSTATUS set_pending_write( HANDLE device )
4974 NTSTATUS status;
4976 SERVER_START_REQ( set_serial_info )
4978 req->handle = wine_server_obj_handle( device );
4979 req->flags = SERIALINFO_PENDING_WRITE;
4980 status = wine_server_call( req );
4982 SERVER_END_REQ;
4983 return status;
4987 /******************************************************************************
4988 * NtReadFile (NTDLL.@)
4990 NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
4991 IO_STATUS_BLOCK *io, void *buffer, ULONG length,
4992 LARGE_INTEGER *offset, ULONG *key )
4994 int result, unix_handle, needs_close;
4995 unsigned int options;
4996 struct io_timeouts timeouts;
4997 NTSTATUS status, ret_status;
4998 ULONG total = 0;
4999 enum server_fd_type type;
5000 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5001 BOOL send_completion = FALSE, async_read, timeout_init_done = FALSE;
5003 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5004 handle, event, apc, apc_user, io, buffer, length, offset, key );
5006 if (!io) return STATUS_ACCESS_VIOLATION;
5008 status = server_get_unix_fd( handle, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5009 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5011 if (!virtual_check_buffer_for_write( buffer, length )) return STATUS_ACCESS_VIOLATION;
5013 if (status == STATUS_BAD_DEVICE_TYPE)
5014 return server_read_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5016 async_read = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5018 if (type == FD_TYPE_FILE)
5020 if (async_read && (!offset || offset->QuadPart < 0))
5022 status = STATUS_INVALID_PARAMETER;
5023 goto done;
5026 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5028 /* async I/O doesn't make sense on regular files */
5029 while ((result = virtual_locked_pread( unix_handle, buffer, length, offset->QuadPart )) == -1)
5031 if (errno != EINTR)
5033 status = errno_to_status( errno );
5034 goto done;
5037 if (!async_read) /* update file pointer position */
5038 lseek( unix_handle, offset->QuadPart + result, SEEK_SET );
5040 total = result;
5041 status = (total || !length) ? STATUS_SUCCESS : STATUS_END_OF_FILE;
5042 goto done;
5045 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5047 if (async_read && (!offset || offset->QuadPart < 0))
5049 status = STATUS_INVALID_PARAMETER;
5050 goto done;
5054 if (type == FD_TYPE_SERIAL && async_read && length)
5056 /* an asynchronous serial port read with a read interval timeout needs to
5057 skip the synchronous read to make sure that the server starts the read
5058 interval timer after the first read */
5059 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5060 if (timeouts.interval)
5062 status = register_async_file_read( handle, event, apc, apc_user, io,
5063 buffer, total, length, FALSE );
5064 goto err;
5068 for (;;)
5070 if ((result = virtual_locked_read( unix_handle, (char *)buffer + total, length - total )) >= 0)
5072 total += result;
5073 if (!result || total == length)
5075 if (total)
5077 status = STATUS_SUCCESS;
5078 goto done;
5080 switch (type)
5082 case FD_TYPE_FILE:
5083 case FD_TYPE_CHAR:
5084 case FD_TYPE_DEVICE:
5085 status = length ? STATUS_END_OF_FILE : STATUS_SUCCESS;
5086 goto done;
5087 case FD_TYPE_SERIAL:
5088 if (!length)
5090 status = STATUS_SUCCESS;
5091 goto done;
5093 break;
5094 default:
5095 status = STATUS_PIPE_BROKEN;
5096 goto err;
5099 else if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5101 else if (errno != EAGAIN)
5103 if (errno == EINTR) continue;
5104 if (!total) status = errno_to_status( errno );
5105 goto err;
5108 if (async_read)
5110 BOOL avail_mode;
5112 if ((status = get_io_avail_mode( handle, type, &avail_mode ))) goto err;
5113 if (total && avail_mode)
5115 status = STATUS_SUCCESS;
5116 goto done;
5118 status = register_async_file_read( handle, event, apc, apc_user, io,
5119 buffer, total, length, avail_mode );
5120 goto err;
5122 else /* synchronous read, wait for the fd to become ready */
5124 struct pollfd pfd;
5125 int ret, timeout;
5127 if (!timeout_init_done)
5129 timeout_init_done = TRUE;
5130 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5131 if (event) NtResetEvent( event, NULL );
5133 timeout = get_next_io_timeout( &timeouts, total );
5135 pfd.fd = unix_handle;
5136 pfd.events = POLLIN;
5138 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5140 if (total) /* return with what we got so far */
5141 status = STATUS_SUCCESS;
5142 else
5143 status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT;
5144 goto done;
5146 if (ret == -1 && errno != EINTR)
5148 status = errno_to_status( errno );
5149 goto done;
5151 /* will now restart the read */
5155 done:
5156 send_completion = cvalue != 0;
5158 err:
5159 if (needs_close) close( unix_handle );
5160 if (status == STATUS_SUCCESS || (status == STATUS_END_OF_FILE && (!async_read || type == FD_TYPE_FILE)))
5162 io->u.Status = status;
5163 io->Information = total;
5164 TRACE("= SUCCESS (%u)\n", total);
5165 if (event) NtSetEvent( event, NULL );
5166 if (apc && (!status || async_read)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5167 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5169 else
5171 TRACE("= 0x%08x\n", status);
5172 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5175 ret_status = async_read && type == FD_TYPE_FILE && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE)
5176 ? STATUS_PENDING : status;
5178 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5179 return ret_status;
5183 /******************************************************************************
5184 * NtReadFileScatter (NTDLL.@)
5186 NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5187 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5188 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5190 int result, unix_handle, needs_close;
5191 unsigned int options;
5192 NTSTATUS status;
5193 ULONG pos = 0, total = 0;
5194 enum server_fd_type type;
5195 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5196 BOOL send_completion = FALSE;
5198 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5199 file, event, apc, apc_user, io, segments, length, offset, key );
5201 if (!io) return STATUS_ACCESS_VIOLATION;
5203 status = server_get_unix_fd( file, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5204 if (status) return status;
5206 if ((type != FD_TYPE_FILE) ||
5207 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5208 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5210 status = STATUS_INVALID_PARAMETER;
5211 goto error;
5214 while (length)
5216 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5217 result = pread( unix_handle, (char *)segments->Buffer + pos,
5218 min( length - pos, page_size - pos ), offset->QuadPart + total );
5219 else
5220 result = read( unix_handle, (char *)segments->Buffer + pos, min( length - pos, page_size - pos ) );
5222 if (result == -1)
5224 if (errno == EINTR) continue;
5225 status = errno_to_status( errno );
5226 break;
5228 if (!result) break;
5229 total += result;
5230 length -= result;
5231 if ((pos += result) == page_size)
5233 pos = 0;
5234 segments++;
5238 if (total == 0) status = STATUS_END_OF_FILE;
5240 send_completion = cvalue != 0;
5242 if (needs_close) close( unix_handle );
5243 io->u.Status = status;
5244 io->Information = total;
5245 TRACE("= 0x%08x (%u)\n", status, total);
5246 if (event) NtSetEvent( event, NULL );
5247 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5248 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5249 if (send_completion) add_completion( file, cvalue, status, total, TRUE );
5251 return STATUS_PENDING;
5253 error:
5254 if (needs_close) close( unix_handle );
5255 if (event) NtResetEvent( event, NULL );
5256 TRACE("= 0x%08x\n", status);
5257 return status;
5261 /******************************************************************************
5262 * NtWriteFile (NTDLL.@)
5264 NTSTATUS WINAPI NtWriteFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5265 IO_STATUS_BLOCK *io, const void *buffer, ULONG length,
5266 LARGE_INTEGER *offset, ULONG *key )
5268 int result, unix_handle, needs_close;
5269 unsigned int options;
5270 struct io_timeouts timeouts;
5271 NTSTATUS status, ret_status;
5272 ULONG total = 0;
5273 enum server_fd_type type;
5274 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5275 BOOL send_completion = FALSE, async_write, append_write = FALSE, timeout_init_done = FALSE;
5276 LARGE_INTEGER offset_eof;
5278 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5279 handle, event, apc, apc_user, io, buffer, length, offset, key );
5281 if (!io) return STATUS_ACCESS_VIOLATION;
5283 status = server_get_unix_fd( handle, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5284 if (status == STATUS_ACCESS_DENIED)
5286 status = server_get_unix_fd( handle, FILE_APPEND_DATA, &unix_handle,
5287 &needs_close, &type, &options );
5288 append_write = TRUE;
5290 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5292 async_write = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5294 if (!virtual_check_buffer_for_read( buffer, length ))
5296 status = STATUS_INVALID_USER_BUFFER;
5297 goto done;
5300 if (status == STATUS_BAD_DEVICE_TYPE)
5301 return server_write_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5303 if (type == FD_TYPE_FILE)
5305 if (async_write &&
5306 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5308 status = STATUS_INVALID_PARAMETER;
5309 goto done;
5312 if (append_write)
5314 offset_eof.QuadPart = FILE_WRITE_TO_END_OF_FILE;
5315 offset = &offset_eof;
5318 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5320 off_t off = offset->QuadPart;
5322 if (offset->QuadPart == FILE_WRITE_TO_END_OF_FILE)
5324 struct stat st;
5326 if (fstat( unix_handle, &st ) == -1)
5328 status = errno_to_status( errno );
5329 goto done;
5331 off = st.st_size;
5333 else if (offset->QuadPart < 0)
5335 status = STATUS_INVALID_PARAMETER;
5336 goto done;
5339 /* async I/O doesn't make sense on regular files */
5340 while ((result = pwrite( unix_handle, buffer, length, off )) == -1)
5342 if (errno != EINTR)
5344 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5345 else status = errno_to_status( errno );
5346 goto done;
5350 if (!async_write) /* update file pointer position */
5351 lseek( unix_handle, off + result, SEEK_SET );
5353 total = result;
5354 status = STATUS_SUCCESS;
5355 goto done;
5358 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5360 if (async_write &&
5361 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5363 status = STATUS_INVALID_PARAMETER;
5364 goto done;
5368 for (;;)
5370 /* zero-length writes on sockets may not work with plain write(2) */
5371 if (!length && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET))
5372 result = send( unix_handle, buffer, 0, 0 );
5373 else
5374 result = write( unix_handle, (const char *)buffer + total, length - total );
5376 if (result >= 0)
5378 total += result;
5379 if (total == length)
5381 status = STATUS_SUCCESS;
5382 goto done;
5384 if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5386 else if (errno != EAGAIN)
5388 if (errno == EINTR) continue;
5389 if (!total)
5391 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5392 else status = errno_to_status( errno );
5394 goto err;
5397 if (async_write)
5399 struct async_fileio_write *fileio;
5401 fileio = (struct async_fileio_write *)alloc_fileio( sizeof(*fileio), async_write_proc, handle );
5402 if (!fileio)
5404 status = STATUS_NO_MEMORY;
5405 goto err;
5407 fileio->already = total;
5408 fileio->count = length;
5409 fileio->buffer = buffer;
5411 SERVER_START_REQ( register_async )
5413 req->type = ASYNC_TYPE_WRITE;
5414 req->count = length;
5415 req->async = server_async( handle, &fileio->io, event, apc, apc_user, io );
5416 status = wine_server_call( req );
5418 SERVER_END_REQ;
5420 if (status != STATUS_PENDING) free( fileio );
5421 goto err;
5423 else /* synchronous write, wait for the fd to become ready */
5425 struct pollfd pfd;
5426 int ret, timeout;
5428 if (!timeout_init_done)
5430 timeout_init_done = TRUE;
5431 if ((status = get_io_timeouts( handle, type, length, FALSE, &timeouts )))
5432 goto err;
5433 if (event) NtResetEvent( event, NULL );
5435 timeout = get_next_io_timeout( &timeouts, total );
5437 pfd.fd = unix_handle;
5438 pfd.events = POLLOUT;
5440 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5442 /* return with what we got so far */
5443 status = total ? STATUS_SUCCESS : STATUS_TIMEOUT;
5444 goto done;
5446 if (ret == -1 && errno != EINTR)
5448 status = errno_to_status( errno );
5449 goto done;
5451 /* will now restart the write */
5455 done:
5456 send_completion = cvalue != 0;
5458 err:
5459 if (needs_close) close( unix_handle );
5461 if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING))
5462 set_pending_write( handle );
5464 if (status == STATUS_SUCCESS)
5466 io->u.Status = status;
5467 io->Information = total;
5468 TRACE("= SUCCESS (%u)\n", total);
5469 if (event) NtSetEvent( event, NULL );
5470 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5471 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5473 else
5475 TRACE("= 0x%08x\n", status);
5476 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5479 ret_status = async_write && type == FD_TYPE_FILE && status == STATUS_SUCCESS ? STATUS_PENDING : status;
5480 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5481 return ret_status;
5485 /******************************************************************************
5486 * NtWriteFileGather (NTDLL.@)
5488 NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5489 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5490 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5492 int result, unix_handle, needs_close;
5493 unsigned int options;
5494 NTSTATUS status;
5495 ULONG pos = 0, total = 0;
5496 enum server_fd_type type;
5497 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5498 BOOL send_completion = FALSE;
5500 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5501 file, event, apc, apc_user, io, segments, length, offset, key );
5503 if (length % page_size) return STATUS_INVALID_PARAMETER;
5504 if (!io) return STATUS_ACCESS_VIOLATION;
5506 status = server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5507 if (status) return status;
5509 if ((type != FD_TYPE_FILE) ||
5510 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5511 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5513 status = STATUS_INVALID_PARAMETER;
5514 goto done;
5517 while (length)
5519 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5520 result = pwrite( unix_handle, (char *)segments->Buffer + pos,
5521 page_size - pos, offset->QuadPart + total );
5522 else
5523 result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos );
5525 if (result == -1)
5527 if (errno == EINTR) continue;
5528 if (errno == EFAULT)
5530 status = STATUS_INVALID_USER_BUFFER;
5531 goto done;
5533 status = errno_to_status( errno );
5534 break;
5536 if (!result)
5538 status = STATUS_DISK_FULL;
5539 break;
5541 total += result;
5542 length -= result;
5543 if ((pos += result) == page_size)
5545 pos = 0;
5546 segments++;
5550 send_completion = cvalue != 0;
5552 done:
5553 if (needs_close) close( unix_handle );
5554 if (status == STATUS_SUCCESS)
5556 io->u.Status = status;
5557 io->Information = total;
5558 TRACE("= SUCCESS (%u)\n", total);
5559 if (event) NtSetEvent( event, NULL );
5560 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5561 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5563 else
5565 TRACE("= 0x%08x\n", status);
5566 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5568 if (send_completion) add_completion( file, cvalue, status, total, FALSE );
5569 return status;
5573 /******************************************************************************
5574 * NtDeviceIoControlFile (NTDLL.@)
5576 NTSTATUS WINAPI NtDeviceIoControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5577 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5578 void *out_buffer, ULONG out_size )
5580 ULONG device = (code >> 16);
5581 NTSTATUS status = STATUS_NOT_SUPPORTED;
5583 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5584 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5586 switch (device)
5588 case FILE_DEVICE_DISK:
5589 case FILE_DEVICE_CD_ROM:
5590 case FILE_DEVICE_DVD:
5591 case FILE_DEVICE_CONTROLLER:
5592 case FILE_DEVICE_MASS_STORAGE:
5593 status = cdrom_DeviceIoControl( handle, event, apc, apc_context, io, code,
5594 in_buffer, in_size, out_buffer, out_size );
5595 break;
5596 case FILE_DEVICE_SERIAL_PORT:
5597 status = serial_DeviceIoControl( handle, event, apc, apc_context, io, code,
5598 in_buffer, in_size, out_buffer, out_size );
5599 break;
5600 case FILE_DEVICE_TAPE:
5601 status = tape_DeviceIoControl( handle, event, apc, apc_context, io, code,
5602 in_buffer, in_size, out_buffer, out_size );
5603 break;
5606 if (status == STATUS_NOT_SUPPORTED || status == STATUS_BAD_DEVICE_TYPE)
5607 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5608 in_buffer, in_size, out_buffer, out_size );
5610 if (status != STATUS_PENDING) io->u.Status = status;
5611 return status;
5615 /* Tell Valgrind to ignore any holes in structs we will be passing to the
5616 * server */
5617 static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer, ULONG in_size )
5619 #ifdef VALGRIND_MAKE_MEM_DEFINED
5620 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
5621 do { \
5622 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
5623 if ((size) >= FIELD_OFFSET(t, f2)) \
5624 VALGRIND_MAKE_MEM_DEFINED( \
5625 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
5626 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
5627 } while (0)
5629 switch (code)
5631 case FSCTL_PIPE_WAIT:
5632 IGNORE_STRUCT_HOLE(in_buffer, in_size, FILE_PIPE_WAIT_FOR_BUFFER, TimeoutSpecified, Name);
5633 break;
5635 #endif
5639 /******************************************************************************
5640 * NtFsControlFile (NTDLL.@)
5642 NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5643 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5644 void *out_buffer, ULONG out_size )
5646 NTSTATUS status;
5648 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5649 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5651 if (!io) return STATUS_INVALID_PARAMETER;
5653 ignore_server_ioctl_struct_holes( code, in_buffer, in_size );
5655 switch (code)
5657 case FSCTL_DISMOUNT_VOLUME:
5658 status = server_ioctl_file( handle, event, apc, apc_context, io, code,
5659 in_buffer, in_size, out_buffer, out_size );
5660 if (!status) status = unmount_device( handle );
5661 return status;
5663 case FSCTL_PIPE_IMPERSONATE:
5664 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
5665 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5666 in_buffer, in_size, out_buffer, out_size );
5668 case FSCTL_IS_VOLUME_MOUNTED:
5669 case FSCTL_LOCK_VOLUME:
5670 case FSCTL_UNLOCK_VOLUME:
5671 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
5672 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
5673 status = STATUS_SUCCESS;
5674 break;
5676 case FSCTL_GET_RETRIEVAL_POINTERS:
5678 RETRIEVAL_POINTERS_BUFFER *buffer = (RETRIEVAL_POINTERS_BUFFER *)out_buffer;
5680 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
5682 if (out_size >= sizeof(RETRIEVAL_POINTERS_BUFFER))
5684 buffer->ExtentCount = 1;
5685 buffer->StartingVcn.QuadPart = 1;
5686 buffer->Extents[0].NextVcn.QuadPart = 0;
5687 buffer->Extents[0].Lcn.QuadPart = 0;
5688 io->Information = sizeof(RETRIEVAL_POINTERS_BUFFER);
5689 status = STATUS_SUCCESS;
5691 else
5693 io->Information = 0;
5694 status = STATUS_BUFFER_TOO_SMALL;
5696 break;
5699 case FSCTL_GET_OBJECT_ID:
5701 FILE_OBJECTID_BUFFER *info = out_buffer;
5702 int fd, needs_close;
5703 struct stat st;
5705 io->Information = 0;
5706 if (out_size >= sizeof(*info))
5708 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
5709 if (status) break;
5710 fstat( fd, &st );
5711 if (needs_close) close( fd );
5712 memset( info, 0, sizeof(*info) );
5713 memcpy( info->ObjectId, &st.st_dev, sizeof(st.st_dev) );
5714 memcpy( info->ObjectId + 8, &st.st_ino, sizeof(st.st_ino) );
5715 io->Information = sizeof(*info);
5717 else status = STATUS_BUFFER_TOO_SMALL;
5718 break;
5721 case FSCTL_SET_SPARSE:
5722 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
5723 io->Information = 0;
5724 status = STATUS_SUCCESS;
5725 break;
5726 default:
5727 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5728 in_buffer, in_size, out_buffer, out_size );
5731 if (status != STATUS_PENDING) io->u.Status = status;
5732 return status;
5736 /******************************************************************************
5737 * NtFlushBuffersFile (NTDLL.@)
5739 NTSTATUS WINAPI NtFlushBuffersFile( HANDLE handle, IO_STATUS_BLOCK *io )
5741 NTSTATUS ret;
5742 HANDLE wait_handle;
5743 enum server_fd_type type;
5744 int fd, needs_close;
5746 if (!io || !virtual_check_buffer_for_write( io, sizeof(*io) )) return STATUS_ACCESS_VIOLATION;
5748 ret = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, &type, NULL );
5749 if (ret == STATUS_ACCESS_DENIED)
5750 ret = server_get_unix_fd( handle, FILE_APPEND_DATA, &fd, &needs_close, &type, NULL );
5752 if (!ret && (type == FD_TYPE_FILE || type == FD_TYPE_DIR || type == FD_TYPE_CHAR))
5754 if (fsync(fd)) ret = errno_to_status( errno );
5755 io->u.Status = ret;
5756 io->Information = 0;
5758 else if (!ret && type == FD_TYPE_SERIAL)
5760 ret = serial_FlushBuffersFile( fd );
5762 else if (ret != STATUS_ACCESS_DENIED)
5764 struct async_irp *async;
5766 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5767 return STATUS_NO_MEMORY;
5768 async->buffer = NULL;
5769 async->size = 0;
5771 SERVER_START_REQ( flush )
5773 req->async = server_async( handle, &async->io, NULL, NULL, NULL, io );
5774 ret = wine_server_call( req );
5775 wait_handle = wine_server_ptr_handle( reply->event );
5776 if (wait_handle && ret != STATUS_PENDING)
5778 io->u.Status = ret;
5779 io->Information = 0;
5782 SERVER_END_REQ;
5784 if (ret != STATUS_PENDING) free( async );
5786 if (wait_handle) ret = wait_async( wait_handle, FALSE, io );
5789 if (needs_close) close( fd );
5790 return ret;
5794 /**************************************************************************
5795 * NtCancelIoFile (NTDLL.@)
5797 NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status )
5799 TRACE( "%p %p\n", handle, io_status );
5801 SERVER_START_REQ( cancel_async )
5803 req->handle = wine_server_obj_handle( handle );
5804 req->only_thread = TRUE;
5805 io_status->u.Status = wine_server_call( req );
5807 SERVER_END_REQ;
5808 return io_status->u.Status;
5812 /**************************************************************************
5813 * NtCancelIoFileEx (NTDLL.@)
5815 NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status )
5817 TRACE( "%p %p %p\n", handle, io, io_status );
5819 SERVER_START_REQ( cancel_async )
5821 req->handle = wine_server_obj_handle( handle );
5822 req->iosb = wine_server_client_ptr( io );
5823 io_status->u.Status = wine_server_call( req );
5825 SERVER_END_REQ;
5826 return io_status->u.Status;
5830 /******************************************************************
5831 * NtLockFile (NTDLL.@)
5833 NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void* apc_user,
5834 IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
5835 LARGE_INTEGER *count, ULONG *key, BOOLEAN dont_wait, BOOLEAN exclusive )
5837 static int warn;
5838 NTSTATUS ret;
5839 HANDLE handle;
5840 BOOLEAN async;
5842 if (apc || io_status || key)
5844 FIXME("Unimplemented yet parameter\n");
5845 return STATUS_NOT_IMPLEMENTED;
5847 if (apc_user && !warn++) FIXME("I/O completion on lock not implemented yet\n");
5849 for (;;)
5851 SERVER_START_REQ( lock_file )
5853 req->handle = wine_server_obj_handle( file );
5854 req->offset = offset->QuadPart;
5855 req->count = count->QuadPart;
5856 req->shared = !exclusive;
5857 req->wait = !dont_wait;
5858 ret = wine_server_call( req );
5859 handle = wine_server_ptr_handle( reply->handle );
5860 async = reply->overlapped;
5862 SERVER_END_REQ;
5863 if (ret != STATUS_PENDING)
5865 if (!ret && event) NtSetEvent( event, NULL );
5866 return ret;
5868 if (async)
5870 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
5871 if (handle) NtClose( handle );
5872 return STATUS_PENDING;
5874 if (handle)
5876 NtWaitForSingleObject( handle, FALSE, NULL );
5877 NtClose( handle );
5879 else /* Unix lock conflict, sleep a bit and retry */
5881 LARGE_INTEGER time;
5882 time.QuadPart = -100 * (ULONGLONG)10000;
5883 NtDelayExecution( FALSE, &time );
5889 /******************************************************************
5890 * NtUnlockFile (NTDLL.@)
5892 NTSTATUS WINAPI NtUnlockFile( HANDLE handle, IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
5893 LARGE_INTEGER *count, ULONG *key )
5895 NTSTATUS status;
5897 TRACE( "%p %x%08x %x%08x\n",
5898 handle, offset->u.HighPart, offset->u.LowPart, count->u.HighPart, count->u.LowPart );
5900 if (io_status || key)
5902 FIXME("Unimplemented yet parameter\n");
5903 return STATUS_NOT_IMPLEMENTED;
5906 SERVER_START_REQ( unlock_file )
5908 req->handle = wine_server_obj_handle( handle );
5909 req->offset = offset->QuadPart;
5910 req->count = count->QuadPart;
5911 status = wine_server_call( req );
5913 SERVER_END_REQ;
5914 return status;
5918 static NTSTATUS read_changes_apc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
5920 struct async_fileio_read_changes *fileio = user;
5921 int size = 0;
5923 if (status == STATUS_ALERTED)
5925 SERVER_START_REQ( read_change )
5927 req->handle = wine_server_obj_handle( fileio->io.handle );
5928 wine_server_set_reply( req, fileio->data, fileio->data_size );
5929 status = wine_server_call( req );
5930 size = wine_server_reply_size( reply );
5932 SERVER_END_REQ;
5934 if (status == STATUS_SUCCESS && fileio->buffer)
5936 FILE_NOTIFY_INFORMATION *pfni = fileio->buffer;
5937 int i, left = fileio->buffer_size;
5938 DWORD *last_entry_offset = NULL;
5939 struct filesystem_event *event = (struct filesystem_event*)fileio->data;
5941 while (size && left >= sizeof(*pfni))
5943 DWORD len = (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR);
5945 /* convert to an NT style path */
5946 for (i = 0; i < event->len; i++)
5947 if (event->name[i] == '/') event->name[i] = '\\';
5949 pfni->Action = event->action;
5950 pfni->FileNameLength = ntdll_umbstowcs( event->name, event->len, pfni->FileName, len );
5951 last_entry_offset = &pfni->NextEntryOffset;
5953 if (pfni->FileNameLength == len) break;
5955 i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
5956 pfni->FileNameLength *= sizeof(WCHAR);
5957 pfni->NextEntryOffset = i;
5958 pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
5959 left -= i;
5961 i = (offsetof(struct filesystem_event, name[event->len])
5962 + sizeof(int)-1) / sizeof(int) * sizeof(int);
5963 event = (struct filesystem_event*)((char*)event + i);
5964 size -= i;
5967 if (size)
5969 status = STATUS_NOTIFY_ENUM_DIR;
5970 size = 0;
5972 else
5974 if (last_entry_offset) *last_entry_offset = 0;
5975 size = fileio->buffer_size - left;
5978 else
5980 status = STATUS_NOTIFY_ENUM_DIR;
5981 size = 0;
5985 if (status != STATUS_PENDING)
5987 iosb->u.Status = status;
5988 iosb->Information = size;
5989 release_fileio( &fileio->io );
5991 return status;
5994 #define FILE_NOTIFY_ALL ( \
5995 FILE_NOTIFY_CHANGE_FILE_NAME | \
5996 FILE_NOTIFY_CHANGE_DIR_NAME | \
5997 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
5998 FILE_NOTIFY_CHANGE_SIZE | \
5999 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6000 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6001 FILE_NOTIFY_CHANGE_CREATION | \
6002 FILE_NOTIFY_CHANGE_SECURITY )
6004 /******************************************************************************
6005 * NtNotifyChangeDirectoryFile (NTDLL.@)
6007 NTSTATUS WINAPI NtNotifyChangeDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
6008 void *apc_context, IO_STATUS_BLOCK *iosb, void *buffer,
6009 ULONG buffer_size, ULONG filter, BOOLEAN subtree )
6011 struct async_fileio_read_changes *fileio;
6012 NTSTATUS status;
6013 ULONG size = max( 4096, buffer_size );
6015 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6016 handle, event, apc, apc_context, iosb, buffer, buffer_size, filter, subtree );
6018 if (!iosb) return STATUS_ACCESS_VIOLATION;
6019 if (filter == 0 || (filter & ~FILE_NOTIFY_ALL)) return STATUS_INVALID_PARAMETER;
6021 fileio = (struct async_fileio_read_changes *)alloc_fileio(
6022 offsetof(struct async_fileio_read_changes, data[size]), read_changes_apc, handle );
6023 if (!fileio) return STATUS_NO_MEMORY;
6025 fileio->buffer = buffer;
6026 fileio->buffer_size = buffer_size;
6027 fileio->data_size = size;
6029 SERVER_START_REQ( read_directory_changes )
6031 req->filter = filter;
6032 req->want_data = (buffer != NULL);
6033 req->subtree = subtree;
6034 req->async = server_async( handle, &fileio->io, event, apc, apc_context, iosb );
6035 status = wine_server_call( req );
6037 SERVER_END_REQ;
6039 if (status != STATUS_PENDING) free( fileio );
6040 return status;
6044 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6045 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6046 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION *info, const char *fstypename,
6047 unsigned int flags )
6049 if (!strcmp("cd9660", fstypename) || !strcmp("udf", fstypename))
6051 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6052 /* Don't assume read-only, let the mount options set it below */
6053 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6055 else if (!strcmp("nfs", fstypename) || !strcmp("nwfs", fstypename) ||
6056 !strcmp("smbfs", fstypename) || !strcmp("afpfs", fstypename))
6058 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6059 info->Characteristics |= FILE_REMOTE_DEVICE;
6061 else if (!strcmp("procfs", fstypename))
6062 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6063 else
6064 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6066 if (flags & MNT_RDONLY)
6067 info->Characteristics |= FILE_READ_ONLY_DEVICE;
6069 if (!(flags & MNT_LOCAL))
6071 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6072 info->Characteristics |= FILE_REMOTE_DEVICE;
6075 #endif
6077 static inline BOOL is_device_placeholder( int fd )
6079 static const char wine_placeholder[] = "Wine device placeholder";
6080 char buffer[sizeof(wine_placeholder)-1];
6082 if (pread( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1)
6083 return FALSE;
6084 return !memcmp( buffer, wine_placeholder, sizeof(wine_placeholder) - 1 );
6087 static NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info )
6089 struct stat st;
6091 info->Characteristics = 0;
6092 if (fstat( fd, &st ) < 0) return errno_to_status( errno );
6093 if (S_ISCHR( st.st_mode ))
6095 info->DeviceType = FILE_DEVICE_UNKNOWN;
6096 #ifdef linux
6097 switch(major(st.st_rdev))
6099 case MEM_MAJOR:
6100 info->DeviceType = FILE_DEVICE_NULL;
6101 break;
6102 case TTY_MAJOR:
6103 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6104 break;
6105 case LP_MAJOR:
6106 info->DeviceType = FILE_DEVICE_PARALLEL_PORT;
6107 break;
6108 case SCSI_TAPE_MAJOR:
6109 info->DeviceType = FILE_DEVICE_TAPE;
6110 break;
6112 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6114 int d_type;
6115 if (ioctl(fd, FIODTYPE, &d_type) == 0)
6117 switch(d_type)
6119 case D_TAPE:
6120 info->DeviceType = FILE_DEVICE_TAPE;
6121 break;
6122 case D_DISK:
6123 info->DeviceType = FILE_DEVICE_DISK;
6124 break;
6125 case D_TTY:
6126 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6127 break;
6128 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6129 case D_MEM:
6130 info->DeviceType = FILE_DEVICE_NULL;
6131 break;
6132 #endif
6134 /* no special d_type for parallel ports */
6137 #endif
6139 else if (S_ISBLK( st.st_mode ))
6141 info->DeviceType = FILE_DEVICE_DISK;
6143 else if (S_ISFIFO( st.st_mode ) || S_ISSOCK( st.st_mode ))
6145 info->DeviceType = FILE_DEVICE_NAMED_PIPE;
6147 else if (is_device_placeholder( fd ))
6149 info->DeviceType = FILE_DEVICE_DISK;
6151 else /* regular file or directory */
6153 #if defined(linux) && defined(HAVE_FSTATFS)
6154 struct statfs stfs;
6156 /* check for floppy disk */
6157 if (major(st.st_dev) == FLOPPY_MAJOR)
6158 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6160 if (fstatfs( fd, &stfs ) < 0) stfs.f_type = 0;
6161 switch (stfs.f_type)
6163 case 0x9660: /* iso9660 */
6164 case 0x9fa1: /* supermount */
6165 case 0x15013346: /* udf */
6166 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6167 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6168 break;
6169 case 0x6969: /* nfs */
6170 case 0xff534d42: /* cifs */
6171 case 0xfe534d42: /* smb2 */
6172 case 0x517b: /* smbfs */
6173 case 0x564c: /* ncpfs */
6174 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6175 info->Characteristics |= FILE_REMOTE_DEVICE;
6176 break;
6177 case 0x1373: /* devfs */
6178 case 0x9fa0: /* procfs */
6179 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6180 break;
6181 case 0x01021994: /* tmpfs */
6182 case 0x28cd3d45: /* cramfs */
6183 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6184 * filesystems are rare on Windows, and some programs refuse to
6185 * recognize them as valid. */
6186 default:
6187 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6188 break;
6190 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6191 struct statfs stfs;
6193 if (fstatfs( fd, &stfs ) < 0)
6194 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6195 else
6196 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flags );
6197 #elif defined(__NetBSD__)
6198 struct statvfs stfs;
6200 if (fstatvfs( fd, &stfs) < 0)
6201 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6202 else
6203 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flag );
6204 #elif defined(sun)
6205 /* Use dkio to work out device types */
6207 # include <sys/dkio.h>
6208 # include <sys/vtoc.h>
6209 struct dk_cinfo dkinf;
6210 int retval = ioctl(fd, DKIOCINFO, &dkinf);
6211 if(retval==-1){
6212 WARN("Unable to get disk device type information - assuming a disk like device\n");
6213 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6215 switch (dkinf.dki_ctype)
6217 case DKC_CDROM:
6218 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6219 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6220 break;
6221 case DKC_NCRFLOPPY:
6222 case DKC_SMSFLOPPY:
6223 case DKC_INTEL82072:
6224 case DKC_INTEL82077:
6225 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6226 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6227 break;
6228 case DKC_MD:
6229 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6230 * filesystems are rare on Windows, and some programs refuse to
6231 * recognize them as valid. */
6232 default:
6233 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6236 #else
6237 static int warned;
6238 if (!warned++) FIXME( "device info not properly supported on this platform\n" );
6239 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6240 #endif
6241 info->Characteristics |= FILE_DEVICE_IS_MOUNTED;
6243 return STATUS_SUCCESS;
6247 /******************************************************************************
6248 * NtQueryVolumeInformationFile (NTDLL.@)
6250 NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
6251 void *buffer, ULONG length,
6252 FS_INFORMATION_CLASS info_class )
6254 int fd, needs_close;
6255 struct stat st;
6257 io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
6258 if (io->u.Status == STATUS_BAD_DEVICE_TYPE)
6260 SERVER_START_REQ( get_volume_info )
6262 req->handle = wine_server_obj_handle( handle );
6263 req->info_class = info_class;
6264 wine_server_set_reply( req, buffer, length );
6265 io->u.Status = wine_server_call( req );
6266 if (!io->u.Status) io->Information = wine_server_reply_size( reply );
6268 SERVER_END_REQ;
6269 return io->u.Status;
6271 else if (io->u.Status) return io->u.Status;
6273 io->u.Status = STATUS_NOT_IMPLEMENTED;
6274 io->Information = 0;
6276 switch( info_class )
6278 case FileFsLabelInformation:
6279 FIXME( "%p: label info not supported\n", handle );
6280 break;
6282 case FileFsSizeInformation:
6283 if (length < sizeof(FILE_FS_SIZE_INFORMATION))
6284 io->u.Status = STATUS_BUFFER_TOO_SMALL;
6285 else
6287 FILE_FS_SIZE_INFORMATION *info = buffer;
6289 if (fstat( fd, &st ) < 0)
6291 io->u.Status = errno_to_status( errno );
6292 break;
6294 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
6296 io->u.Status = STATUS_INVALID_DEVICE_REQUEST;
6298 else
6300 ULONGLONG bsize;
6301 /* Linux's fstatvfs is buggy */
6302 #if !defined(linux) || !defined(HAVE_FSTATFS)
6303 struct statvfs stfs;
6305 if (fstatvfs( fd, &stfs ) < 0)
6307 io->u.Status = errno_to_status( errno );
6308 break;
6310 bsize = stfs.f_frsize;
6311 #else
6312 struct statfs stfs;
6313 if (fstatfs( fd, &stfs ) < 0)
6315 io->u.Status = errno_to_status( errno );
6316 break;
6318 bsize = stfs.f_bsize;
6319 #endif
6320 if (bsize == 2048) /* assume CD-ROM */
6322 info->BytesPerSector = 2048;
6323 info->SectorsPerAllocationUnit = 1;
6325 else
6327 info->BytesPerSector = 512;
6328 info->SectorsPerAllocationUnit = 8;
6330 info->TotalAllocationUnits.QuadPart = bsize * stfs.f_blocks / (info->BytesPerSector * info->SectorsPerAllocationUnit);
6331 info->AvailableAllocationUnits.QuadPart = bsize * stfs.f_bavail / (info->BytesPerSector * info->SectorsPerAllocationUnit);
6332 io->Information = sizeof(*info);
6333 io->u.Status = STATUS_SUCCESS;
6336 break;
6338 case FileFsDeviceInformation:
6339 if (length < sizeof(FILE_FS_DEVICE_INFORMATION))
6340 io->u.Status = STATUS_BUFFER_TOO_SMALL;
6341 else
6343 FILE_FS_DEVICE_INFORMATION *info = buffer;
6345 if ((io->u.Status = get_device_info( fd, info )) == STATUS_SUCCESS)
6346 io->Information = sizeof(*info);
6348 break;
6350 case FileFsAttributeInformation:
6352 static const WCHAR fatW[] = {'F','A','T'};
6353 static const WCHAR fat32W[] = {'F','A','T','3','2'};
6354 static const WCHAR ntfsW[] = {'N','T','F','S'};
6355 static const WCHAR cdfsW[] = {'C','D','F','S'};
6356 static const WCHAR udfW[] = {'U','D','F'};
6358 FILE_FS_ATTRIBUTE_INFORMATION *info = buffer;
6359 struct mountmgr_unix_drive drive;
6360 enum mountmgr_fs_type fs_type = MOUNTMGR_FS_TYPE_NTFS;
6362 if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
6364 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
6365 break;
6368 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) )) fs_type = drive.fs_type;
6369 else
6371 struct statfs stfs;
6373 if (!fstatfs( fd, &stfs ))
6375 #if defined(linux) && defined(HAVE_FSTATFS)
6376 switch (stfs.f_type)
6378 case 0x9660:
6379 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6380 break;
6381 case 0x15013346:
6382 fs_type = MOUNTMGR_FS_TYPE_UDF;
6383 break;
6384 case 0x4d44:
6385 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6386 break;
6388 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6389 if (!strcmp( stfs.f_fstypename, "cd9660" ))
6390 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6391 else if (!strcmp( stfs.f_fstypename, "udf" ))
6392 fs_type = MOUNTMGR_FS_TYPE_UDF;
6393 else if (!strcmp( stfs.f_fstypename, "msdos" )) /* FreeBSD < 5, Apple */
6394 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6395 else if (!strcmp( stfs.f_fstypename, "msdosfs" )) /* FreeBSD >= 5 */
6396 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6397 #endif
6401 switch (fs_type)
6403 case MOUNTMGR_FS_TYPE_ISO9660:
6404 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
6405 info->MaximumComponentNameLength = 221;
6406 info->FileSystemNameLength = min( sizeof(cdfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6407 memcpy(info->FileSystemName, cdfsW, info->FileSystemNameLength);
6408 break;
6409 case MOUNTMGR_FS_TYPE_UDF:
6410 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
6411 info->MaximumComponentNameLength = 255;
6412 info->FileSystemNameLength = min( sizeof(udfW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6413 memcpy(info->FileSystemName, udfW, info->FileSystemNameLength);
6414 break;
6415 case MOUNTMGR_FS_TYPE_FAT:
6416 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6417 info->MaximumComponentNameLength = 255;
6418 info->FileSystemNameLength = min( sizeof(fatW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6419 memcpy(info->FileSystemName, fatW, info->FileSystemNameLength);
6420 break;
6421 case MOUNTMGR_FS_TYPE_FAT32:
6422 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6423 info->MaximumComponentNameLength = 255;
6424 info->FileSystemNameLength = min( sizeof(fat32W), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6425 memcpy(info->FileSystemName, fat32W, info->FileSystemNameLength);
6426 break;
6427 default:
6428 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
6429 info->MaximumComponentNameLength = 255;
6430 info->FileSystemNameLength = min( sizeof(ntfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6431 memcpy(info->FileSystemName, ntfsW, info->FileSystemNameLength);
6432 break;
6435 io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
6436 io->u.Status = STATUS_SUCCESS;
6437 break;
6440 case FileFsVolumeInformation:
6442 FILE_FS_VOLUME_INFORMATION *info = buffer;
6443 ULONGLONG data[64];
6444 struct mountmgr_unix_drive *drive = (struct mountmgr_unix_drive *)data;
6445 const WCHAR *label;
6447 if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
6449 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
6450 break;
6453 if (get_mountmgr_fs_info( handle, fd, drive, sizeof(data) ))
6455 io->u.Status = STATUS_NOT_IMPLEMENTED;
6456 break;
6459 label = (WCHAR *)((char *)drive + drive->label_offset);
6460 info->VolumeCreationTime.QuadPart = 0; /* FIXME */
6461 info->VolumeSerialNumber = drive->serial;
6462 info->VolumeLabelLength = min( wcslen( label ) * sizeof(WCHAR),
6463 length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
6464 info->SupportsObjects = (drive->fs_type == MOUNTMGR_FS_TYPE_NTFS);
6465 memcpy( info->VolumeLabel, label, info->VolumeLabelLength );
6466 io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
6467 io->u.Status = STATUS_SUCCESS;
6468 break;
6471 case FileFsControlInformation:
6472 FIXME( "%p: control info not supported\n", handle );
6473 break;
6475 case FileFsFullSizeInformation:
6476 FIXME( "%p: full size info not supported\n", handle );
6477 break;
6479 case FileFsObjectIdInformation:
6480 FIXME( "%p: object id info not supported\n", handle );
6481 break;
6483 case FileFsMaximumInformation:
6484 FIXME( "%p: maximum info not supported\n", handle );
6485 break;
6487 default:
6488 io->u.Status = STATUS_INVALID_PARAMETER;
6489 break;
6491 if (needs_close) close( fd );
6492 return io->u.Status;
6496 /******************************************************************************
6497 * NtSetVolumeInformationFile (NTDLL.@)
6499 NTSTATUS WINAPI NtSetVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, void *info,
6500 ULONG length, FS_INFORMATION_CLASS class )
6502 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle, io, info, length, class );
6503 return STATUS_SUCCESS;
6507 /******************************************************************
6508 * NtQueryEaFile (NTDLL.@)
6510 NTSTATUS WINAPI NtQueryEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
6511 BOOLEAN single_entry, void *list, ULONG list_len,
6512 ULONG *index, BOOLEAN restart )
6514 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) stub\n",
6515 handle, io, buffer, length, single_entry, list, list_len, index, restart );
6516 return STATUS_ACCESS_DENIED;
6520 /******************************************************************
6521 * NtSetEaFile (NTDLL.@)
6523 NTSTATUS WINAPI NtSetEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length )
6525 FIXME( "(%p,%p,%p,%d) stub\n", handle, io, buffer, length );
6526 return STATUS_ACCESS_DENIED;
6530 /* convert type information from server format; helper for NtQueryObject */
6531 static void *put_object_type_info( OBJECT_TYPE_INFORMATION *p, struct object_type_info *info )
6533 const ULONG align = sizeof(DWORD_PTR) - 1;
6535 memset( p, 0, sizeof(*p) );
6536 p->TypeName.Buffer = (WCHAR *)(p + 1);
6537 p->TypeName.Length = info->name_len;
6538 p->TypeName.MaximumLength = info->name_len + sizeof(WCHAR);
6539 p->TotalNumberOfObjects = info->obj_count;
6540 p->TotalNumberOfHandles = info->handle_count;
6541 p->HighWaterNumberOfObjects = info->obj_max;
6542 p->HighWaterNumberOfHandles = info->handle_max;
6543 p->TypeIndex = info->index + 2;
6544 p->GenericMapping.GenericRead = info->mapping.read;
6545 p->GenericMapping.GenericWrite = info->mapping.write;
6546 p->GenericMapping.GenericExecute = info->mapping.exec;
6547 p->GenericMapping.GenericAll = info->mapping.all;
6548 p->ValidAccessMask = info->valid_access;
6549 memcpy( p->TypeName.Buffer, info + 1, info->name_len );
6550 p->TypeName.Buffer[info->name_len / sizeof(WCHAR)] = 0;
6551 return (char *)(p + 1) + ((p->TypeName.MaximumLength + align) & ~align);
6554 /**************************************************************************
6555 * NtQueryObject (NTDLL.@)
6557 NTSTATUS WINAPI NtQueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
6558 void *ptr, ULONG len, ULONG *used_len )
6560 NTSTATUS status;
6562 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle, info_class, ptr, len, used_len);
6564 if (used_len) *used_len = 0;
6566 switch (info_class)
6568 case ObjectBasicInformation:
6570 OBJECT_BASIC_INFORMATION *p = ptr;
6572 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6574 SERVER_START_REQ( get_object_info )
6576 req->handle = wine_server_obj_handle( handle );
6577 status = wine_server_call( req );
6578 if (status == STATUS_SUCCESS)
6580 memset( p, 0, sizeof(*p) );
6581 p->GrantedAccess = reply->access;
6582 p->PointerCount = reply->ref_count;
6583 p->HandleCount = reply->handle_count;
6584 if (used_len) *used_len = sizeof(*p);
6587 SERVER_END_REQ;
6588 break;
6591 case ObjectNameInformation:
6593 OBJECT_NAME_INFORMATION *p = ptr;
6594 char *unix_name;
6595 WCHAR *nt_name;
6597 /* first try as a file object */
6599 if (!(status = server_get_unix_name( handle, &unix_name )))
6601 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
6603 ULONG size = (wcslen(nt_name) + 1) * sizeof(WCHAR);
6604 if (len < sizeof(*p)) status = STATUS_INFO_LENGTH_MISMATCH;
6605 else if (len < sizeof(*p) + size) status = STATUS_BUFFER_OVERFLOW;
6606 else
6608 p->Name.Buffer = (WCHAR *)(p + 1);
6609 p->Name.Length = size - sizeof(WCHAR);
6610 p->Name.MaximumLength = size;
6611 wcscpy( p->Name.Buffer, nt_name );
6613 if (used_len) *used_len = sizeof(*p) + size;
6614 free( nt_name );
6616 free( unix_name );
6617 break;
6619 else if (status != STATUS_OBJECT_TYPE_MISMATCH) break;
6621 /* not a file, treat as a generic object */
6623 SERVER_START_REQ( get_object_info )
6625 req->handle = wine_server_obj_handle( handle );
6626 if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
6627 status = wine_server_call( req );
6628 if (status == STATUS_SUCCESS)
6630 if (!reply->total) /* no name */
6632 if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
6633 else memset( p, 0, sizeof(*p) );
6634 if (used_len) *used_len = sizeof(*p);
6636 else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
6638 if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
6639 status = STATUS_INFO_LENGTH_MISMATCH;
6641 else
6643 ULONG res = wine_server_reply_size( reply );
6644 p->Name.Buffer = (WCHAR *)(p + 1);
6645 p->Name.Length = res;
6646 p->Name.MaximumLength = res + sizeof(WCHAR);
6647 p->Name.Buffer[res / sizeof(WCHAR)] = 0;
6648 if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
6652 SERVER_END_REQ;
6653 break;
6656 case ObjectTypeInformation:
6658 OBJECT_TYPE_INFORMATION *p = ptr;
6659 char buffer[sizeof(struct object_type_info) + 64];
6660 struct object_type_info *info = (struct object_type_info *)buffer;
6662 SERVER_START_REQ( get_object_type )
6664 req->handle = wine_server_obj_handle( handle );
6665 wine_server_set_reply( req, buffer, sizeof(buffer) );
6666 status = wine_server_call( req );
6668 SERVER_END_REQ;
6669 if (status) break;
6670 if (sizeof(*p) + info->name_len + sizeof(WCHAR) <= len)
6672 put_object_type_info( p, info );
6673 if (used_len) *used_len = sizeof(*p) + p->TypeName.MaximumLength;
6675 else
6677 if (used_len) *used_len = sizeof(*p) + info->name_len + sizeof(WCHAR);
6678 status = STATUS_INFO_LENGTH_MISMATCH;
6680 break;
6683 case ObjectTypesInformation:
6685 OBJECT_TYPES_INFORMATION *types = ptr;
6686 OBJECT_TYPE_INFORMATION *p;
6687 struct object_type_info *buffer;
6688 /* assume at most 32 types, with an average 16-char name */
6689 ULONG size = 32 * (sizeof(struct object_type_info) + 16 * sizeof(WCHAR));
6690 ULONG i, count, pos, total, align = sizeof(DWORD_PTR) - 1;
6692 buffer = malloc( size );
6693 SERVER_START_REQ( get_object_types )
6695 wine_server_set_reply( req, buffer, size );
6696 status = wine_server_call( req );
6697 count = reply->count;
6699 SERVER_END_REQ;
6700 if (!status)
6702 if (len >= sizeof(*types)) types->NumberOfTypes = count;
6703 total = (sizeof(*types) + align) & ~align;
6704 p = (OBJECT_TYPE_INFORMATION *)((char *)ptr + total);
6705 for (i = pos = 0; i < count; i++)
6707 struct object_type_info *info = (struct object_type_info *)((char *)buffer + pos);
6708 pos += sizeof(*info) + ((info->name_len + 3) & ~3);
6709 total += sizeof(*p) + ((info->name_len + sizeof(WCHAR) + align) & ~align);
6710 if (total <= len) p = put_object_type_info( p, info );
6712 if (used_len) *used_len = total;
6713 if (total > len) status = STATUS_INFO_LENGTH_MISMATCH;
6715 else if (status == STATUS_BUFFER_OVERFLOW) FIXME( "size %u too small\n", size );
6717 free( buffer );
6718 break;
6721 case ObjectDataInformation:
6723 OBJECT_DATA_INFORMATION* p = ptr;
6725 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6727 SERVER_START_REQ( set_handle_info )
6729 req->handle = wine_server_obj_handle( handle );
6730 req->flags = 0;
6731 req->mask = 0;
6732 status = wine_server_call( req );
6733 if (status == STATUS_SUCCESS)
6735 p->InheritHandle = (reply->old_flags & HANDLE_FLAG_INHERIT) != 0;
6736 p->ProtectFromClose = (reply->old_flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0;
6737 if (used_len) *used_len = sizeof(*p);
6740 SERVER_END_REQ;
6741 break;
6744 default:
6745 FIXME("Unsupported information class %u\n", info_class);
6746 status = STATUS_NOT_IMPLEMENTED;
6747 break;
6749 return status;
6753 /**************************************************************************
6754 * NtSetInformationObject (NTDLL.@)
6756 NTSTATUS WINAPI NtSetInformationObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
6757 void *ptr, ULONG len )
6759 NTSTATUS status;
6761 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle, info_class, ptr, len);
6763 switch (info_class)
6765 case ObjectDataInformation:
6767 OBJECT_DATA_INFORMATION* p = ptr;
6769 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6771 SERVER_START_REQ( set_handle_info )
6773 req->handle = wine_server_obj_handle( handle );
6774 req->mask = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
6775 if (p->InheritHandle) req->flags |= HANDLE_FLAG_INHERIT;
6776 if (p->ProtectFromClose) req->flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
6777 status = wine_server_call( req );
6779 SERVER_END_REQ;
6780 break;
6783 default:
6784 FIXME("Unsupported information class %u\n", info_class);
6785 status = STATUS_NOT_IMPLEMENTED;
6786 break;
6788 return status;