mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / ntdll / unix / file.c
blob0ba0ee7b586ee22e93cac8cd1cfdcdcb1dc66754
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 memset( drive, 0, sizeof(*drive) );
1967 if (letter == -1)
1969 struct stat st;
1971 fstat( fd, &st );
1972 drive->unix_dev = st.st_rdev ? st.st_rdev : st.st_dev;
1974 else
1975 drive->letter = 'a' + letter;
1977 init_unicode_string( &string, MOUNTMGR_DEVICE_NAME );
1978 InitializeObjectAttributes( &attr, &string, 0, NULL, NULL );
1979 status = NtOpenFile( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr, &io,
1980 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT );
1981 if (status) return status;
1983 status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE,
1984 drive, sizeof(*drive), drive, size );
1985 NtClose( mountmgr );
1986 if (status == STATUS_BUFFER_OVERFLOW) status = STATUS_SUCCESS;
1987 else if (status) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status);
1988 return status;
1992 /***********************************************************************
1993 * get_dir_data_entry
1995 * Return a directory entry from the cached data.
1997 static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, IO_STATUS_BLOCK *io,
1998 ULONG max_length, FILE_INFORMATION_CLASS class,
1999 union file_directory_info **last_info )
2001 const struct dir_data_names *names = &dir_data->names[dir_data->pos];
2002 union file_directory_info *info;
2003 struct stat st;
2004 ULONG name_len, start, dir_size, attributes;
2006 if (get_file_info( names->unix_name, &st, &attributes ) == -1)
2008 TRACE( "file no longer exists %s\n", names->unix_name );
2009 return STATUS_SUCCESS;
2011 if (is_ignored_file( &st ))
2013 TRACE( "ignoring file %s\n", names->unix_name );
2014 return STATUS_SUCCESS;
2016 start = dir_info_align( io->Information );
2017 dir_size = dir_info_size( class, 0 );
2018 if (start + dir_size > max_length) return STATUS_MORE_ENTRIES;
2020 max_length -= start + dir_size;
2021 name_len = wcslen( names->long_name ) * sizeof(WCHAR);
2022 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2023 if (*last_info && name_len > max_length) return STATUS_MORE_ENTRIES;
2025 info = (union file_directory_info *)((char *)info_ptr + start);
2026 info->dir.NextEntryOffset = 0;
2027 info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
2029 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2030 if (class != FileNamesInformation)
2032 if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */
2034 if (!show_dot_files && names->long_name[0] == '.' && names->long_name[1] &&
2035 (names->long_name[1] != '.' || names->long_name[2]))
2036 attributes |= FILE_ATTRIBUTE_HIDDEN;
2038 fill_file_info( &st, attributes, info, class );
2041 switch (class)
2043 case FileDirectoryInformation:
2044 info->dir.FileNameLength = name_len;
2045 break;
2047 case FileFullDirectoryInformation:
2048 info->full.EaSize = 0; /* FIXME */
2049 info->full.FileNameLength = name_len;
2050 break;
2052 case FileIdFullDirectoryInformation:
2053 info->id_full.EaSize = 0; /* FIXME */
2054 info->id_full.FileNameLength = name_len;
2055 break;
2057 case FileBothDirectoryInformation:
2058 info->both.EaSize = 0; /* FIXME */
2059 info->both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2060 memcpy( info->both.ShortName, names->short_name, info->both.ShortNameLength );
2061 info->both.FileNameLength = name_len;
2062 break;
2064 case FileIdBothDirectoryInformation:
2065 info->id_both.EaSize = 0; /* FIXME */
2066 info->id_both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2067 memcpy( info->id_both.ShortName, names->short_name, info->id_both.ShortNameLength );
2068 info->id_both.FileNameLength = name_len;
2069 break;
2071 case FileIdGlobalTxDirectoryInformation:
2072 info->id_tx.TxInfoFlags = 0;
2073 info->id_tx.FileNameLength = name_len;
2074 break;
2076 case FileNamesInformation:
2077 info->names.FileNameLength = name_len;
2078 break;
2080 default:
2081 assert(0);
2082 return 0;
2085 memcpy( (char *)info + dir_size, names->long_name, min( name_len, max_length ) );
2086 io->Information = start + dir_size + min( name_len, max_length );
2087 if (*last_info) (*last_info)->next = (char *)info - (char *)*last_info;
2088 *last_info = info;
2089 return name_len > max_length ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
2092 #ifdef VFAT_IOCTL_READDIR_BOTH
2094 /***********************************************************************
2095 * read_directory_vfat
2097 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2099 static NTSTATUS read_directory_data_vfat( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2101 char *short_name, *long_name;
2102 KERNEL_DIRENT de[2];
2103 NTSTATUS status = STATUS_NO_MEMORY;
2104 off_t old_pos = lseek( fd, 0, SEEK_CUR );
2106 lseek( fd, 0, SEEK_SET );
2108 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
2110 if (errno != ENOENT)
2112 status = STATUS_NOT_SUPPORTED;
2113 goto done;
2115 de[0].d_reclen = 0;
2118 if (!append_entry( data, ".", NULL, mask )) goto done;
2119 if (!append_entry( data, "..", NULL, mask )) goto done;
2121 while (de[0].d_reclen)
2123 if (strcmp( de[0].d_name, "." ) && strcmp( de[0].d_name, ".." ))
2125 if (de[1].d_name[0])
2127 short_name = de[0].d_name;
2128 long_name = de[1].d_name;
2130 else
2132 long_name = de[0].d_name;
2133 short_name = NULL;
2135 if (!append_entry( data, long_name, short_name, mask )) goto done;
2137 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
2139 status = STATUS_SUCCESS;
2140 done:
2141 lseek( fd, old_pos, SEEK_SET );
2142 return status;
2144 #endif /* VFAT_IOCTL_READDIR_BOTH */
2147 #ifdef HAVE_GETATTRLIST
2148 /***********************************************************************
2149 * read_directory_getattrlist
2151 * Read a single file from a directory by determining whether the file
2152 * identified by mask exists using getattrlist.
2154 static NTSTATUS read_directory_data_getattrlist( struct dir_data *data, const char *unix_name )
2156 struct attrlist attrlist;
2157 #include "pshpack4.h"
2158 struct
2160 u_int32_t length;
2161 struct attrreference name_reference;
2162 fsobj_type_t type;
2163 char name[NAME_MAX * 3 + 1];
2164 } buffer;
2165 #include "poppack.h"
2167 memset( &attrlist, 0, sizeof(attrlist) );
2168 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
2169 attrlist.commonattr = ATTR_CMN_NAME | ATTR_CMN_OBJTYPE;
2170 if (getattrlist( unix_name, &attrlist, &buffer, sizeof(buffer), FSOPT_NOFOLLOW ) == -1)
2171 return STATUS_NO_SUCH_FILE;
2172 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2173 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2174 if (buffer.type == VLNK)
2176 u_int32_t dummy;
2177 attrlist.commonattr = 0;
2178 if (getattrlist( unix_name, &attrlist, &dummy, sizeof(dummy), 0 ) == -1)
2179 return STATUS_NO_SUCH_FILE;
2182 TRACE( "found %s\n", buffer.name );
2184 if (!append_entry( data, buffer.name, NULL, NULL )) return STATUS_NO_MEMORY;
2186 return STATUS_SUCCESS;
2188 #endif /* HAVE_GETATTRLIST */
2191 /***********************************************************************
2192 * read_directory_stat
2194 * Read a single file from a directory by determining whether the file
2195 * identified by mask exists using stat.
2197 static NTSTATUS read_directory_data_stat( struct dir_data *data, const char *unix_name )
2199 struct stat st;
2201 /* if the file system is not case sensitive we can't find the actual name through stat() */
2202 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE;
2203 if (stat( unix_name, &st ) == -1) return STATUS_NO_SUCH_FILE;
2205 TRACE( "found %s\n", unix_name );
2207 if (!append_entry( data, unix_name, NULL, NULL )) return STATUS_NO_MEMORY;
2209 return STATUS_SUCCESS;
2213 /***********************************************************************
2214 * read_directory_readdir
2216 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2218 static NTSTATUS read_directory_data_readdir( struct dir_data *data, const UNICODE_STRING *mask )
2220 struct dirent *de;
2221 NTSTATUS status = STATUS_NO_MEMORY;
2222 DIR *dir = opendir( "." );
2224 if (!dir) return STATUS_NO_SUCH_FILE;
2226 if (!append_entry( data, ".", NULL, mask )) goto done;
2227 if (!append_entry( data, "..", NULL, mask )) goto done;
2228 while ((de = readdir( dir )))
2230 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2231 if (!append_entry( data, de->d_name, NULL, mask )) goto done;
2233 status = STATUS_SUCCESS;
2235 done:
2236 closedir( dir );
2237 return status;
2241 /***********************************************************************
2242 * read_directory_data
2244 * Read the full contents of a directory, using one of the above helper functions.
2246 static NTSTATUS read_directory_data( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2248 NTSTATUS status;
2250 #ifdef VFAT_IOCTL_READDIR_BOTH
2251 if (!(status = read_directory_data_vfat( data, fd, mask ))) return status;
2252 #endif
2254 if (!has_wildcard( mask ))
2256 /* convert the mask to a Unix name and check for it */
2257 char unix_name[MAX_DIR_ENTRY_LEN * 3 + 1];
2258 int ret = ntdll_wcstoumbs( mask->Buffer, mask->Length / sizeof(WCHAR),
2259 unix_name, sizeof(unix_name) - 1, TRUE );
2260 if (ret > 0)
2262 unix_name[ret] = 0;
2263 #ifdef HAVE_GETATTRLIST
2264 if (!(status = read_directory_data_getattrlist( data, unix_name ))) return status;
2265 #endif
2266 if (!(status = read_directory_data_stat( data, unix_name ))) return status;
2270 return read_directory_data_readdir( data, mask );
2274 /* compare file names for directory sorting */
2275 static int name_compare( const void *a, const void *b )
2277 const struct dir_data_names *file_a = (const struct dir_data_names *)a;
2278 const struct dir_data_names *file_b = (const struct dir_data_names *)b;
2279 int ret = wcsicmp( file_a->long_name, file_b->long_name );
2280 if (!ret) ret = wcscmp( file_a->long_name, file_b->long_name );
2281 return ret;
2285 /***********************************************************************
2286 * init_cached_dir_data
2288 * Initialize the cached directory contents.
2290 static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const UNICODE_STRING *mask )
2292 struct dir_data *data;
2293 struct stat st;
2294 NTSTATUS status;
2295 unsigned int i;
2297 if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY;
2299 if ((status = read_directory_data( data, fd, mask )))
2301 free_dir_data( data );
2302 return status;
2305 /* sort filenames, but not "." and ".." */
2306 i = 0;
2307 if (i < data->count && !strcmp( data->names[i].unix_name, "." )) i++;
2308 if (i < data->count && !strcmp( data->names[i].unix_name, ".." )) i++;
2309 if (i < data->count) qsort( data->names + i, data->count - i, sizeof(*data->names), name_compare );
2311 if (data->count)
2313 fstat( fd, &st );
2314 data->id.dev = st.st_dev;
2315 data->id.ino = st.st_ino;
2318 TRACE( "mask %s found %u files\n", debugstr_us( mask ), data->count );
2319 for (i = 0; i < data->count; i++)
2320 TRACE( "%s %s\n", debugstr_w(data->names[i].long_name), debugstr_w(data->names[i].short_name) );
2322 *data_ret = data;
2323 return data->count ? STATUS_SUCCESS : STATUS_NO_SUCH_FILE;
2327 /***********************************************************************
2328 * get_cached_dir_data
2330 * Retrieve the cached directory data, or initialize it if necessary.
2332 static NTSTATUS get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd,
2333 const UNICODE_STRING *mask )
2335 unsigned int i;
2336 int entry = -1, free_entries[16];
2337 NTSTATUS status;
2339 SERVER_START_REQ( get_directory_cache_entry )
2341 req->handle = wine_server_obj_handle( handle );
2342 wine_server_set_reply( req, free_entries, sizeof(free_entries) );
2343 if (!(status = wine_server_call( req ))) entry = reply->entry;
2345 for (i = 0; i < wine_server_reply_size( reply ) / sizeof(*free_entries); i++)
2347 int free_idx = free_entries[i];
2348 if (free_idx < dir_data_cache_size)
2350 free_dir_data( dir_data_cache[free_idx] );
2351 dir_data_cache[free_idx] = NULL;
2355 SERVER_END_REQ;
2357 if (status)
2359 if (status == STATUS_SHARING_VIOLATION) FIXME( "shared directory handle not supported yet\n" );
2360 return status;
2363 if (entry >= dir_data_cache_size)
2365 unsigned int size = max( dir_data_cache_initial_size, max( dir_data_cache_size * 2, entry + 1 ) );
2366 struct dir_data **new_cache = realloc( dir_data_cache, size * sizeof(*new_cache) );
2368 if (!new_cache) return STATUS_NO_MEMORY;
2369 memset( new_cache + dir_data_cache_size, 0, (size - dir_data_cache_size) * sizeof(*new_cache) );
2370 dir_data_cache = new_cache;
2371 dir_data_cache_size = size;
2374 if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask );
2376 *data_ret = dir_data_cache[entry];
2377 return status;
2381 /******************************************************************************
2382 * NtQueryDirectoryFile (NTDLL.@)
2384 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine,
2385 void *apc_context, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
2386 FILE_INFORMATION_CLASS info_class, BOOLEAN single_entry,
2387 UNICODE_STRING *mask, BOOLEAN restart_scan )
2389 int cwd, fd, needs_close;
2390 enum server_fd_type type;
2391 struct dir_data *data;
2392 NTSTATUS status;
2394 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2395 handle, event, apc_routine, apc_context, io, buffer,
2396 length, info_class, single_entry, debugstr_us(mask),
2397 restart_scan);
2399 if (event || apc_routine)
2401 FIXME( "Unsupported yet option\n" );
2402 return STATUS_NOT_IMPLEMENTED;
2404 switch (info_class)
2406 case FileDirectoryInformation:
2407 case FileBothDirectoryInformation:
2408 case FileFullDirectoryInformation:
2409 case FileIdBothDirectoryInformation:
2410 case FileIdFullDirectoryInformation:
2411 case FileIdGlobalTxDirectoryInformation:
2412 case FileNamesInformation:
2413 if (length < dir_info_align( dir_info_size( info_class, 1 ))) return STATUS_INFO_LENGTH_MISMATCH;
2414 break;
2415 case FileObjectIdInformation:
2416 if (length != sizeof(FILE_OBJECTID_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2417 return STATUS_INVALID_INFO_CLASS;
2418 case FileQuotaInformation:
2419 if (length != sizeof(FILE_QUOTA_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2420 return STATUS_INVALID_INFO_CLASS;
2421 case FileReparsePointInformation:
2422 if (length != sizeof(FILE_REPARSE_POINT_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2423 return STATUS_INVALID_INFO_CLASS;
2424 default:
2425 return STATUS_INVALID_INFO_CLASS;
2427 if (!buffer) return STATUS_ACCESS_VIOLATION;
2429 if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, &type, NULL )))
2430 return status;
2432 if (type != FD_TYPE_DIR)
2434 if (needs_close) close( fd );
2435 return STATUS_INVALID_PARAMETER;
2438 io->Information = 0;
2440 mutex_lock( &dir_mutex );
2442 cwd = open( ".", O_RDONLY );
2443 if (fchdir( fd ) != -1)
2445 if (!(status = get_cached_dir_data( handle, &data, fd, mask )))
2447 union file_directory_info *last_info = NULL;
2449 if (restart_scan) data->pos = 0;
2451 while (!status && data->pos < data->count)
2453 status = get_dir_data_entry( data, buffer, io, length, info_class, &last_info );
2454 if (!status || status == STATUS_BUFFER_OVERFLOW) data->pos++;
2455 if (single_entry && last_info) break;
2458 if (!last_info) status = STATUS_NO_MORE_FILES;
2459 else if (status == STATUS_MORE_ENTRIES) status = STATUS_SUCCESS;
2461 io->u.Status = status;
2463 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
2465 else status = errno_to_status( errno );
2467 mutex_unlock( &dir_mutex );
2469 if (needs_close) close( fd );
2470 if (cwd != -1) close( cwd );
2471 TRACE( "=> %x (%ld)\n", status, io->Information );
2472 return status;
2476 /***********************************************************************
2477 * find_file_in_dir
2479 * Find a file in a directory the hard way, by doing a case-insensitive search.
2480 * The file found is appended to unix_name at pos.
2481 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2483 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
2484 BOOLEAN check_case, BOOLEAN *is_win_dir )
2486 WCHAR buffer[MAX_DIR_ENTRY_LEN];
2487 BOOLEAN is_name_8_dot_3;
2488 DIR *dir;
2489 struct dirent *de;
2490 struct stat st;
2491 int ret;
2493 /* try a shortcut for this directory */
2495 unix_name[pos++] = '/';
2496 ret = ntdll_wcstoumbs( name, length, unix_name + pos, MAX_DIR_ENTRY_LEN + 1, TRUE );
2497 if (ret >= 0 && ret <= MAX_DIR_ENTRY_LEN)
2499 unix_name[pos + ret] = 0;
2500 if (!stat( unix_name, &st ))
2502 if (is_win_dir) *is_win_dir = is_same_file( &windir, &st );
2503 return STATUS_SUCCESS;
2506 if (check_case) goto not_found; /* we want an exact match */
2508 if (pos > 1) unix_name[pos - 1] = 0;
2509 else unix_name[1] = 0; /* keep the initial slash */
2511 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2513 is_name_8_dot_3 = is_legal_8dot3_name( name, length );
2514 #ifndef VFAT_IOCTL_READDIR_BOTH
2515 is_name_8_dot_3 = is_name_8_dot_3 && length >= 8 && name[4] == '~';
2516 #endif
2518 if (!is_name_8_dot_3 && !get_dir_case_sensitivity( unix_name )) goto not_found;
2520 /* now look for it through the directory */
2522 #ifdef VFAT_IOCTL_READDIR_BOTH
2523 if (is_name_8_dot_3)
2525 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
2526 if (fd != -1)
2528 KERNEL_DIRENT kde[2];
2530 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) != -1)
2532 unix_name[pos - 1] = '/';
2533 while (kde[0].d_reclen)
2535 if (kde[1].d_name[0])
2537 ret = ntdll_umbstowcs( kde[1].d_name, strlen(kde[1].d_name),
2538 buffer, MAX_DIR_ENTRY_LEN );
2539 if (ret == length && !wcsnicmp( buffer, name, ret ))
2541 strcpy( unix_name + pos, kde[1].d_name );
2542 close( fd );
2543 goto success;
2546 ret = ntdll_umbstowcs( kde[0].d_name, strlen(kde[0].d_name),
2547 buffer, MAX_DIR_ENTRY_LEN );
2548 if (ret == length && !wcsnicmp( buffer, name, ret ))
2550 strcpy( unix_name + pos,
2551 kde[1].d_name[0] ? kde[1].d_name : kde[0].d_name );
2552 close( fd );
2553 goto success;
2555 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) == -1)
2557 close( fd );
2558 goto not_found;
2561 /* if that did not work, restore previous state of unix_name */
2562 unix_name[pos - 1] = 0;
2564 close( fd );
2566 /* fall through to normal handling */
2568 #endif /* VFAT_IOCTL_READDIR_BOTH */
2570 if (!(dir = opendir( unix_name ))) return errno_to_status( errno );
2572 unix_name[pos - 1] = '/';
2573 while ((de = readdir( dir )))
2575 ret = ntdll_umbstowcs( de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
2576 if (ret == length && !wcsnicmp( buffer, name, ret ))
2578 strcpy( unix_name + pos, de->d_name );
2579 closedir( dir );
2580 goto success;
2583 if (!is_name_8_dot_3) continue;
2585 if (!is_legal_8dot3_name( buffer, ret ))
2587 WCHAR short_nameW[12];
2588 ret = hash_short_file_name( buffer, ret, short_nameW );
2589 if (ret == length && !wcsnicmp( short_nameW, name, length ))
2591 strcpy( unix_name + pos, de->d_name );
2592 closedir( dir );
2593 goto success;
2597 closedir( dir );
2599 not_found:
2600 unix_name[pos - 1] = 0;
2601 return STATUS_OBJECT_PATH_NOT_FOUND;
2603 success:
2604 if (is_win_dir && !stat( unix_name, &st )) *is_win_dir = is_same_file( &windir, &st );
2605 return STATUS_SUCCESS;
2609 #ifndef _WIN64
2611 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2612 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2613 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};
2614 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2615 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2616 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2617 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
2618 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
2619 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2621 static struct
2623 const WCHAR *source;
2624 const char *unix_target;
2625 } redirects[] =
2627 { catrootW, NULL },
2628 { catroot2W, NULL },
2629 { driversstoreW, NULL },
2630 { driversetcW, NULL },
2631 { logfilesW, NULL },
2632 { spoolW, NULL },
2633 { system32W, "syswow64" },
2634 { sysnativeW, "system32" },
2635 { regeditW, "syswow64/regedit.exe" }
2638 static unsigned int nb_redirects;
2641 /***********************************************************************
2642 * init_redirects
2644 static void init_redirects(void)
2646 static const char windows_dir[] = "/dosdevices/c:/windows";
2647 char *dir;
2648 struct stat st;
2650 if (!(dir = malloc( strlen(config_dir) + sizeof(windows_dir) ))) return;
2651 strcpy( dir, config_dir );
2652 strcat( dir, windows_dir );
2653 if (!stat( dir, &st ))
2655 windir.dev = st.st_dev;
2656 windir.ino = st.st_ino;
2657 nb_redirects = ARRAY_SIZE( redirects );
2659 else ERR( "%s: %s\n", dir, strerror(errno) );
2660 free( dir );
2665 /***********************************************************************
2666 * match_redirect
2668 * Check if path matches a redirect name. If yes, return matched length.
2670 static int match_redirect( const WCHAR *path, int len, const WCHAR *redir, BOOLEAN check_case )
2672 int i = 0;
2674 while (i < len)
2676 int start = i;
2677 while (i < len && !IS_SEPARATOR(path[i])) i++;
2678 if (check_case)
2680 if (wcsncmp( path + start, redir, i - start )) return 0;
2682 else
2684 if (wcsnicmp( path + start, redir, i - start )) return 0;
2686 redir += i - start;
2687 while (i < len && IS_SEPARATOR(path[i])) i++;
2688 if (!*redir) return i;
2689 if (*redir++ != '\\') return 0;
2691 return 0;
2695 /***********************************************************************
2696 * get_redirect_path
2698 * Retrieve the Unix path corresponding to a redirected path if any.
2700 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, BOOLEAN check_case )
2702 unsigned int i;
2703 int len;
2705 for (i = 0; i < nb_redirects; i++)
2707 if ((len = match_redirect( name, length, redirects[i].source, check_case )))
2709 if (!redirects[i].unix_target) break;
2710 unix_name[pos++] = '/';
2711 strcpy( unix_name + pos, redirects[i].unix_target );
2712 return len;
2715 return 0;
2718 #else /* _WIN64 */
2720 /* there are no redirects on 64-bit */
2721 static int get_redirect_path( char *unix_name, int pos, const WCHAR *name, int length, BOOLEAN check_case )
2723 return 0;
2726 #endif
2728 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
2730 /***********************************************************************
2731 * init_files
2733 void init_files(void)
2735 HANDLE key;
2737 #ifndef _WIN64
2738 if (is_wow64) init_redirects();
2739 #endif
2740 /* a couple of directories that we don't want to return in directory searches */
2741 ignore_file( config_dir );
2742 ignore_file( "/dev" );
2743 ignore_file( "/proc" );
2744 #ifdef linux
2745 ignore_file( "/sys" );
2746 #endif
2747 /* retrieve initial umask */
2748 start_umask = umask( 0777 );
2749 umask( start_umask );
2751 if (!open_hkcu_key( "Software\\Wine", &key ))
2753 static WCHAR showdotfilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
2754 char tmp[80];
2755 DWORD dummy;
2756 UNICODE_STRING nameW;
2758 init_unicode_string( &nameW, showdotfilesW );
2759 if (!NtQueryValueKey( key, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
2761 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
2762 show_dot_files = IS_OPTION_TRUE( str[0] );
2764 NtClose( key );
2769 /******************************************************************************
2770 * get_dos_device
2772 * Get the Unix path of a DOS device.
2774 static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, char **unix_name_ret )
2776 struct stat st;
2777 char *unix_name, *new_name, *dev;
2778 unsigned int i;
2779 int unix_len;
2781 /* make sure the device name is ASCII */
2782 for (i = 0; i < name_len; i++)
2783 if (name[i] <= 32 || name[i] >= 127) return STATUS_BAD_DEVICE_TYPE;
2785 unix_len = strlen(config_dir) + sizeof("/dosdevices/") + name_len + 1;
2787 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
2789 strcpy( unix_name, config_dir );
2790 strcat( unix_name, "/dosdevices/" );
2791 dev = unix_name + strlen(unix_name);
2793 for (i = 0; i < name_len; i++) dev[i] = (name[i] >= 'A' && name[i] <= 'Z' ? name[i] + 32 : name[i]);
2794 dev[i] = 0;
2796 /* special case for drive devices */
2797 if (name_len == 2 && dev[1] == ':')
2799 dev[i++] = ':';
2800 dev[i] = 0;
2803 for (;;)
2805 if (!stat( unix_name, &st ))
2807 TRACE( "%s -> %s\n", debugstr_wn(name,name_len), debugstr_a(unix_name) );
2808 *unix_name_ret = unix_name;
2809 return STATUS_SUCCESS;
2811 if (!dev) break;
2813 /* now try some defaults for it */
2814 if (!strcmp( dev, "aux" ))
2816 strcpy( dev, "com1" );
2817 continue;
2819 if (!strcmp( dev, "prn" ))
2821 strcpy( dev, "lpt1" );
2822 continue;
2825 new_name = NULL;
2826 if (dev[1] == ':' && dev[2] == ':') /* drive device */
2828 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
2829 new_name = get_default_drive_device( unix_name );
2832 if (!new_name) break;
2833 free( unix_name );
2834 unix_name = new_name;
2835 dev = NULL; /* last try */
2837 free( unix_name );
2838 return STATUS_BAD_DEVICE_TYPE;
2842 /* return the length of the DOS namespace prefix if any */
2843 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
2845 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
2846 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2848 if (name->Length >= sizeof(nt_prefixW) &&
2849 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
2850 return ARRAY_SIZE( nt_prefixW );
2852 if (name->Length >= sizeof(dosdev_prefixW) &&
2853 !wcsnicmp( name->Buffer, dosdev_prefixW, ARRAY_SIZE( dosdev_prefixW )))
2854 return ARRAY_SIZE( dosdev_prefixW );
2856 return 0;
2860 /***********************************************************************
2861 * remove_last_componentA
2863 * Remove the last component of the path. Helper for find_drive_rootA.
2865 static inline unsigned int remove_last_componentA( const char *path, unsigned int len )
2867 int level = 0;
2869 while (level < 1)
2871 /* find start of the last path component */
2872 unsigned int prev = len;
2873 if (prev <= 1) break; /* reached root */
2874 while (prev > 1 && path[prev - 1] != '/') prev--;
2875 /* does removing it take us up a level? */
2876 if (len - prev != 1 || path[prev] != '.') /* not '.' */
2878 if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.') /* is it '..'? */
2879 level--;
2880 else
2881 level++;
2883 /* strip off trailing slashes */
2884 while (prev > 1 && path[prev - 1] == '/') prev--;
2885 len = prev;
2887 return len;
2891 /***********************************************************************
2892 * find_drive_rootA
2894 * Find a drive for which the root matches the beginning of the given path.
2895 * This can be used to translate a Unix path into a drive + DOS path.
2896 * Return value is the drive, or -1 on error. On success, ppath is modified
2897 * to point to the beginning of the DOS path.
2899 static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_ret )
2901 /* Starting with the full path, check if the device and inode match any of
2902 * the wine 'drives'. If not then remove the last path component and try
2903 * again. If the last component was a '..' then skip a normal component
2904 * since it's a directory that's ascended back out of.
2906 int drive;
2907 char *buffer;
2908 const char *path = *ppath;
2909 struct stat st;
2910 struct file_identity info[MAX_DOS_DRIVES];
2912 /* get device and inode of all drives */
2913 if (!get_drives_info( info )) return STATUS_OBJECT_PATH_NOT_FOUND;
2915 /* strip off trailing slashes */
2916 while (len > 1 && path[len - 1] == '/') len--;
2918 /* make a copy of the path */
2919 if (!(buffer = malloc( len + 1 ))) return STATUS_NO_MEMORY;
2920 memcpy( buffer, path, len );
2921 buffer[len] = 0;
2923 for (;;)
2925 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
2927 /* Find the drive */
2928 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
2930 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
2932 if (len == 1) len = 0; /* preserve root slash in returned path */
2933 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2934 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
2935 *ppath += len;
2936 *drive_ret = drive;
2937 free( buffer );
2938 return STATUS_SUCCESS;
2942 if (len <= 1) break; /* reached root */
2943 len = remove_last_componentA( buffer, len );
2944 buffer[len] = 0;
2946 free( buffer );
2947 return STATUS_OBJECT_PATH_NOT_FOUND;
2951 /******************************************************************************
2952 * rebuild_nt_name
2954 static void rebuild_nt_name( const UNICODE_STRING *nameW, DWORD prefix_len,
2955 const char *unix_name, UNICODE_STRING *nt_name )
2957 WCHAR *buf;
2958 DWORD len;
2960 while (*unix_name == '/') unix_name++;
2961 nt_name->MaximumLength = (prefix_len + strlen(unix_name) + 2) * sizeof(WCHAR);
2962 if (!(buf = malloc( nt_name->MaximumLength ))) return;
2963 nt_name->Buffer = buf;
2964 memcpy( buf, nameW->Buffer, prefix_len * sizeof(WCHAR) );
2965 if (prefix_len && buf[prefix_len - 1] != '\\') buf[prefix_len++] = '\\';
2966 buf += prefix_len;
2967 len = ntdll_umbstowcs( unix_name, strlen(unix_name), buf, strlen(unix_name) );
2968 for (; len; len--, buf++) if (*buf == '/') *buf = '\\';
2969 *buf = 0;
2970 nt_name->Length = (buf - nt_name->Buffer) * sizeof(WCHAR);
2974 /******************************************************************************
2975 * find_file_id
2977 * Recursively search directories from the dir queue for a given inode.
2979 static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev )
2981 unsigned int pos;
2982 DIR *dir;
2983 struct dirent *de;
2984 NTSTATUS status;
2985 struct stat st;
2986 char *name = *unix_name;
2988 while (!(status = next_dir_in_queue( name )))
2990 if (!(dir = opendir( name ))) continue;
2991 TRACE( "searching %s for %s\n", debugstr_a(name), wine_dbgstr_longlong(file_id) );
2992 pos = strlen( name );
2993 if (pos + MAX_DIR_ENTRY_LEN >= *len / sizeof(WCHAR))
2995 if (!(name = realloc( name, *len * 2 )))
2997 closedir( dir );
2998 return STATUS_NO_MEMORY;
3000 *len *= 2;
3001 *unix_name = name;
3003 name[pos++] = '/';
3004 while ((de = readdir( dir )))
3006 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
3007 strcpy( name + pos, de->d_name );
3008 if (lstat( name, &st ) == -1) continue;
3009 if (st.st_dev != dev) continue;
3010 if (st.st_ino == file_id)
3012 closedir( dir );
3013 return STATUS_SUCCESS;
3015 if (!S_ISDIR( st.st_mode )) continue;
3016 if ((status = add_dir_to_queue( name )) != STATUS_SUCCESS)
3018 closedir( dir );
3019 return status;
3022 closedir( dir );
3024 return status;
3028 /******************************************************************************
3029 * file_id_to_unix_file_name
3031 * Lookup a file from its file id instead of its name.
3033 static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **unix_name_ret,
3034 UNICODE_STRING *nt_name )
3036 enum server_fd_type type;
3037 int old_cwd, root_fd, needs_close;
3038 char *unix_name;
3039 ULONG len;
3040 NTSTATUS status;
3041 ULONGLONG file_id;
3042 struct stat st, root_st;
3044 if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
3045 if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
3046 memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
3048 len = 2 * MAX_DIR_ENTRY_LEN + 4;
3049 if (!(unix_name = malloc( len ))) return STATUS_NO_MEMORY;
3050 strcpy( unix_name, "." );
3052 if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3053 goto done;
3055 if (type != FD_TYPE_DIR)
3057 status = STATUS_OBJECT_TYPE_MISMATCH;
3058 goto done;
3061 fstat( root_fd, &root_st );
3062 if (root_st.st_ino == file_id) /* shortcut for "." */
3064 status = STATUS_SUCCESS;
3065 goto done;
3068 mutex_lock( &dir_mutex );
3069 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3071 /* shortcut for ".." */
3072 if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
3074 strcpy( unix_name, ".." );
3075 status = STATUS_SUCCESS;
3077 else
3079 status = add_dir_to_queue( "." );
3080 if (!status)
3081 status = find_file_id( &unix_name, &len, file_id, root_st.st_dev );
3082 if (!status) /* get rid of "./" prefix */
3083 memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 );
3084 flush_dir_queue();
3086 if (fchdir( old_cwd ) == -1) chdir( "/" );
3088 else status = errno_to_status( errno );
3089 mutex_unlock( &dir_mutex );
3090 if (old_cwd != -1) close( old_cwd );
3092 done:
3093 if (status == STATUS_SUCCESS)
3095 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name) );
3096 *unix_name_ret = unix_name;
3097 if (nt_name) rebuild_nt_name( attr->ObjectName, 0, unix_name, nt_name );
3099 else
3101 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
3102 free( unix_name );
3104 if (needs_close) close( root_fd );
3105 return status;
3109 /******************************************************************************
3110 * lookup_unix_name
3112 * Helper for nt_to_unix_file_name
3114 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
3115 UINT disposition, BOOLEAN check_case )
3117 NTSTATUS status;
3118 int ret, len;
3119 struct stat st;
3120 char *unix_name = *buffer;
3121 const BOOL redirect = NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR];
3123 /* try a shortcut first */
3125 while (name_len && IS_SEPARATOR(*name))
3127 name++;
3128 name_len--;
3131 unix_name[pos] = '/';
3132 ret = ntdll_wcstoumbs( name, name_len, unix_name + pos + 1, unix_len - pos - 1, TRUE );
3133 if (ret >= 0 && ret < unix_len - pos - 1)
3135 char *p;
3136 unix_name[pos + 1 + ret] = 0;
3137 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
3138 if (!name_len || !redirect || (!strstr( unix_name, "/windows/") && strncmp( unix_name, "windows/", 8 )))
3140 if (!stat( unix_name, &st ))
3142 if (disposition == FILE_CREATE)
3143 return STATUS_OBJECT_NAME_COLLISION;
3144 return STATUS_SUCCESS;
3149 if (!name_len) /* empty name -> drive root doesn't exist */
3150 return STATUS_OBJECT_PATH_NOT_FOUND;
3151 if (check_case && !redirect && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
3152 return STATUS_OBJECT_NAME_NOT_FOUND;
3154 /* now do it component by component */
3156 while (name_len)
3158 const WCHAR *end, *next;
3159 BOOLEAN is_win_dir = FALSE;
3161 end = name;
3162 while (end < name + name_len && !IS_SEPARATOR(*end)) end++;
3163 next = end;
3164 while (next < name + name_len && IS_SEPARATOR(*next)) next++;
3165 name_len -= next - name;
3167 /* grow the buffer if needed */
3169 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
3171 char *new_name;
3172 unix_len += 2 * MAX_DIR_ENTRY_LEN;
3173 if (!(new_name = realloc( unix_name, unix_len ))) return STATUS_NO_MEMORY;
3174 unix_name = *buffer = new_name;
3177 status = find_file_in_dir( unix_name, pos, name, end - name,
3178 check_case, redirect ? &is_win_dir : NULL );
3180 /* if this is the last element, not finding it is not necessarily fatal */
3181 if (!name_len)
3183 if (status == STATUS_OBJECT_PATH_NOT_FOUND)
3185 status = STATUS_OBJECT_NAME_NOT_FOUND;
3186 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3188 ret = ntdll_wcstoumbs( name, end - name, unix_name + pos + 1, MAX_DIR_ENTRY_LEN + 1, TRUE );
3189 if (ret > 0 && ret <= MAX_DIR_ENTRY_LEN)
3191 unix_name[pos] = '/';
3192 unix_name[pos + 1 + ret] = 0;
3193 status = STATUS_NO_SUCH_FILE;
3194 break;
3198 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
3200 status = STATUS_OBJECT_NAME_COLLISION;
3204 if (status != STATUS_SUCCESS) break;
3206 pos += strlen( unix_name + pos );
3207 name = next;
3209 if (is_win_dir && (len = get_redirect_path( unix_name, pos, name, name_len, check_case )))
3211 name += len;
3212 name_len -= len;
3213 pos += strlen( unix_name + pos );
3214 TRACE( "redirecting -> %s + %s\n", debugstr_a(unix_name), debugstr_w(name) );
3218 return status;
3222 /******************************************************************************
3223 * nt_to_unix_file_name_attr
3225 static NTSTATUS nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, char **name_ret,
3226 UNICODE_STRING *nt_name, UINT disposition )
3228 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
3229 enum server_fd_type type;
3230 int old_cwd, root_fd, needs_close;
3231 const WCHAR *name, *p;
3232 char *unix_name;
3233 int name_len, unix_len;
3234 NTSTATUS status;
3236 if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
3237 return nt_to_unix_file_name( attr->ObjectName, name_ret, nt_name, disposition );
3239 name = attr->ObjectName->Buffer;
3240 name_len = attr->ObjectName->Length / sizeof(WCHAR);
3242 if (name_len && IS_SEPARATOR(name[0])) return STATUS_INVALID_PARAMETER;
3244 /* check for invalid characters */
3245 for (p = name; p < name + name_len; p++)
3246 if (*p < 32 || wcschr( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
3248 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3249 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3250 unix_name[0] = '.';
3252 if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3254 if (type != FD_TYPE_DIR)
3256 if (needs_close) close( root_fd );
3257 status = STATUS_BAD_DEVICE_TYPE;
3259 else
3261 mutex_lock( &dir_mutex );
3262 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3264 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1,
3265 disposition, FALSE );
3266 if (fchdir( old_cwd ) == -1) chdir( "/" );
3268 else status = errno_to_status( errno );
3269 mutex_unlock( &dir_mutex );
3270 if (old_cwd != -1) close( old_cwd );
3271 if (needs_close) close( root_fd );
3274 else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
3276 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3278 TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
3279 *name_ret = unix_name;
3280 if (nt_name) rebuild_nt_name( attr->ObjectName, 0, unix_name, nt_name );
3282 else
3284 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3285 free( unix_name );
3287 return status;
3291 /******************************************************************************
3292 * nt_to_unix_file_name
3294 * Convert a file name from NT namespace to Unix namespace.
3296 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3297 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3298 * returned, but the unix name is still filled in properly.
3300 NTSTATUS nt_to_unix_file_name( const UNICODE_STRING *nameW, char **unix_name_ret,
3301 UNICODE_STRING *nt_name, UINT disposition )
3303 static const WCHAR unixW[] = {'u','n','i','x'};
3304 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
3306 NTSTATUS status = STATUS_SUCCESS;
3307 const WCHAR *name, *p;
3308 struct stat st;
3309 char *unix_name;
3310 int pos, ret, name_len, unix_len, prefix_len;
3311 WCHAR prefix[MAX_DIR_ENTRY_LEN + 1];
3312 BOOLEAN check_case = FALSE;
3313 BOOLEAN is_unix = FALSE;
3315 name = nameW->Buffer;
3316 name_len = nameW->Length / sizeof(WCHAR);
3318 if (!name_len || !IS_SEPARATOR(name[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD;
3320 if (!(pos = get_dos_prefix_len( nameW )))
3321 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
3323 name += pos;
3324 name_len -= pos;
3326 if (!name_len) return STATUS_OBJECT_NAME_INVALID;
3328 /* check for sub-directory */
3329 for (pos = 0; pos < name_len && pos <= MAX_DIR_ENTRY_LEN; pos++)
3331 if (IS_SEPARATOR(name[pos])) break;
3332 if (name[pos] < 32 || wcschr( invalid_charsW, name[pos] ))
3333 return STATUS_OBJECT_NAME_INVALID;
3334 prefix[pos] = (name[pos] >= 'A' && name[pos] <= 'Z') ? name[pos] + 'a' - 'A' : name[pos];
3336 if (pos > MAX_DIR_ENTRY_LEN) return STATUS_OBJECT_NAME_INVALID;
3338 if (pos == name_len) /* no subdir, plain DOS device */
3339 return get_dos_device( name, name_len, unix_name_ret );
3341 prefix_len = pos;
3342 prefix[prefix_len] = 0;
3344 name += prefix_len;
3345 name_len -= prefix_len;
3347 /* check for invalid characters (all chars except 0 are valid for unix) */
3348 is_unix = (prefix_len == 4 && !memcmp( prefix, unixW, sizeof(unixW) ));
3349 if (is_unix)
3351 for (p = name; p < name + name_len; p++)
3352 if (!*p) return STATUS_OBJECT_NAME_INVALID;
3353 check_case = TRUE;
3355 else
3357 for (p = name; p < name + name_len; p++)
3358 if (*p < 32 || wcschr( invalid_charsW, *p )) return STATUS_OBJECT_NAME_INVALID;
3361 unix_len = (prefix_len + name_len) * 3 + MAX_DIR_ENTRY_LEN + 3;
3362 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
3363 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3364 strcpy( unix_name, config_dir );
3365 strcat( unix_name, "/dosdevices/" );
3366 pos = strlen(unix_name);
3368 ret = ntdll_wcstoumbs( prefix, prefix_len, unix_name + pos, unix_len - pos - 1, TRUE );
3369 if (ret <= 0)
3371 free( unix_name );
3372 return STATUS_OBJECT_NAME_INVALID;
3374 pos += ret;
3376 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3378 if (prefix_len != 2 || prefix[1] != ':')
3380 unix_name[pos] = 0;
3381 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
3383 if (!is_unix)
3385 free( unix_name );
3386 return STATUS_BAD_DEVICE_TYPE;
3388 pos = 0; /* fall back to unix root */
3392 status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, check_case );
3393 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3395 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
3396 *unix_name_ret = unix_name;
3397 if (nt_name) rebuild_nt_name( nameW, name - nameW->Buffer, unix_name + pos, nt_name );
3399 else
3401 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3402 free( unix_name );
3404 return status;
3408 /******************************************************************************
3409 * wine_nt_to_unix_file_name
3411 * Convert a file name from NT namespace to Unix namespace.
3413 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3414 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3415 * returned, but the unix name is still filled in properly.
3417 NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, char *nameA, SIZE_T *size,
3418 UINT disposition )
3420 char *buffer = NULL;
3421 NTSTATUS status = nt_to_unix_file_name( nameW, &buffer, NULL, disposition );
3423 if (buffer)
3425 if (*size > strlen(buffer)) strcpy( nameA, buffer );
3426 else status = STATUS_BUFFER_TOO_SMALL;
3427 *size = strlen(buffer) + 1;
3428 free( buffer );
3430 return status;
3434 /******************************************************************
3435 * collapse_path
3437 * Get rid of . and .. components in the path.
3439 static void collapse_path( WCHAR *path )
3441 WCHAR *p, *start, *next;
3443 /* convert every / into a \ */
3444 for (p = path; *p; p++) if (*p == '/') *p = '\\';
3446 p = path + 4;
3447 while (*p && *p != '\\') p++;
3448 start = p + 1;
3450 /* collapse duplicate backslashes */
3451 next = start;
3452 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
3453 *next = 0;
3455 p = start;
3456 while (*p)
3458 if (*p == '.')
3460 switch(p[1])
3462 case '\\': /* .\ component */
3463 next = p + 2;
3464 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3465 continue;
3466 case 0: /* final . */
3467 if (p > start) p--;
3468 *p = 0;
3469 continue;
3470 case '.':
3471 if (p[2] == '\\') /* ..\ component */
3473 next = p + 3;
3474 if (p > start)
3476 p--;
3477 while (p > start && p[-1] != '\\') p--;
3479 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3480 continue;
3482 else if (!p[2]) /* final .. */
3484 if (p > start)
3486 p--;
3487 while (p > start && p[-1] != '\\') p--;
3488 if (p > start) p--;
3490 *p = 0;
3491 continue;
3493 break;
3496 /* skip to the next component */
3497 while (*p && *p != '\\') p++;
3498 if (*p == '\\')
3500 /* remove last dot in previous dir name */
3501 if (p > start && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
3502 else p++;
3506 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3507 while (p > start && (p[-1] == ' ' || p[-1] == '.')) p--;
3508 *p = 0;
3512 /******************************************************************
3513 * unix_to_nt_file_name
3515 NTSTATUS unix_to_nt_file_name( const char *name, WCHAR **nt )
3517 static const WCHAR unix_prefixW[] = {'\\','?','?','\\','u','n','i','x',0};
3518 WCHAR dos_prefixW[] = {'\\','?','?','\\','A',':','\\',0};
3519 const WCHAR *prefix = unix_prefixW;
3520 unsigned int lenW, lenA = strlen(name);
3521 const char *path = name;
3522 NTSTATUS status;
3523 WCHAR *buffer;
3524 int drive;
3526 status = find_drive_rootA( &path, lenA, &drive );
3527 lenA -= path - name;
3529 if (status == STATUS_SUCCESS)
3531 while (lenA && path[0] == '/') { lenA--; path++; }
3532 dos_prefixW[4] += drive;
3533 prefix = dos_prefixW;
3535 else if (status != STATUS_OBJECT_PATH_NOT_FOUND) return status;
3537 lenW = wcslen( prefix );
3538 if (!(buffer = malloc( (lenA + lenW + 1) * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
3539 memcpy( buffer, prefix, lenW * sizeof(WCHAR) );
3540 lenW += ntdll_umbstowcs( path, lenA, buffer + lenW, lenA );
3541 buffer[lenW] = 0;
3542 collapse_path( buffer );
3543 *nt = buffer;
3544 return STATUS_SUCCESS;
3548 /******************************************************************
3549 * wine_unix_to_nt_file_name
3551 NTSTATUS CDECL wine_unix_to_nt_file_name( const char *name, WCHAR *buffer, SIZE_T *size )
3553 WCHAR *nt_name = NULL;
3554 NTSTATUS status;
3556 if (name[0] != '/') return STATUS_INVALID_PARAMETER; /* relative paths are not supported */
3558 status = unix_to_nt_file_name( name, &nt_name );
3559 if (nt_name)
3561 if (*size > wcslen(nt_name)) wcscpy( buffer, nt_name );
3562 else status = STATUS_BUFFER_TOO_SMALL;
3563 *size = wcslen(nt_name) + 1;
3564 free( nt_name );
3566 return status;
3570 /***********************************************************************
3571 * get_full_path
3573 * Simplified version of RtlGetFullPathName_U.
3575 NTSTATUS get_full_path( const WCHAR *name, const WCHAR *curdir, WCHAR **path )
3577 static const WCHAR uncW[] = {'\\','?','?','\\','U','N','C','\\',0};
3578 static const WCHAR devW[] = {'\\','?','?','\\',0};
3579 static const WCHAR unixW[] = {'u','n','i','x'};
3580 WCHAR *ret, root[] = {'\\','?','?','\\','C',':','\\',0};
3581 NTSTATUS status = STATUS_SUCCESS;
3582 const WCHAR *prefix;
3584 if (IS_SEPARATOR(name[0]) && IS_SEPARATOR(name[1])) /* \\ prefix */
3586 if ((name[2] == '.' || name[2] == '?') && IS_SEPARATOR(name[3])) /* \\?\ device */
3588 name += 4;
3589 if (!wcsnicmp( name, unixW, 4 ) && IS_SEPARATOR(name[4])) /* \\?\unix special name */
3591 char *unix_name;
3592 name += 4;
3593 unix_name = malloc( wcslen(name) * 3 + 1 );
3594 ntdll_wcstoumbs( name, wcslen(name) + 1, unix_name, wcslen(name) * 3 + 1, FALSE );
3595 status = unix_to_nt_file_name( unix_name, path );
3596 free( unix_name );
3597 return status;
3599 prefix = devW;
3601 else prefix = uncW; /* UNC path */
3603 else if (IS_SEPARATOR(name[0])) /* absolute path */
3605 root[4] = curdir[0];
3606 prefix = root;
3608 else if (name[0] && name[1] == ':') /* drive letter */
3610 root[4] = towupper(name[0]);
3611 name += 2;
3612 prefix = root;
3614 else prefix = curdir; /* relative path */
3616 ret = malloc( (wcslen(prefix) + wcslen(name) + 1) * sizeof(WCHAR) );
3617 wcscpy( ret, prefix );
3618 wcscat( ret, name );
3619 collapse_path( ret );
3620 *path = ret;
3621 return STATUS_SUCCESS;
3625 /***********************************************************************
3626 * unmount_device
3628 * Unmount the specified device.
3630 static NTSTATUS unmount_device( HANDLE handle )
3632 NTSTATUS status;
3633 int unix_fd, needs_close;
3635 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
3637 struct stat st;
3638 char *mount_point = NULL;
3640 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
3641 status = STATUS_INVALID_PARAMETER;
3642 else
3644 if ((mount_point = get_device_mount_point( st.st_rdev )))
3646 #ifdef __APPLE__
3647 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
3648 #else
3649 static const char umount[] = "umount >/dev/null 2>&1 ";
3650 #endif
3651 char *cmd = malloc( strlen(mount_point)+sizeof(umount));
3652 if (cmd)
3654 strcpy( cmd, umount );
3655 strcat( cmd, mount_point );
3656 system( cmd );
3657 free( cmd );
3658 #ifdef linux
3659 /* umount will fail to release the loop device since we still have
3660 a handle to it, so we release it here */
3661 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3662 #endif
3664 free( mount_point );
3667 if (needs_close) close( unix_fd );
3669 return status;
3673 /******************************************************************************
3674 * open_unix_file
3676 * Helper for NtCreateFile that takes a Unix path.
3678 NTSTATUS open_unix_file( HANDLE *handle, const char *unix_name, ACCESS_MASK access,
3679 OBJECT_ATTRIBUTES *attr, ULONG attributes, ULONG sharing, ULONG disposition,
3680 ULONG options, void *ea_buffer, ULONG ea_length )
3682 struct object_attributes *objattr;
3683 NTSTATUS status;
3684 data_size_t len;
3686 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3688 SERVER_START_REQ( create_file )
3690 req->access = access;
3691 req->sharing = sharing;
3692 req->create = disposition;
3693 req->options = options;
3694 req->attrs = attributes;
3695 wine_server_add_data( req, objattr, len );
3696 wine_server_add_data( req, unix_name, strlen(unix_name) );
3697 status = wine_server_call( req );
3698 *handle = wine_server_ptr_handle( reply->handle );
3700 SERVER_END_REQ;
3701 free( objattr );
3702 return status;
3706 /******************************************************************************
3707 * NtCreateFile (NTDLL.@)
3709 NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
3710 IO_STATUS_BLOCK *io, LARGE_INTEGER *alloc_size,
3711 ULONG attributes, ULONG sharing, ULONG disposition,
3712 ULONG options, void *ea_buffer, ULONG ea_length )
3714 UNICODE_STRING nt_name = { 0 };
3715 char *unix_name;
3716 BOOL created = FALSE;
3718 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
3719 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
3720 handle, access, debugstr_us(attr->ObjectName), attr->Attributes,
3721 attr->RootDirectory, attr->SecurityDescriptor, io, alloc_size,
3722 attributes, sharing, disposition, options, ea_buffer, ea_length );
3724 if (!attr || !attr->ObjectName) return STATUS_INVALID_PARAMETER;
3726 if (alloc_size) FIXME( "alloc_size not supported\n" );
3728 if (options & FILE_OPEN_BY_FILE_ID)
3729 io->u.Status = file_id_to_unix_file_name( attr, &unix_name, &nt_name );
3730 else
3731 io->u.Status = nt_to_unix_file_name_attr( attr, &unix_name, &nt_name, disposition );
3733 if (io->u.Status == STATUS_BAD_DEVICE_TYPE)
3735 SERVER_START_REQ( open_file_object )
3737 req->access = access;
3738 req->attributes = attr->Attributes;
3739 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
3740 req->sharing = sharing;
3741 req->options = options;
3742 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
3743 io->u.Status = wine_server_call( req );
3744 *handle = wine_server_ptr_handle( reply->handle );
3746 SERVER_END_REQ;
3747 if (io->u.Status == STATUS_SUCCESS) io->Information = FILE_OPENED;
3748 return io->u.Status;
3751 if (io->u.Status == STATUS_NO_SUCH_FILE && disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3753 created = TRUE;
3754 io->u.Status = STATUS_SUCCESS;
3757 if (io->u.Status == STATUS_SUCCESS)
3759 OBJECT_ATTRIBUTES nt_attr = *attr;
3761 if (nt_name.Buffer) nt_attr.ObjectName = &nt_name;
3762 io->u.Status = open_unix_file( handle, unix_name, access, &nt_attr, attributes,
3763 sharing, disposition, options, ea_buffer, ea_length );
3764 free( nt_name.Buffer );
3765 free( unix_name );
3767 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), io->u.Status );
3769 if (io->u.Status == STATUS_SUCCESS)
3771 if (created) io->Information = FILE_CREATED;
3772 else switch(disposition)
3774 case FILE_SUPERSEDE:
3775 io->Information = FILE_SUPERSEDED;
3776 break;
3777 case FILE_CREATE:
3778 io->Information = FILE_CREATED;
3779 break;
3780 case FILE_OPEN:
3781 case FILE_OPEN_IF:
3782 io->Information = FILE_OPENED;
3783 break;
3784 case FILE_OVERWRITE:
3785 case FILE_OVERWRITE_IF:
3786 io->Information = FILE_OVERWRITTEN;
3787 break;
3790 else if (io->u.Status == STATUS_TOO_MANY_OPENED_FILES)
3792 static int once;
3793 if (!once++) ERR_(winediag)( "Too many open files, ulimit -n probably needs to be increased\n" );
3796 return io->u.Status;
3800 /******************************************************************************
3801 * NtOpenFile (NTDLL.@)
3803 NTSTATUS WINAPI NtOpenFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
3804 IO_STATUS_BLOCK *io, ULONG sharing, ULONG options )
3806 return NtCreateFile( handle, access, attr, io, NULL, 0, sharing, FILE_OPEN, options, NULL, 0 );
3810 /******************************************************************************
3811 * NtCreateMailslotFile (NTDLL.@)
3813 NTSTATUS WINAPI NtCreateMailslotFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
3814 IO_STATUS_BLOCK *io, ULONG options, ULONG quota, ULONG msg_size,
3815 LARGE_INTEGER *timeout )
3817 NTSTATUS status;
3818 data_size_t len;
3819 struct object_attributes *objattr;
3821 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
3822 handle, access, attr, io, options, quota, msg_size, timeout );
3824 if (!handle) return STATUS_ACCESS_VIOLATION;
3825 if (!attr) return STATUS_INVALID_PARAMETER;
3827 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3829 SERVER_START_REQ( create_mailslot )
3831 req->access = access;
3832 req->max_msgsize = msg_size;
3833 req->read_timeout = timeout ? timeout->QuadPart : -1;
3834 wine_server_add_data( req, objattr, len );
3835 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
3837 SERVER_END_REQ;
3839 free( objattr );
3840 return status;
3844 /******************************************************************
3845 * NtCreateNamedPipeFile (NTDLL.@)
3847 NTSTATUS WINAPI NtCreateNamedPipeFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
3848 IO_STATUS_BLOCK *io, ULONG sharing, ULONG dispo, ULONG options,
3849 ULONG pipe_type, ULONG read_mode, ULONG completion_mode,
3850 ULONG max_inst, ULONG inbound_quota, ULONG outbound_quota,
3851 LARGE_INTEGER *timeout )
3853 NTSTATUS status;
3854 data_size_t len;
3855 struct object_attributes *objattr;
3857 if (!attr) return STATUS_INVALID_PARAMETER;
3859 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
3860 handle, access, debugstr_us(attr->ObjectName), io, sharing, dispo,
3861 options, pipe_type, read_mode, completion_mode, max_inst, inbound_quota,
3862 outbound_quota, timeout );
3864 /* assume we only get relative timeout */
3865 if (timeout->QuadPart > 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout->QuadPart) );
3867 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3869 SERVER_START_REQ( create_named_pipe )
3871 req->access = access;
3872 req->options = options;
3873 req->sharing = sharing;
3874 req->flags =
3875 (pipe_type ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0) |
3876 (read_mode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0) |
3877 (completion_mode ? NAMED_PIPE_NONBLOCKING_MODE : 0);
3878 req->maxinstances = max_inst;
3879 req->outsize = outbound_quota;
3880 req->insize = inbound_quota;
3881 req->timeout = timeout->QuadPart;
3882 wine_server_add_data( req, objattr, len );
3883 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
3885 SERVER_END_REQ;
3887 free( objattr );
3888 return status;
3892 /******************************************************************
3893 * NtDeleteFile (NTDLL.@)
3895 NTSTATUS WINAPI NtDeleteFile( OBJECT_ATTRIBUTES *attr )
3897 HANDLE handle;
3898 NTSTATUS status;
3899 IO_STATUS_BLOCK io;
3901 status = NtCreateFile( &handle, GENERIC_READ | GENERIC_WRITE | DELETE, attr, &io, NULL, 0,
3902 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN,
3903 FILE_DELETE_ON_CLOSE, NULL, 0 );
3904 if (status == STATUS_SUCCESS) NtClose( handle );
3905 return status;
3909 /******************************************************************************
3910 * NtQueryFullAttributesFile (NTDLL.@)
3912 NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr,
3913 FILE_NETWORK_OPEN_INFORMATION *info )
3915 char *unix_name;
3916 NTSTATUS status;
3918 if (!(status = nt_to_unix_file_name_attr( attr, &unix_name, NULL, FILE_OPEN )))
3920 ULONG attributes;
3921 struct stat st;
3923 if (get_file_info( unix_name, &st, &attributes ) == -1)
3924 status = errno_to_status( errno );
3925 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3926 status = STATUS_INVALID_INFO_CLASS;
3927 else
3929 FILE_BASIC_INFORMATION basic;
3930 FILE_STANDARD_INFORMATION std;
3932 fill_file_info( &st, attributes, &basic, FileBasicInformation );
3933 fill_file_info( &st, attributes, &std, FileStandardInformation );
3935 info->CreationTime = basic.CreationTime;
3936 info->LastAccessTime = basic.LastAccessTime;
3937 info->LastWriteTime = basic.LastWriteTime;
3938 info->ChangeTime = basic.ChangeTime;
3939 info->AllocationSize = std.AllocationSize;
3940 info->EndOfFile = std.EndOfFile;
3941 info->FileAttributes = basic.FileAttributes;
3942 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
3944 free( unix_name );
3946 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
3947 return status;
3951 /******************************************************************************
3952 * NtQueryAttributesFile (NTDLL.@)
3954 NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC_INFORMATION *info )
3956 char *unix_name;
3957 NTSTATUS status;
3959 if (!(status = nt_to_unix_file_name_attr( attr, &unix_name, NULL, FILE_OPEN )))
3961 ULONG attributes;
3962 struct stat st;
3964 if (get_file_info( unix_name, &st, &attributes ) == -1)
3965 status = errno_to_status( errno );
3966 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3967 status = STATUS_INVALID_INFO_CLASS;
3968 else
3970 status = fill_file_info( &st, attributes, info, FileBasicInformation );
3971 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
3973 free( unix_name );
3975 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
3976 return status;
3980 /******************************************************************************
3981 * NtQueryInformationFile (NTDLL.@)
3983 NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
3984 void *ptr, LONG len, FILE_INFORMATION_CLASS class )
3986 static const size_t info_sizes[] =
3989 sizeof(FILE_DIRECTORY_INFORMATION), /* FileDirectoryInformation */
3990 sizeof(FILE_FULL_DIRECTORY_INFORMATION), /* FileFullDirectoryInformation */
3991 sizeof(FILE_BOTH_DIRECTORY_INFORMATION), /* FileBothDirectoryInformation */
3992 sizeof(FILE_BASIC_INFORMATION), /* FileBasicInformation */
3993 sizeof(FILE_STANDARD_INFORMATION), /* FileStandardInformation */
3994 sizeof(FILE_INTERNAL_INFORMATION), /* FileInternalInformation */
3995 sizeof(FILE_EA_INFORMATION), /* FileEaInformation */
3996 0, /* FileAccessInformation */
3997 sizeof(FILE_NAME_INFORMATION), /* FileNameInformation */
3998 sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
3999 0, /* FileLinkInformation */
4000 sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR), /* FileNamesInformation */
4001 sizeof(FILE_DISPOSITION_INFORMATION), /* FileDispositionInformation */
4002 sizeof(FILE_POSITION_INFORMATION), /* FilePositionInformation */
4003 sizeof(FILE_FULL_EA_INFORMATION), /* FileFullEaInformation */
4004 0, /* FileModeInformation */
4005 sizeof(FILE_ALIGNMENT_INFORMATION), /* FileAlignmentInformation */
4006 sizeof(FILE_ALL_INFORMATION), /* FileAllInformation */
4007 sizeof(FILE_ALLOCATION_INFORMATION), /* FileAllocationInformation */
4008 sizeof(FILE_END_OF_FILE_INFORMATION), /* FileEndOfFileInformation */
4009 0, /* FileAlternateNameInformation */
4010 sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
4011 sizeof(FILE_PIPE_INFORMATION), /* FilePipeInformation */
4012 sizeof(FILE_PIPE_LOCAL_INFORMATION), /* FilePipeLocalInformation */
4013 0, /* FilePipeRemoteInformation */
4014 sizeof(FILE_MAILSLOT_QUERY_INFORMATION), /* FileMailslotQueryInformation */
4015 0, /* FileMailslotSetInformation */
4016 0, /* FileCompressionInformation */
4017 0, /* FileObjectIdInformation */
4018 0, /* FileCompletionInformation */
4019 0, /* FileMoveClusterInformation */
4020 0, /* FileQuotaInformation */
4021 0, /* FileReparsePointInformation */
4022 sizeof(FILE_NETWORK_OPEN_INFORMATION), /* FileNetworkOpenInformation */
4023 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), /* FileAttributeTagInformation */
4024 0, /* FileTrackingInformation */
4025 0, /* FileIdBothDirectoryInformation */
4026 0, /* FileIdFullDirectoryInformation */
4027 0, /* FileValidDataLengthInformation */
4028 0, /* FileShortNameInformation */
4029 0, /* FileIoCompletionNotificationInformation, */
4030 0, /* FileIoStatusBlockRangeInformation */
4031 0, /* FileIoPriorityHintInformation */
4032 0, /* FileSfioReserveInformation */
4033 0, /* FileSfioVolumeInformation */
4034 0, /* FileHardLinkInformation */
4035 0, /* FileProcessIdsUsingFileInformation */
4036 0, /* FileNormalizedNameInformation */
4037 0, /* FileNetworkPhysicalNameInformation */
4038 0, /* FileIdGlobalTxDirectoryInformation */
4039 0, /* FileIsRemoteDeviceInformation */
4040 0, /* FileAttributeCacheInformation */
4041 0, /* FileNumaNodeInformation */
4042 0, /* FileStandardLinkInformation */
4043 0, /* FileRemoteProtocolInformation */
4044 0, /* FileRenameInformationBypassAccessCheck */
4045 0, /* FileLinkInformationBypassAccessCheck */
4046 0, /* FileVolumeNameInformation */
4047 sizeof(FILE_ID_INFORMATION), /* FileIdInformation */
4048 0, /* FileIdExtdDirectoryInformation */
4049 0, /* FileReplaceCompletionInformation */
4050 0, /* FileHardLinkFullIdInformation */
4051 0, /* FileIdExtdBothDirectoryInformation */
4054 struct stat st;
4055 int fd, needs_close = FALSE;
4056 ULONG attr;
4057 unsigned int options;
4059 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class);
4061 io->Information = 0;
4063 if (class <= 0 || class >= FileMaximumInformation)
4064 return io->u.Status = STATUS_INVALID_INFO_CLASS;
4065 if (!info_sizes[class])
4066 return server_get_file_info( handle, io, ptr, len, class );
4067 if (len < info_sizes[class])
4068 return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4070 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options )))
4072 if (io->u.Status != STATUS_BAD_DEVICE_TYPE) return io->u.Status;
4073 return server_get_file_info( handle, io, ptr, len, class );
4076 switch (class)
4078 case FileBasicInformation:
4079 if (fd_get_file_info( fd, options, &st, &attr ) == -1)
4080 io->u.Status = errno_to_status( errno );
4081 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4082 io->u.Status = STATUS_INVALID_INFO_CLASS;
4083 else
4084 fill_file_info( &st, attr, ptr, class );
4085 break;
4086 case FileStandardInformation:
4088 FILE_STANDARD_INFORMATION *info = ptr;
4090 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4091 else
4093 fill_file_info( &st, attr, info, class );
4094 info->DeletePending = FALSE; /* FIXME */
4097 break;
4098 case FilePositionInformation:
4100 FILE_POSITION_INFORMATION *info = ptr;
4101 off_t res = lseek( fd, 0, SEEK_CUR );
4102 if (res == (off_t)-1) io->u.Status = errno_to_status( errno );
4103 else info->CurrentByteOffset.QuadPart = res;
4105 break;
4106 case FileInternalInformation:
4107 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4108 else fill_file_info( &st, attr, ptr, class );
4109 break;
4110 case FileEaInformation:
4112 FILE_EA_INFORMATION *info = ptr;
4113 info->EaSize = 0;
4115 break;
4116 case FileEndOfFileInformation:
4117 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4118 else fill_file_info( &st, attr, ptr, class );
4119 break;
4120 case FileAllInformation:
4122 FILE_ALL_INFORMATION *info = ptr;
4123 char *unix_name;
4125 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4126 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4127 io->u.Status = STATUS_INVALID_INFO_CLASS;
4128 else if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4130 LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
4132 fill_file_info( &st, attr, info, FileAllInformation );
4133 info->StandardInformation.DeletePending = FALSE; /* FIXME */
4134 info->EaInformation.EaSize = 0;
4135 info->AccessInformation.AccessFlags = 0; /* FIXME */
4136 info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
4137 info->ModeInformation.Mode = 0; /* FIXME */
4138 info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
4140 io->u.Status = fill_name_info( unix_name, &info->NameInformation, &name_len );
4141 free( unix_name );
4142 io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len;
4145 break;
4146 case FileMailslotQueryInformation:
4148 FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;
4150 SERVER_START_REQ( set_mailslot_info )
4152 req->handle = wine_server_obj_handle( handle );
4153 req->flags = 0;
4154 io->u.Status = wine_server_call( req );
4155 if( io->u.Status == STATUS_SUCCESS )
4157 info->MaximumMessageSize = reply->max_msgsize;
4158 info->MailslotQuota = 0;
4159 info->NextMessageSize = 0;
4160 info->MessagesAvailable = 0;
4161 info->ReadTimeout.QuadPart = reply->read_timeout;
4164 SERVER_END_REQ;
4165 if (!io->u.Status)
4167 char *tmpbuf;
4168 ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
4169 if (size > 0x10000) size = 0x10000;
4170 if ((tmpbuf = malloc( size )))
4172 if (!server_get_unix_fd( handle, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
4174 int res = recv( fd, tmpbuf, size, MSG_PEEK );
4175 info->MessagesAvailable = (res > 0);
4176 info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
4177 if (needs_close) close( fd );
4179 free( tmpbuf );
4183 break;
4184 case FileNameInformation:
4186 FILE_NAME_INFORMATION *info = ptr;
4187 char *unix_name;
4189 if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4191 LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
4192 io->u.Status = fill_name_info( unix_name, info, &name_len );
4193 free( unix_name );
4194 io->Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + name_len;
4197 break;
4198 case FileNetworkOpenInformation:
4200 FILE_NETWORK_OPEN_INFORMATION *info = ptr;
4201 char *unix_name;
4203 if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4205 ULONG attributes;
4206 struct stat st;
4208 if (get_file_info( unix_name, &st, &attributes ) == -1)
4209 io->u.Status = errno_to_status( errno );
4210 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4211 io->u.Status = STATUS_INVALID_INFO_CLASS;
4212 else
4214 FILE_BASIC_INFORMATION basic;
4215 FILE_STANDARD_INFORMATION std;
4217 fill_file_info( &st, attributes, &basic, FileBasicInformation );
4218 fill_file_info( &st, attributes, &std, FileStandardInformation );
4220 info->CreationTime = basic.CreationTime;
4221 info->LastAccessTime = basic.LastAccessTime;
4222 info->LastWriteTime = basic.LastWriteTime;
4223 info->ChangeTime = basic.ChangeTime;
4224 info->AllocationSize = std.AllocationSize;
4225 info->EndOfFile = std.EndOfFile;
4226 info->FileAttributes = basic.FileAttributes;
4228 free( unix_name );
4231 break;
4232 case FileIdInformation:
4233 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4234 else
4236 struct mountmgr_unix_drive drive;
4237 FILE_ID_INFORMATION *info = ptr;
4239 info->VolumeSerialNumber = 0;
4240 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) ))
4241 info->VolumeSerialNumber = drive.serial;
4242 memset( &info->FileId, 0, sizeof(info->FileId) );
4243 *(ULONGLONG *)&info->FileId = st.st_ino;
4245 break;
4246 case FileAttributeTagInformation:
4247 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4248 else
4250 FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
4251 info->FileAttributes = attr;
4252 info->ReparseTag = 0; /* FIXME */
4253 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
4254 info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
4256 break;
4257 default:
4258 FIXME("Unsupported class (%d)\n", class);
4259 io->u.Status = STATUS_NOT_IMPLEMENTED;
4260 break;
4262 if (needs_close) close( fd );
4263 if (io->u.Status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
4264 return io->u.Status;
4268 /******************************************************************************
4269 * NtSetInformationFile (NTDLL.@)
4271 NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4272 void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
4274 int fd, needs_close;
4276 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class );
4278 io->u.Status = STATUS_SUCCESS;
4279 switch (class)
4281 case FileBasicInformation:
4282 if (len >= sizeof(FILE_BASIC_INFORMATION))
4284 struct stat st;
4285 const FILE_BASIC_INFORMATION *info = ptr;
4286 LARGE_INTEGER mtime, atime;
4288 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4289 return io->u.Status;
4291 mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart;
4292 atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
4294 if (atime.QuadPart || mtime.QuadPart)
4295 io->u.Status = set_file_times( fd, &mtime, &atime );
4297 if (io->u.Status == STATUS_SUCCESS && info->FileAttributes)
4299 if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
4300 else
4302 if (info->FileAttributes & FILE_ATTRIBUTE_READONLY)
4304 if (S_ISDIR( st.st_mode))
4305 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
4306 else
4307 st.st_mode &= ~0222; /* clear write permission bits */
4309 else
4311 /* add write permission only where we already have read permission */
4312 st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~start_umask);
4314 if (fchmod( fd, st.st_mode ) == -1) io->u.Status = errno_to_status( errno );
4318 if (needs_close) close( fd );
4320 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4321 break;
4323 case FilePositionInformation:
4324 if (len >= sizeof(FILE_POSITION_INFORMATION))
4326 const FILE_POSITION_INFORMATION *info = ptr;
4328 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4329 return io->u.Status;
4331 if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
4332 io->u.Status = errno_to_status( errno );
4334 if (needs_close) close( fd );
4336 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4337 break;
4339 case FileEndOfFileInformation:
4340 if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
4342 struct stat st;
4343 const FILE_END_OF_FILE_INFORMATION *info = ptr;
4345 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4346 return io->u.Status;
4348 /* first try normal truncate */
4349 if (ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
4351 /* now check for the need to extend the file */
4352 if (fstat( fd, &st ) != -1 && (off_t)info->EndOfFile.QuadPart > st.st_size)
4354 static const char zero;
4356 /* extend the file one byte beyond the requested size and then truncate it */
4357 /* this should work around ftruncate implementations that can't extend files */
4358 if (pwrite( fd, &zero, 1, (off_t)info->EndOfFile.QuadPart ) != -1 &&
4359 ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
4361 io->u.Status = errno_to_status( errno );
4363 if (needs_close) close( fd );
4365 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4366 break;
4368 case FilePipeInformation:
4369 if (len >= sizeof(FILE_PIPE_INFORMATION))
4371 FILE_PIPE_INFORMATION *info = ptr;
4373 if ((info->CompletionMode | info->ReadMode) & ~1)
4375 io->u.Status = STATUS_INVALID_PARAMETER;
4376 break;
4379 SERVER_START_REQ( set_named_pipe_info )
4381 req->handle = wine_server_obj_handle( handle );
4382 req->flags = (info->CompletionMode ? NAMED_PIPE_NONBLOCKING_MODE : 0) |
4383 (info->ReadMode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0);
4384 io->u.Status = wine_server_call( req );
4386 SERVER_END_REQ;
4388 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4389 break;
4391 case FileMailslotSetInformation:
4393 FILE_MAILSLOT_SET_INFORMATION *info = ptr;
4395 SERVER_START_REQ( set_mailslot_info )
4397 req->handle = wine_server_obj_handle( handle );
4398 req->flags = MAILSLOT_SET_READ_TIMEOUT;
4399 req->read_timeout = info->ReadTimeout.QuadPart;
4400 io->u.Status = wine_server_call( req );
4402 SERVER_END_REQ;
4404 break;
4406 case FileCompletionInformation:
4407 if (len >= sizeof(FILE_COMPLETION_INFORMATION))
4409 FILE_COMPLETION_INFORMATION *info = ptr;
4411 SERVER_START_REQ( set_completion_info )
4413 req->handle = wine_server_obj_handle( handle );
4414 req->chandle = wine_server_obj_handle( info->CompletionPort );
4415 req->ckey = info->CompletionKey;
4416 io->u.Status = wine_server_call( req );
4418 SERVER_END_REQ;
4419 } else
4420 io->u.Status = STATUS_INVALID_PARAMETER_3;
4421 break;
4423 case FileIoCompletionNotificationInformation:
4424 if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION))
4426 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr;
4428 if (info->Flags & FILE_SKIP_SET_USER_EVENT_ON_FAST_IO)
4429 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4431 SERVER_START_REQ( set_fd_completion_mode )
4433 req->handle = wine_server_obj_handle( handle );
4434 req->flags = info->Flags;
4435 io->u.Status = wine_server_call( req );
4437 SERVER_END_REQ;
4438 } else
4439 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4440 break;
4442 case FileIoPriorityHintInformation:
4443 if (len >= sizeof(FILE_IO_PRIORITY_HINT_INFO))
4445 FILE_IO_PRIORITY_HINT_INFO *info = ptr;
4446 if (info->PriorityHint < MaximumIoPriorityHintType)
4447 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info->PriorityHint );
4448 else
4449 io->u.Status = STATUS_INVALID_PARAMETER;
4451 else io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4452 break;
4454 case FileAllInformation:
4455 io->u.Status = STATUS_INVALID_INFO_CLASS;
4456 break;
4458 case FileValidDataLengthInformation:
4459 if (len >= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION))
4461 struct stat st;
4462 const FILE_VALID_DATA_LENGTH_INFORMATION *info = ptr;
4464 if ((io->u.Status = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL )))
4465 return io->u.Status;
4467 if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
4468 else if (info->ValidDataLength.QuadPart <= 0 || (off_t)info->ValidDataLength.QuadPart > st.st_size)
4469 io->u.Status = STATUS_INVALID_PARAMETER;
4470 else
4472 #ifdef HAVE_FALLOCATE
4473 if (fallocate( fd, 0, 0, (off_t)info->ValidDataLength.QuadPart ) == -1)
4475 NTSTATUS status = errno_to_status( errno );
4476 if (status == STATUS_NOT_SUPPORTED) WARN( "fallocate not supported on this filesystem\n" );
4477 else io->u.Status = status;
4479 #else
4480 FIXME( "setting valid data length not supported\n" );
4481 #endif
4483 if (needs_close) close( fd );
4485 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4486 break;
4488 case FileDispositionInformation:
4489 if (len >= sizeof(FILE_DISPOSITION_INFORMATION))
4491 FILE_DISPOSITION_INFORMATION *info = ptr;
4493 SERVER_START_REQ( set_fd_disp_info )
4495 req->handle = wine_server_obj_handle( handle );
4496 req->unlink = info->DoDeleteFile;
4497 io->u.Status = wine_server_call( req );
4499 SERVER_END_REQ;
4500 } else
4501 io->u.Status = STATUS_INVALID_PARAMETER_3;
4502 break;
4504 case FileRenameInformation:
4505 if (len >= sizeof(FILE_RENAME_INFORMATION))
4507 FILE_RENAME_INFORMATION *info = ptr;
4508 UNICODE_STRING name_str, nt_name = { 0 };
4509 OBJECT_ATTRIBUTES attr;
4510 char *unix_name;
4512 name_str.Buffer = info->FileName;
4513 name_str.Length = info->FileNameLength;
4514 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4516 attr.Length = sizeof(attr);
4517 attr.ObjectName = &name_str;
4518 attr.RootDirectory = info->RootDirectory;
4519 attr.Attributes = OBJ_CASE_INSENSITIVE;
4521 io->u.Status = nt_to_unix_file_name_attr( &attr, &unix_name, &nt_name, FILE_OPEN_IF );
4522 if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
4523 break;
4525 SERVER_START_REQ( set_fd_name_info )
4527 req->handle = wine_server_obj_handle( handle );
4528 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4529 req->namelen = nt_name.Length;
4530 req->link = FALSE;
4531 req->replace = info->ReplaceIfExists;
4532 wine_server_add_data( req, nt_name.Buffer, nt_name.Length );
4533 wine_server_add_data( req, unix_name, strlen(unix_name) );
4534 io->u.Status = wine_server_call( req );
4536 SERVER_END_REQ;
4538 free( unix_name );
4539 free( nt_name.Buffer );
4541 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4542 break;
4544 case FileLinkInformation:
4545 if (len >= sizeof(FILE_LINK_INFORMATION))
4547 FILE_LINK_INFORMATION *info = ptr;
4548 UNICODE_STRING name_str, nt_name = { 0 };
4549 OBJECT_ATTRIBUTES attr;
4550 char *unix_name;
4552 name_str.Buffer = info->FileName;
4553 name_str.Length = info->FileNameLength;
4554 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4556 attr.Length = sizeof(attr);
4557 attr.ObjectName = &name_str;
4558 attr.RootDirectory = info->RootDirectory;
4559 attr.Attributes = OBJ_CASE_INSENSITIVE;
4561 io->u.Status = nt_to_unix_file_name_attr( &attr, &unix_name, &nt_name, FILE_OPEN_IF );
4562 if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
4563 break;
4565 SERVER_START_REQ( set_fd_name_info )
4567 req->handle = wine_server_obj_handle( handle );
4568 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4569 req->namelen = nt_name.Length;
4570 req->link = TRUE;
4571 req->replace = info->ReplaceIfExists;
4572 wine_server_add_data( req, nt_name.Buffer, nt_name.Length );
4573 wine_server_add_data( req, unix_name, strlen(unix_name) );
4574 io->u.Status = wine_server_call( req );
4576 SERVER_END_REQ;
4578 free( unix_name );
4579 free( nt_name.Buffer );
4581 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4582 break;
4584 default:
4585 FIXME("Unsupported class (%d)\n", class);
4586 io->u.Status = STATUS_NOT_IMPLEMENTED;
4587 break;
4589 io->Information = 0;
4590 return io->u.Status;
4594 /***********************************************************************
4595 * Asynchronous file I/O *
4598 typedef NTSTATUS async_callback_t( void *user, IO_STATUS_BLOCK *io, NTSTATUS status );
4600 struct async_fileio
4602 async_callback_t *callback; /* must be the first field */
4603 struct async_fileio *next;
4604 HANDLE handle;
4607 struct async_fileio_read
4609 struct async_fileio io;
4610 char *buffer;
4611 unsigned int already;
4612 unsigned int count;
4613 BOOL avail_mode;
4616 struct async_fileio_write
4618 struct async_fileio io;
4619 const char *buffer;
4620 unsigned int already;
4621 unsigned int count;
4624 struct async_fileio_read_changes
4626 struct async_fileio io;
4627 void *buffer;
4628 ULONG buffer_size;
4629 ULONG data_size;
4630 char data[1];
4633 struct async_irp
4635 struct async_fileio io;
4636 void *buffer; /* buffer for output */
4637 ULONG size; /* size of buffer */
4640 static struct async_fileio *fileio_freelist;
4642 static void release_fileio( struct async_fileio *io )
4644 for (;;)
4646 struct async_fileio *next = fileio_freelist;
4647 io->next = next;
4648 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist, io, next ) == next) return;
4652 static struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle )
4654 /* first free remaining previous fileinfos */
4655 struct async_fileio *io = InterlockedExchangePointer( (void **)&fileio_freelist, NULL );
4657 while (io)
4659 struct async_fileio *next = io->next;
4660 free( io );
4661 io = next;
4664 if ((io = malloc( size )))
4666 io->callback = callback;
4667 io->handle = handle;
4669 return io;
4672 static async_data_t server_async( HANDLE handle, struct async_fileio *user, HANDLE event,
4673 PIO_APC_ROUTINE apc, void *apc_context, IO_STATUS_BLOCK *io )
4675 async_data_t async;
4676 async.handle = wine_server_obj_handle( handle );
4677 async.user = wine_server_client_ptr( user );
4678 async.iosb = wine_server_client_ptr( io );
4679 async.event = wine_server_obj_handle( event );
4680 async.apc = wine_server_client_ptr( apc );
4681 async.apc_context = wine_server_client_ptr( apc_context );
4682 return async;
4685 static NTSTATUS wait_async( HANDLE handle, BOOL alertable, IO_STATUS_BLOCK *io )
4687 if (NtWaitForSingleObject( handle, alertable, NULL )) return STATUS_PENDING;
4688 return io->u.Status;
4691 /* callback for irp async I/O completion */
4692 static NTSTATUS irp_completion( void *user, IO_STATUS_BLOCK *io, NTSTATUS status )
4694 struct async_irp *async = user;
4695 ULONG information = 0;
4697 if (status == STATUS_ALERTED)
4699 SERVER_START_REQ( get_async_result )
4701 req->user_arg = wine_server_client_ptr( async );
4702 wine_server_set_reply( req, async->buffer, async->size );
4703 status = virtual_locked_server_call( req );
4704 information = reply->size;
4706 SERVER_END_REQ;
4708 if (status != STATUS_PENDING)
4710 io->u.Status = status;
4711 io->Information = information;
4712 release_fileio( &async->io );
4714 return status;
4717 static NTSTATUS async_read_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
4719 struct async_fileio_read *fileio = user;
4720 int fd, needs_close, result;
4722 switch (status)
4724 case STATUS_ALERTED: /* got some new data */
4725 /* check to see if the data is ready (non-blocking) */
4726 if ((status = server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd,
4727 &needs_close, NULL, NULL )))
4728 break;
4730 result = virtual_locked_read(fd, &fileio->buffer[fileio->already], fileio->count-fileio->already);
4731 if (needs_close) close( fd );
4733 if (result < 0)
4735 if (errno == EAGAIN || errno == EINTR)
4736 status = STATUS_PENDING;
4737 else /* check to see if the transfer is complete */
4738 status = errno_to_status( errno );
4740 else if (result == 0)
4742 status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN;
4744 else
4746 fileio->already += result;
4747 if (fileio->already >= fileio->count || fileio->avail_mode)
4748 status = STATUS_SUCCESS;
4749 else
4750 status = STATUS_PENDING;
4752 break;
4754 case STATUS_TIMEOUT:
4755 case STATUS_IO_TIMEOUT:
4756 if (fileio->already) status = STATUS_SUCCESS;
4757 break;
4759 if (status != STATUS_PENDING)
4761 iosb->u.Status = status;
4762 iosb->Information = fileio->already;
4763 release_fileio( &fileio->io );
4765 return status;
4768 static NTSTATUS async_write_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
4770 struct async_fileio_write *fileio = user;
4771 int result, fd, needs_close;
4772 enum server_fd_type type;
4774 switch (status)
4776 case STATUS_ALERTED:
4777 /* write some data (non-blocking) */
4778 if ((status = server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd,
4779 &needs_close, &type, NULL )))
4780 break;
4782 if (!fileio->count && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET))
4783 result = send( fd, fileio->buffer, 0, 0 );
4784 else
4785 result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already );
4787 if (needs_close) close( fd );
4789 if (result < 0)
4791 if (errno == EAGAIN || errno == EINTR) status = STATUS_PENDING;
4792 else status = errno_to_status( errno );
4794 else
4796 fileio->already += result;
4797 status = (fileio->already < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS;
4799 break;
4801 case STATUS_TIMEOUT:
4802 case STATUS_IO_TIMEOUT:
4803 if (fileio->already) status = STATUS_SUCCESS;
4804 break;
4806 if (status != STATUS_PENDING)
4808 iosb->u.Status = status;
4809 iosb->Information = fileio->already;
4810 release_fileio( &fileio->io );
4812 return status;
4815 /* do a read call through the server */
4816 static NTSTATUS server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
4817 IO_STATUS_BLOCK *io, void *buffer, ULONG size,
4818 LARGE_INTEGER *offset, ULONG *key )
4820 struct async_irp *async;
4821 NTSTATUS status;
4822 HANDLE wait_handle;
4823 ULONG options;
4825 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4826 return STATUS_NO_MEMORY;
4828 async->buffer = buffer;
4829 async->size = size;
4831 SERVER_START_REQ( read )
4833 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4834 req->pos = offset ? offset->QuadPart : 0;
4835 wine_server_set_reply( req, buffer, size );
4836 status = virtual_locked_server_call( req );
4837 wait_handle = wine_server_ptr_handle( reply->wait );
4838 options = reply->options;
4839 if (wait_handle && status != STATUS_PENDING)
4841 io->u.Status = status;
4842 io->Information = wine_server_reply_size( reply );
4845 SERVER_END_REQ;
4847 if (status != STATUS_PENDING) free( async );
4849 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io );
4850 return status;
4853 /* do a write call through the server */
4854 static NTSTATUS server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
4855 IO_STATUS_BLOCK *io, const void *buffer, ULONG size,
4856 LARGE_INTEGER *offset, ULONG *key )
4858 struct async_irp *async;
4859 NTSTATUS status;
4860 HANDLE wait_handle;
4861 ULONG options;
4863 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4864 return STATUS_NO_MEMORY;
4866 async->buffer = NULL;
4867 async->size = 0;
4869 SERVER_START_REQ( write )
4871 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4872 req->pos = offset ? offset->QuadPart : 0;
4873 wine_server_add_data( req, buffer, size );
4874 status = wine_server_call( req );
4875 wait_handle = wine_server_ptr_handle( reply->wait );
4876 options = reply->options;
4877 if (wait_handle && status != STATUS_PENDING)
4879 io->u.Status = status;
4880 io->Information = reply->size;
4883 SERVER_END_REQ;
4885 if (status != STATUS_PENDING) free( async );
4887 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io );
4888 return status;
4891 /* do an ioctl call through the server */
4892 static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
4893 PIO_APC_ROUTINE apc, PVOID apc_context,
4894 IO_STATUS_BLOCK *io, ULONG code,
4895 const void *in_buffer, ULONG in_size,
4896 PVOID out_buffer, ULONG out_size )
4898 struct async_irp *async;
4899 NTSTATUS status;
4900 HANDLE wait_handle;
4901 ULONG options;
4903 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4904 return STATUS_NO_MEMORY;
4905 async->buffer = out_buffer;
4906 async->size = out_size;
4908 SERVER_START_REQ( ioctl )
4910 req->code = code;
4911 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4912 wine_server_add_data( req, in_buffer, in_size );
4913 if ((code & 3) != METHOD_BUFFERED) wine_server_add_data( req, out_buffer, out_size );
4914 wine_server_set_reply( req, out_buffer, out_size );
4915 status = virtual_locked_server_call( req );
4916 wait_handle = wine_server_ptr_handle( reply->wait );
4917 options = reply->options;
4918 if (wait_handle && status != STATUS_PENDING)
4920 io->u.Status = status;
4921 io->Information = wine_server_reply_size( reply );
4924 SERVER_END_REQ;
4926 if (status == STATUS_NOT_SUPPORTED)
4927 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
4928 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
4930 if (status != STATUS_PENDING) free( async );
4932 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io );
4933 return status;
4937 struct io_timeouts
4939 int interval; /* max interval between two bytes */
4940 int total; /* total timeout for the whole operation */
4941 int end_time; /* absolute time of end of operation */
4944 /* retrieve the I/O timeouts to use for a given handle */
4945 static NTSTATUS get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read,
4946 struct io_timeouts *timeouts )
4948 NTSTATUS status = STATUS_SUCCESS;
4950 timeouts->interval = timeouts->total = -1;
4952 switch(type)
4954 case FD_TYPE_SERIAL:
4956 /* GetCommTimeouts */
4957 SERIAL_TIMEOUTS st;
4958 IO_STATUS_BLOCK io;
4960 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
4961 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
4962 if (status) break;
4964 if (is_read)
4966 if (st.ReadIntervalTimeout)
4967 timeouts->interval = st.ReadIntervalTimeout;
4969 if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant)
4971 timeouts->total = st.ReadTotalTimeoutConstant;
4972 if (st.ReadTotalTimeoutMultiplier != MAXDWORD)
4973 timeouts->total += count * st.ReadTotalTimeoutMultiplier;
4975 else if (st.ReadIntervalTimeout == MAXDWORD)
4976 timeouts->interval = timeouts->total = 0;
4978 else /* write */
4980 if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant)
4982 timeouts->total = st.WriteTotalTimeoutConstant;
4983 if (st.WriteTotalTimeoutMultiplier != MAXDWORD)
4984 timeouts->total += count * st.WriteTotalTimeoutMultiplier;
4987 break;
4989 case FD_TYPE_MAILSLOT:
4990 if (is_read)
4992 timeouts->interval = 0; /* return as soon as we got something */
4993 SERVER_START_REQ( set_mailslot_info )
4995 req->handle = wine_server_obj_handle( handle );
4996 req->flags = 0;
4997 if (!(status = wine_server_call( req )) &&
4998 reply->read_timeout != TIMEOUT_INFINITE)
4999 timeouts->total = reply->read_timeout / -10000;
5001 SERVER_END_REQ;
5003 break;
5004 case FD_TYPE_SOCKET:
5005 case FD_TYPE_CHAR:
5006 if (is_read) timeouts->interval = 0; /* return as soon as we got something */
5007 break;
5008 default:
5009 break;
5011 if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total;
5012 return STATUS_SUCCESS;
5016 /* retrieve the timeout for the next wait, in milliseconds */
5017 static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already )
5019 int ret = -1;
5021 if (timeouts->total != -1)
5023 ret = timeouts->end_time - NtGetTickCount();
5024 if (ret < 0) ret = 0;
5026 if (already && timeouts->interval != -1)
5028 if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval;
5030 return ret;
5034 /* retrieve the avail_mode flag for async reads */
5035 static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode )
5037 NTSTATUS status = STATUS_SUCCESS;
5039 switch(type)
5041 case FD_TYPE_SERIAL:
5043 /* GetCommTimeouts */
5044 SERIAL_TIMEOUTS st;
5045 IO_STATUS_BLOCK io;
5047 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
5048 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
5049 if (status) break;
5050 *avail_mode = (!st.ReadTotalTimeoutMultiplier &&
5051 !st.ReadTotalTimeoutConstant &&
5052 st.ReadIntervalTimeout == MAXDWORD);
5053 break;
5055 case FD_TYPE_MAILSLOT:
5056 case FD_TYPE_SOCKET:
5057 case FD_TYPE_CHAR:
5058 *avail_mode = TRUE;
5059 break;
5060 default:
5061 *avail_mode = FALSE;
5062 break;
5064 return status;
5067 /* register an async I/O for a file read; helper for NtReadFile */
5068 static NTSTATUS register_async_file_read( HANDLE handle, HANDLE event,
5069 PIO_APC_ROUTINE apc, void *apc_user,
5070 IO_STATUS_BLOCK *iosb, void *buffer,
5071 ULONG already, ULONG length, BOOL avail_mode )
5073 struct async_fileio_read *fileio;
5074 NTSTATUS status;
5076 if (!(fileio = (struct async_fileio_read *)alloc_fileio( sizeof(*fileio), async_read_proc, handle )))
5077 return STATUS_NO_MEMORY;
5079 fileio->already = already;
5080 fileio->count = length;
5081 fileio->buffer = buffer;
5082 fileio->avail_mode = avail_mode;
5084 SERVER_START_REQ( register_async )
5086 req->type = ASYNC_TYPE_READ;
5087 req->count = length;
5088 req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb );
5089 status = wine_server_call( req );
5091 SERVER_END_REQ;
5093 if (status != STATUS_PENDING) free( fileio );
5094 return status;
5097 static void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async )
5099 SERVER_START_REQ( add_fd_completion )
5101 req->handle = wine_server_obj_handle( handle );
5102 req->cvalue = value;
5103 req->status = status;
5104 req->information = info;
5105 req->async = async;
5106 wine_server_call( req );
5108 SERVER_END_REQ;
5111 static NTSTATUS set_pending_write( HANDLE device )
5113 NTSTATUS status;
5115 SERVER_START_REQ( set_serial_info )
5117 req->handle = wine_server_obj_handle( device );
5118 req->flags = SERIALINFO_PENDING_WRITE;
5119 status = wine_server_call( req );
5121 SERVER_END_REQ;
5122 return status;
5126 /******************************************************************************
5127 * NtReadFile (NTDLL.@)
5129 NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5130 IO_STATUS_BLOCK *io, void *buffer, ULONG length,
5131 LARGE_INTEGER *offset, ULONG *key )
5133 int result, unix_handle, needs_close;
5134 unsigned int options;
5135 struct io_timeouts timeouts;
5136 NTSTATUS status, ret_status;
5137 ULONG total = 0;
5138 enum server_fd_type type;
5139 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5140 BOOL send_completion = FALSE, async_read, timeout_init_done = FALSE;
5142 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5143 handle, event, apc, apc_user, io, buffer, length, offset, key );
5145 if (!io) return STATUS_ACCESS_VIOLATION;
5147 status = server_get_unix_fd( handle, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5148 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5150 if (!virtual_check_buffer_for_write( buffer, length )) return STATUS_ACCESS_VIOLATION;
5152 if (status == STATUS_BAD_DEVICE_TYPE)
5153 return server_read_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5155 async_read = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5157 if (type == FD_TYPE_FILE)
5159 if (async_read && (!offset || offset->QuadPart < 0))
5161 status = STATUS_INVALID_PARAMETER;
5162 goto done;
5165 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5167 /* async I/O doesn't make sense on regular files */
5168 while ((result = virtual_locked_pread( unix_handle, buffer, length, offset->QuadPart )) == -1)
5170 if (errno != EINTR)
5172 status = errno_to_status( errno );
5173 goto done;
5176 if (!async_read) /* update file pointer position */
5177 lseek( unix_handle, offset->QuadPart + result, SEEK_SET );
5179 total = result;
5180 status = (total || !length) ? STATUS_SUCCESS : STATUS_END_OF_FILE;
5181 goto done;
5184 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5186 if (async_read && (!offset || offset->QuadPart < 0))
5188 status = STATUS_INVALID_PARAMETER;
5189 goto done;
5193 if (type == FD_TYPE_SERIAL && async_read && length)
5195 /* an asynchronous serial port read with a read interval timeout needs to
5196 skip the synchronous read to make sure that the server starts the read
5197 interval timer after the first read */
5198 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5199 if (timeouts.interval)
5201 status = register_async_file_read( handle, event, apc, apc_user, io,
5202 buffer, total, length, FALSE );
5203 goto err;
5207 for (;;)
5209 if ((result = virtual_locked_read( unix_handle, (char *)buffer + total, length - total )) >= 0)
5211 total += result;
5212 if (!result || total == length)
5214 if (total)
5216 status = STATUS_SUCCESS;
5217 goto done;
5219 switch (type)
5221 case FD_TYPE_FILE:
5222 case FD_TYPE_CHAR:
5223 case FD_TYPE_DEVICE:
5224 status = length ? STATUS_END_OF_FILE : STATUS_SUCCESS;
5225 goto done;
5226 case FD_TYPE_SERIAL:
5227 if (!length)
5229 status = STATUS_SUCCESS;
5230 goto done;
5232 break;
5233 default:
5234 status = STATUS_PIPE_BROKEN;
5235 goto err;
5238 else if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5240 else if (errno != EAGAIN)
5242 if (errno == EINTR) continue;
5243 if (!total) status = errno_to_status( errno );
5244 goto err;
5247 if (async_read)
5249 BOOL avail_mode;
5251 if ((status = get_io_avail_mode( handle, type, &avail_mode ))) goto err;
5252 if (total && avail_mode)
5254 status = STATUS_SUCCESS;
5255 goto done;
5257 status = register_async_file_read( handle, event, apc, apc_user, io,
5258 buffer, total, length, avail_mode );
5259 goto err;
5261 else /* synchronous read, wait for the fd to become ready */
5263 struct pollfd pfd;
5264 int ret, timeout;
5266 if (!timeout_init_done)
5268 timeout_init_done = TRUE;
5269 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5270 if (event) NtResetEvent( event, NULL );
5272 timeout = get_next_io_timeout( &timeouts, total );
5274 pfd.fd = unix_handle;
5275 pfd.events = POLLIN;
5277 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5279 if (total) /* return with what we got so far */
5280 status = STATUS_SUCCESS;
5281 else
5282 status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT;
5283 goto done;
5285 if (ret == -1 && errno != EINTR)
5287 status = errno_to_status( errno );
5288 goto done;
5290 /* will now restart the read */
5294 done:
5295 send_completion = cvalue != 0;
5297 err:
5298 if (needs_close) close( unix_handle );
5299 if (status == STATUS_SUCCESS || (status == STATUS_END_OF_FILE && (!async_read || type == FD_TYPE_FILE)))
5301 io->u.Status = status;
5302 io->Information = total;
5303 TRACE("= SUCCESS (%u)\n", total);
5304 if (event) NtSetEvent( event, NULL );
5305 if (apc && (!status || async_read)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5306 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5308 else
5310 TRACE("= 0x%08x\n", status);
5311 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5314 ret_status = async_read && type == FD_TYPE_FILE && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE)
5315 ? STATUS_PENDING : status;
5317 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5318 return ret_status;
5322 /******************************************************************************
5323 * NtReadFileScatter (NTDLL.@)
5325 NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5326 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5327 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5329 int result, unix_handle, needs_close;
5330 unsigned int options;
5331 NTSTATUS status;
5332 ULONG pos = 0, total = 0;
5333 enum server_fd_type type;
5334 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5335 BOOL send_completion = FALSE;
5337 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5338 file, event, apc, apc_user, io, segments, length, offset, key );
5340 if (!io) return STATUS_ACCESS_VIOLATION;
5342 status = server_get_unix_fd( file, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5343 if (status) return status;
5345 if ((type != FD_TYPE_FILE) ||
5346 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5347 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5349 status = STATUS_INVALID_PARAMETER;
5350 goto error;
5353 while (length)
5355 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5356 result = pread( unix_handle, (char *)segments->Buffer + pos,
5357 min( length - pos, page_size - pos ), offset->QuadPart + total );
5358 else
5359 result = read( unix_handle, (char *)segments->Buffer + pos, min( length - pos, page_size - pos ) );
5361 if (result == -1)
5363 if (errno == EINTR) continue;
5364 status = errno_to_status( errno );
5365 break;
5367 if (!result) break;
5368 total += result;
5369 length -= result;
5370 if ((pos += result) == page_size)
5372 pos = 0;
5373 segments++;
5377 if (total == 0) status = STATUS_END_OF_FILE;
5379 send_completion = cvalue != 0;
5381 if (needs_close) close( unix_handle );
5382 io->u.Status = status;
5383 io->Information = total;
5384 TRACE("= 0x%08x (%u)\n", status, total);
5385 if (event) NtSetEvent( event, NULL );
5386 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5387 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5388 if (send_completion) add_completion( file, cvalue, status, total, TRUE );
5390 return STATUS_PENDING;
5392 error:
5393 if (needs_close) close( unix_handle );
5394 if (event) NtResetEvent( event, NULL );
5395 TRACE("= 0x%08x\n", status);
5396 return status;
5400 /******************************************************************************
5401 * NtWriteFile (NTDLL.@)
5403 NTSTATUS WINAPI NtWriteFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5404 IO_STATUS_BLOCK *io, const void *buffer, ULONG length,
5405 LARGE_INTEGER *offset, ULONG *key )
5407 int result, unix_handle, needs_close;
5408 unsigned int options;
5409 struct io_timeouts timeouts;
5410 NTSTATUS status, ret_status;
5411 ULONG total = 0;
5412 enum server_fd_type type;
5413 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5414 BOOL send_completion = FALSE, async_write, append_write = FALSE, timeout_init_done = FALSE;
5415 LARGE_INTEGER offset_eof;
5417 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5418 handle, event, apc, apc_user, io, buffer, length, offset, key );
5420 if (!io) return STATUS_ACCESS_VIOLATION;
5422 status = server_get_unix_fd( handle, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5423 if (status == STATUS_ACCESS_DENIED)
5425 status = server_get_unix_fd( handle, FILE_APPEND_DATA, &unix_handle,
5426 &needs_close, &type, &options );
5427 append_write = TRUE;
5429 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5431 async_write = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5433 if (!virtual_check_buffer_for_read( buffer, length ))
5435 status = STATUS_INVALID_USER_BUFFER;
5436 goto done;
5439 if (status == STATUS_BAD_DEVICE_TYPE)
5440 return server_write_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5442 if (type == FD_TYPE_FILE)
5444 if (async_write &&
5445 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5447 status = STATUS_INVALID_PARAMETER;
5448 goto done;
5451 if (append_write)
5453 offset_eof.QuadPart = FILE_WRITE_TO_END_OF_FILE;
5454 offset = &offset_eof;
5457 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5459 off_t off = offset->QuadPart;
5461 if (offset->QuadPart == FILE_WRITE_TO_END_OF_FILE)
5463 struct stat st;
5465 if (fstat( unix_handle, &st ) == -1)
5467 status = errno_to_status( errno );
5468 goto done;
5470 off = st.st_size;
5472 else if (offset->QuadPart < 0)
5474 status = STATUS_INVALID_PARAMETER;
5475 goto done;
5478 /* async I/O doesn't make sense on regular files */
5479 while ((result = pwrite( unix_handle, buffer, length, off )) == -1)
5481 if (errno != EINTR)
5483 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5484 else status = errno_to_status( errno );
5485 goto done;
5489 if (!async_write) /* update file pointer position */
5490 lseek( unix_handle, off + result, SEEK_SET );
5492 total = result;
5493 status = STATUS_SUCCESS;
5494 goto done;
5497 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5499 if (async_write &&
5500 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5502 status = STATUS_INVALID_PARAMETER;
5503 goto done;
5507 for (;;)
5509 /* zero-length writes on sockets may not work with plain write(2) */
5510 if (!length && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET))
5511 result = send( unix_handle, buffer, 0, 0 );
5512 else
5513 result = write( unix_handle, (const char *)buffer + total, length - total );
5515 if (result >= 0)
5517 total += result;
5518 if (total == length)
5520 status = STATUS_SUCCESS;
5521 goto done;
5523 if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5525 else if (errno != EAGAIN)
5527 if (errno == EINTR) continue;
5528 if (!total)
5530 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5531 else status = errno_to_status( errno );
5533 goto err;
5536 if (async_write)
5538 struct async_fileio_write *fileio;
5540 fileio = (struct async_fileio_write *)alloc_fileio( sizeof(*fileio), async_write_proc, handle );
5541 if (!fileio)
5543 status = STATUS_NO_MEMORY;
5544 goto err;
5546 fileio->already = total;
5547 fileio->count = length;
5548 fileio->buffer = buffer;
5550 SERVER_START_REQ( register_async )
5552 req->type = ASYNC_TYPE_WRITE;
5553 req->count = length;
5554 req->async = server_async( handle, &fileio->io, event, apc, apc_user, io );
5555 status = wine_server_call( req );
5557 SERVER_END_REQ;
5559 if (status != STATUS_PENDING) free( fileio );
5560 goto err;
5562 else /* synchronous write, wait for the fd to become ready */
5564 struct pollfd pfd;
5565 int ret, timeout;
5567 if (!timeout_init_done)
5569 timeout_init_done = TRUE;
5570 if ((status = get_io_timeouts( handle, type, length, FALSE, &timeouts )))
5571 goto err;
5572 if (event) NtResetEvent( event, NULL );
5574 timeout = get_next_io_timeout( &timeouts, total );
5576 pfd.fd = unix_handle;
5577 pfd.events = POLLOUT;
5579 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5581 /* return with what we got so far */
5582 status = total ? STATUS_SUCCESS : STATUS_TIMEOUT;
5583 goto done;
5585 if (ret == -1 && errno != EINTR)
5587 status = errno_to_status( errno );
5588 goto done;
5590 /* will now restart the write */
5594 done:
5595 send_completion = cvalue != 0;
5597 err:
5598 if (needs_close) close( unix_handle );
5600 if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING))
5601 set_pending_write( handle );
5603 if (status == STATUS_SUCCESS)
5605 io->u.Status = status;
5606 io->Information = total;
5607 TRACE("= SUCCESS (%u)\n", total);
5608 if (event) NtSetEvent( event, NULL );
5609 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5610 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5612 else
5614 TRACE("= 0x%08x\n", status);
5615 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5618 ret_status = async_write && type == FD_TYPE_FILE && status == STATUS_SUCCESS ? STATUS_PENDING : status;
5619 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5620 return ret_status;
5624 /******************************************************************************
5625 * NtWriteFileGather (NTDLL.@)
5627 NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5628 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5629 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5631 int result, unix_handle, needs_close;
5632 unsigned int options;
5633 NTSTATUS status;
5634 ULONG pos = 0, total = 0;
5635 enum server_fd_type type;
5636 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5637 BOOL send_completion = FALSE;
5639 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5640 file, event, apc, apc_user, io, segments, length, offset, key );
5642 if (length % page_size) return STATUS_INVALID_PARAMETER;
5643 if (!io) return STATUS_ACCESS_VIOLATION;
5645 status = server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5646 if (status) return status;
5648 if ((type != FD_TYPE_FILE) ||
5649 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5650 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5652 status = STATUS_INVALID_PARAMETER;
5653 goto done;
5656 while (length)
5658 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5659 result = pwrite( unix_handle, (char *)segments->Buffer + pos,
5660 page_size - pos, offset->QuadPart + total );
5661 else
5662 result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos );
5664 if (result == -1)
5666 if (errno == EINTR) continue;
5667 if (errno == EFAULT)
5669 status = STATUS_INVALID_USER_BUFFER;
5670 goto done;
5672 status = errno_to_status( errno );
5673 break;
5675 if (!result)
5677 status = STATUS_DISK_FULL;
5678 break;
5680 total += result;
5681 length -= result;
5682 if ((pos += result) == page_size)
5684 pos = 0;
5685 segments++;
5689 send_completion = cvalue != 0;
5691 done:
5692 if (needs_close) close( unix_handle );
5693 if (status == STATUS_SUCCESS)
5695 io->u.Status = status;
5696 io->Information = total;
5697 TRACE("= SUCCESS (%u)\n", total);
5698 if (event) NtSetEvent( event, NULL );
5699 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5700 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5702 else
5704 TRACE("= 0x%08x\n", status);
5705 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5707 if (send_completion) add_completion( file, cvalue, status, total, FALSE );
5708 return status;
5712 /******************************************************************************
5713 * NtDeviceIoControlFile (NTDLL.@)
5715 NTSTATUS WINAPI NtDeviceIoControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5716 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5717 void *out_buffer, ULONG out_size )
5719 ULONG device = (code >> 16);
5720 NTSTATUS status = STATUS_NOT_SUPPORTED;
5722 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5723 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5725 switch (device)
5727 case FILE_DEVICE_DISK:
5728 case FILE_DEVICE_CD_ROM:
5729 case FILE_DEVICE_DVD:
5730 case FILE_DEVICE_CONTROLLER:
5731 case FILE_DEVICE_MASS_STORAGE:
5732 status = cdrom_DeviceIoControl( handle, event, apc, apc_context, io, code,
5733 in_buffer, in_size, out_buffer, out_size );
5734 break;
5735 case FILE_DEVICE_SERIAL_PORT:
5736 status = serial_DeviceIoControl( handle, event, apc, apc_context, io, code,
5737 in_buffer, in_size, out_buffer, out_size );
5738 break;
5739 case FILE_DEVICE_TAPE:
5740 status = tape_DeviceIoControl( handle, event, apc, apc_context, io, code,
5741 in_buffer, in_size, out_buffer, out_size );
5742 break;
5745 if (status == STATUS_NOT_SUPPORTED || status == STATUS_BAD_DEVICE_TYPE)
5746 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5747 in_buffer, in_size, out_buffer, out_size );
5749 if (status != STATUS_PENDING) io->u.Status = status;
5750 return status;
5754 /* Tell Valgrind to ignore any holes in structs we will be passing to the
5755 * server */
5756 static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer, ULONG in_size )
5758 #ifdef VALGRIND_MAKE_MEM_DEFINED
5759 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
5760 do { \
5761 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
5762 if ((size) >= FIELD_OFFSET(t, f2)) \
5763 VALGRIND_MAKE_MEM_DEFINED( \
5764 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
5765 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
5766 } while (0)
5768 switch (code)
5770 case FSCTL_PIPE_WAIT:
5771 IGNORE_STRUCT_HOLE(in_buffer, in_size, FILE_PIPE_WAIT_FOR_BUFFER, TimeoutSpecified, Name);
5772 break;
5774 #endif
5778 /******************************************************************************
5779 * NtFsControlFile (NTDLL.@)
5781 NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5782 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5783 void *out_buffer, ULONG out_size )
5785 NTSTATUS status;
5787 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5788 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5790 if (!io) return STATUS_INVALID_PARAMETER;
5792 ignore_server_ioctl_struct_holes( code, in_buffer, in_size );
5794 switch (code)
5796 case FSCTL_DISMOUNT_VOLUME:
5797 status = server_ioctl_file( handle, event, apc, apc_context, io, code,
5798 in_buffer, in_size, out_buffer, out_size );
5799 if (!status) status = unmount_device( handle );
5800 return status;
5802 case FSCTL_PIPE_IMPERSONATE:
5803 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
5804 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5805 in_buffer, in_size, out_buffer, out_size );
5807 case FSCTL_IS_VOLUME_MOUNTED:
5808 case FSCTL_LOCK_VOLUME:
5809 case FSCTL_UNLOCK_VOLUME:
5810 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
5811 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
5812 status = STATUS_SUCCESS;
5813 break;
5815 case FSCTL_GET_RETRIEVAL_POINTERS:
5817 RETRIEVAL_POINTERS_BUFFER *buffer = (RETRIEVAL_POINTERS_BUFFER *)out_buffer;
5819 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
5821 if (out_size >= sizeof(RETRIEVAL_POINTERS_BUFFER))
5823 buffer->ExtentCount = 1;
5824 buffer->StartingVcn.QuadPart = 1;
5825 buffer->Extents[0].NextVcn.QuadPart = 0;
5826 buffer->Extents[0].Lcn.QuadPart = 0;
5827 io->Information = sizeof(RETRIEVAL_POINTERS_BUFFER);
5828 status = STATUS_SUCCESS;
5830 else
5832 io->Information = 0;
5833 status = STATUS_BUFFER_TOO_SMALL;
5835 break;
5838 case FSCTL_GET_OBJECT_ID:
5840 FILE_OBJECTID_BUFFER *info = out_buffer;
5841 int fd, needs_close;
5842 struct stat st;
5844 io->Information = 0;
5845 if (out_size >= sizeof(*info))
5847 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
5848 if (status) break;
5849 fstat( fd, &st );
5850 if (needs_close) close( fd );
5851 memset( info, 0, sizeof(*info) );
5852 memcpy( info->ObjectId, &st.st_dev, sizeof(st.st_dev) );
5853 memcpy( info->ObjectId + 8, &st.st_ino, sizeof(st.st_ino) );
5854 io->Information = sizeof(*info);
5856 else status = STATUS_BUFFER_TOO_SMALL;
5857 break;
5860 case FSCTL_SET_SPARSE:
5861 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
5862 io->Information = 0;
5863 status = STATUS_SUCCESS;
5864 break;
5865 default:
5866 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5867 in_buffer, in_size, out_buffer, out_size );
5870 if (status != STATUS_PENDING) io->u.Status = status;
5871 return status;
5875 /******************************************************************************
5876 * NtFlushBuffersFile (NTDLL.@)
5878 NTSTATUS WINAPI NtFlushBuffersFile( HANDLE handle, IO_STATUS_BLOCK *io )
5880 NTSTATUS ret;
5881 HANDLE wait_handle;
5882 enum server_fd_type type;
5883 int fd, needs_close;
5885 if (!io || !virtual_check_buffer_for_write( io, sizeof(*io) )) return STATUS_ACCESS_VIOLATION;
5887 ret = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, &type, NULL );
5888 if (ret == STATUS_ACCESS_DENIED)
5889 ret = server_get_unix_fd( handle, FILE_APPEND_DATA, &fd, &needs_close, &type, NULL );
5891 if (!ret && (type == FD_TYPE_FILE || type == FD_TYPE_DIR || type == FD_TYPE_CHAR))
5893 if (fsync(fd)) ret = errno_to_status( errno );
5894 io->u.Status = ret;
5895 io->Information = 0;
5897 else if (!ret && type == FD_TYPE_SERIAL)
5899 ret = serial_FlushBuffersFile( fd );
5901 else if (ret != STATUS_ACCESS_DENIED)
5903 struct async_irp *async;
5905 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5906 return STATUS_NO_MEMORY;
5907 async->buffer = NULL;
5908 async->size = 0;
5910 SERVER_START_REQ( flush )
5912 req->async = server_async( handle, &async->io, NULL, NULL, NULL, io );
5913 ret = wine_server_call( req );
5914 wait_handle = wine_server_ptr_handle( reply->event );
5915 if (wait_handle && ret != STATUS_PENDING)
5917 io->u.Status = ret;
5918 io->Information = 0;
5921 SERVER_END_REQ;
5923 if (ret != STATUS_PENDING) free( async );
5925 if (wait_handle) ret = wait_async( wait_handle, FALSE, io );
5928 if (needs_close) close( fd );
5929 return ret;
5933 /**************************************************************************
5934 * NtCancelIoFile (NTDLL.@)
5936 NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status )
5938 TRACE( "%p %p\n", handle, io_status );
5940 SERVER_START_REQ( cancel_async )
5942 req->handle = wine_server_obj_handle( handle );
5943 req->only_thread = TRUE;
5944 io_status->u.Status = wine_server_call( req );
5946 SERVER_END_REQ;
5947 return io_status->u.Status;
5951 /**************************************************************************
5952 * NtCancelIoFileEx (NTDLL.@)
5954 NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status )
5956 TRACE( "%p %p %p\n", handle, io, io_status );
5958 SERVER_START_REQ( cancel_async )
5960 req->handle = wine_server_obj_handle( handle );
5961 req->iosb = wine_server_client_ptr( io );
5962 io_status->u.Status = wine_server_call( req );
5964 SERVER_END_REQ;
5965 return io_status->u.Status;
5969 /******************************************************************
5970 * NtLockFile (NTDLL.@)
5972 NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void* apc_user,
5973 IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
5974 LARGE_INTEGER *count, ULONG *key, BOOLEAN dont_wait, BOOLEAN exclusive )
5976 static int warn;
5977 NTSTATUS ret;
5978 HANDLE handle;
5979 BOOLEAN async;
5981 if (apc || io_status || key)
5983 FIXME("Unimplemented yet parameter\n");
5984 return STATUS_NOT_IMPLEMENTED;
5986 if (apc_user && !warn++) FIXME("I/O completion on lock not implemented yet\n");
5988 for (;;)
5990 SERVER_START_REQ( lock_file )
5992 req->handle = wine_server_obj_handle( file );
5993 req->offset = offset->QuadPart;
5994 req->count = count->QuadPart;
5995 req->shared = !exclusive;
5996 req->wait = !dont_wait;
5997 ret = wine_server_call( req );
5998 handle = wine_server_ptr_handle( reply->handle );
5999 async = reply->overlapped;
6001 SERVER_END_REQ;
6002 if (ret != STATUS_PENDING)
6004 if (!ret && event) NtSetEvent( event, NULL );
6005 return ret;
6007 if (async)
6009 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6010 if (handle) NtClose( handle );
6011 return STATUS_PENDING;
6013 if (handle)
6015 NtWaitForSingleObject( handle, FALSE, NULL );
6016 NtClose( handle );
6018 else /* Unix lock conflict, sleep a bit and retry */
6020 LARGE_INTEGER time;
6021 time.QuadPart = -100 * (ULONGLONG)10000;
6022 NtDelayExecution( FALSE, &time );
6028 /******************************************************************
6029 * NtUnlockFile (NTDLL.@)
6031 NTSTATUS WINAPI NtUnlockFile( HANDLE handle, IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
6032 LARGE_INTEGER *count, ULONG *key )
6034 NTSTATUS status;
6036 TRACE( "%p %x%08x %x%08x\n",
6037 handle, offset->u.HighPart, offset->u.LowPart, count->u.HighPart, count->u.LowPart );
6039 if (io_status || key)
6041 FIXME("Unimplemented yet parameter\n");
6042 return STATUS_NOT_IMPLEMENTED;
6045 SERVER_START_REQ( unlock_file )
6047 req->handle = wine_server_obj_handle( handle );
6048 req->offset = offset->QuadPart;
6049 req->count = count->QuadPart;
6050 status = wine_server_call( req );
6052 SERVER_END_REQ;
6053 return status;
6057 static NTSTATUS read_changes_apc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
6059 struct async_fileio_read_changes *fileio = user;
6060 int size = 0;
6062 if (status == STATUS_ALERTED)
6064 SERVER_START_REQ( read_change )
6066 req->handle = wine_server_obj_handle( fileio->io.handle );
6067 wine_server_set_reply( req, fileio->data, fileio->data_size );
6068 status = wine_server_call( req );
6069 size = wine_server_reply_size( reply );
6071 SERVER_END_REQ;
6073 if (status == STATUS_SUCCESS && fileio->buffer)
6075 FILE_NOTIFY_INFORMATION *pfni = fileio->buffer;
6076 int i, left = fileio->buffer_size;
6077 DWORD *last_entry_offset = NULL;
6078 struct filesystem_event *event = (struct filesystem_event*)fileio->data;
6080 while (size && left >= sizeof(*pfni))
6082 DWORD len = (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR);
6084 /* convert to an NT style path */
6085 for (i = 0; i < event->len; i++)
6086 if (event->name[i] == '/') event->name[i] = '\\';
6088 pfni->Action = event->action;
6089 pfni->FileNameLength = ntdll_umbstowcs( event->name, event->len, pfni->FileName, len );
6090 last_entry_offset = &pfni->NextEntryOffset;
6092 if (pfni->FileNameLength == len) break;
6094 i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
6095 pfni->FileNameLength *= sizeof(WCHAR);
6096 pfni->NextEntryOffset = i;
6097 pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
6098 left -= i;
6100 i = (offsetof(struct filesystem_event, name[event->len])
6101 + sizeof(int)-1) / sizeof(int) * sizeof(int);
6102 event = (struct filesystem_event*)((char*)event + i);
6103 size -= i;
6106 if (size)
6108 status = STATUS_NOTIFY_ENUM_DIR;
6109 size = 0;
6111 else
6113 if (last_entry_offset) *last_entry_offset = 0;
6114 size = fileio->buffer_size - left;
6117 else
6119 status = STATUS_NOTIFY_ENUM_DIR;
6120 size = 0;
6124 if (status != STATUS_PENDING)
6126 iosb->u.Status = status;
6127 iosb->Information = size;
6128 release_fileio( &fileio->io );
6130 return status;
6133 #define FILE_NOTIFY_ALL ( \
6134 FILE_NOTIFY_CHANGE_FILE_NAME | \
6135 FILE_NOTIFY_CHANGE_DIR_NAME | \
6136 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
6137 FILE_NOTIFY_CHANGE_SIZE | \
6138 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6139 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6140 FILE_NOTIFY_CHANGE_CREATION | \
6141 FILE_NOTIFY_CHANGE_SECURITY )
6143 /******************************************************************************
6144 * NtNotifyChangeDirectoryFile (NTDLL.@)
6146 NTSTATUS WINAPI NtNotifyChangeDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
6147 void *apc_context, IO_STATUS_BLOCK *iosb, void *buffer,
6148 ULONG buffer_size, ULONG filter, BOOLEAN subtree )
6150 struct async_fileio_read_changes *fileio;
6151 NTSTATUS status;
6152 ULONG size = max( 4096, buffer_size );
6154 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6155 handle, event, apc, apc_context, iosb, buffer, buffer_size, filter, subtree );
6157 if (!iosb) return STATUS_ACCESS_VIOLATION;
6158 if (filter == 0 || (filter & ~FILE_NOTIFY_ALL)) return STATUS_INVALID_PARAMETER;
6160 fileio = (struct async_fileio_read_changes *)alloc_fileio(
6161 offsetof(struct async_fileio_read_changes, data[size]), read_changes_apc, handle );
6162 if (!fileio) return STATUS_NO_MEMORY;
6164 fileio->buffer = buffer;
6165 fileio->buffer_size = buffer_size;
6166 fileio->data_size = size;
6168 SERVER_START_REQ( read_directory_changes )
6170 req->filter = filter;
6171 req->want_data = (buffer != NULL);
6172 req->subtree = subtree;
6173 req->async = server_async( handle, &fileio->io, event, apc, apc_context, iosb );
6174 status = wine_server_call( req );
6176 SERVER_END_REQ;
6178 if (status != STATUS_PENDING) free( fileio );
6179 return status;
6183 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6184 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6185 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION *info, const char *fstypename,
6186 unsigned int flags )
6188 if (!strcmp("cd9660", fstypename) || !strcmp("udf", fstypename))
6190 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6191 /* Don't assume read-only, let the mount options set it below */
6192 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6194 else if (!strcmp("nfs", fstypename) || !strcmp("nwfs", fstypename) ||
6195 !strcmp("smbfs", fstypename) || !strcmp("afpfs", fstypename))
6197 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6198 info->Characteristics |= FILE_REMOTE_DEVICE;
6200 else if (!strcmp("procfs", fstypename))
6201 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6202 else
6203 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6205 if (flags & MNT_RDONLY)
6206 info->Characteristics |= FILE_READ_ONLY_DEVICE;
6208 if (!(flags & MNT_LOCAL))
6210 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6211 info->Characteristics |= FILE_REMOTE_DEVICE;
6214 #endif
6216 static inline BOOL is_device_placeholder( int fd )
6218 static const char wine_placeholder[] = "Wine device placeholder";
6219 char buffer[sizeof(wine_placeholder)-1];
6221 if (pread( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1)
6222 return FALSE;
6223 return !memcmp( buffer, wine_placeholder, sizeof(wine_placeholder) - 1 );
6226 static NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info )
6228 struct stat st;
6230 info->Characteristics = 0;
6231 if (fstat( fd, &st ) < 0) return errno_to_status( errno );
6232 if (S_ISCHR( st.st_mode ))
6234 info->DeviceType = FILE_DEVICE_UNKNOWN;
6235 #ifdef linux
6236 switch(major(st.st_rdev))
6238 case MEM_MAJOR:
6239 info->DeviceType = FILE_DEVICE_NULL;
6240 break;
6241 case TTY_MAJOR:
6242 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6243 break;
6244 case LP_MAJOR:
6245 info->DeviceType = FILE_DEVICE_PARALLEL_PORT;
6246 break;
6247 case SCSI_TAPE_MAJOR:
6248 info->DeviceType = FILE_DEVICE_TAPE;
6249 break;
6251 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6253 int d_type;
6254 if (ioctl(fd, FIODTYPE, &d_type) == 0)
6256 switch(d_type)
6258 case D_TAPE:
6259 info->DeviceType = FILE_DEVICE_TAPE;
6260 break;
6261 case D_DISK:
6262 info->DeviceType = FILE_DEVICE_DISK;
6263 break;
6264 case D_TTY:
6265 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6266 break;
6267 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6268 case D_MEM:
6269 info->DeviceType = FILE_DEVICE_NULL;
6270 break;
6271 #endif
6273 /* no special d_type for parallel ports */
6276 #endif
6278 else if (S_ISBLK( st.st_mode ))
6280 info->DeviceType = FILE_DEVICE_DISK;
6282 else if (S_ISFIFO( st.st_mode ) || S_ISSOCK( st.st_mode ))
6284 info->DeviceType = FILE_DEVICE_NAMED_PIPE;
6286 else if (is_device_placeholder( fd ))
6288 info->DeviceType = FILE_DEVICE_DISK;
6290 else /* regular file or directory */
6292 #if defined(linux) && defined(HAVE_FSTATFS)
6293 struct statfs stfs;
6295 /* check for floppy disk */
6296 if (major(st.st_dev) == FLOPPY_MAJOR)
6297 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6299 if (fstatfs( fd, &stfs ) < 0) stfs.f_type = 0;
6300 switch (stfs.f_type)
6302 case 0x9660: /* iso9660 */
6303 case 0x9fa1: /* supermount */
6304 case 0x15013346: /* udf */
6305 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6306 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6307 break;
6308 case 0x6969: /* nfs */
6309 case 0xff534d42: /* cifs */
6310 case 0xfe534d42: /* smb2 */
6311 case 0x517b: /* smbfs */
6312 case 0x564c: /* ncpfs */
6313 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6314 info->Characteristics |= FILE_REMOTE_DEVICE;
6315 break;
6316 case 0x1373: /* devfs */
6317 case 0x9fa0: /* procfs */
6318 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6319 break;
6320 case 0x01021994: /* tmpfs */
6321 case 0x28cd3d45: /* cramfs */
6322 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6323 * filesystems are rare on Windows, and some programs refuse to
6324 * recognize them as valid. */
6325 default:
6326 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6327 break;
6329 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6330 struct statfs stfs;
6332 if (fstatfs( fd, &stfs ) < 0)
6333 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6334 else
6335 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flags );
6336 #elif defined(__NetBSD__)
6337 struct statvfs stfs;
6339 if (fstatvfs( fd, &stfs) < 0)
6340 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6341 else
6342 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flag );
6343 #elif defined(sun)
6344 /* Use dkio to work out device types */
6346 # include <sys/dkio.h>
6347 # include <sys/vtoc.h>
6348 struct dk_cinfo dkinf;
6349 int retval = ioctl(fd, DKIOCINFO, &dkinf);
6350 if(retval==-1){
6351 WARN("Unable to get disk device type information - assuming a disk like device\n");
6352 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6354 switch (dkinf.dki_ctype)
6356 case DKC_CDROM:
6357 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6358 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6359 break;
6360 case DKC_NCRFLOPPY:
6361 case DKC_SMSFLOPPY:
6362 case DKC_INTEL82072:
6363 case DKC_INTEL82077:
6364 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6365 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6366 break;
6367 case DKC_MD:
6368 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6369 * filesystems are rare on Windows, and some programs refuse to
6370 * recognize them as valid. */
6371 default:
6372 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6375 #else
6376 static int warned;
6377 if (!warned++) FIXME( "device info not properly supported on this platform\n" );
6378 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6379 #endif
6380 info->Characteristics |= FILE_DEVICE_IS_MOUNTED;
6382 return STATUS_SUCCESS;
6386 /******************************************************************************
6387 * NtQueryVolumeInformationFile (NTDLL.@)
6389 NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
6390 void *buffer, ULONG length,
6391 FS_INFORMATION_CLASS info_class )
6393 int fd, needs_close;
6394 struct stat st;
6396 io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
6397 if (io->u.Status == STATUS_BAD_DEVICE_TYPE)
6399 struct async_irp *async;
6400 HANDLE wait_handle;
6401 NTSTATUS status;
6403 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
6404 return STATUS_NO_MEMORY;
6405 async->buffer = buffer;
6406 async->size = length;
6408 SERVER_START_REQ( get_volume_info )
6410 req->async = server_async( handle, &async->io, NULL, NULL, NULL, io );
6411 req->handle = wine_server_obj_handle( handle );
6412 req->info_class = info_class;
6413 wine_server_set_reply( req, buffer, length );
6414 status = wine_server_call( req );
6415 if (status != STATUS_PENDING)
6417 io->u.Status = status;
6418 io->Information = wine_server_reply_size( reply );
6420 wait_handle = wine_server_ptr_handle( reply->wait );
6422 SERVER_END_REQ;
6423 if (status != STATUS_PENDING) free( async );
6424 if (wait_handle) status = wait_async( wait_handle, FALSE, io );
6425 return status;
6427 else if (io->u.Status) return io->u.Status;
6429 io->u.Status = STATUS_NOT_IMPLEMENTED;
6430 io->Information = 0;
6432 switch( info_class )
6434 case FileFsLabelInformation:
6435 FIXME( "%p: label info not supported\n", handle );
6436 break;
6438 case FileFsSizeInformation:
6439 if (length < sizeof(FILE_FS_SIZE_INFORMATION))
6440 io->u.Status = STATUS_BUFFER_TOO_SMALL;
6441 else
6443 FILE_FS_SIZE_INFORMATION *info = buffer;
6445 if (fstat( fd, &st ) < 0)
6447 io->u.Status = errno_to_status( errno );
6448 break;
6450 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
6452 io->u.Status = STATUS_INVALID_DEVICE_REQUEST;
6454 else
6456 ULONGLONG bsize;
6457 /* Linux's fstatvfs is buggy */
6458 #if !defined(linux) || !defined(HAVE_FSTATFS)
6459 struct statvfs stfs;
6461 if (fstatvfs( fd, &stfs ) < 0)
6463 io->u.Status = errno_to_status( errno );
6464 break;
6466 bsize = stfs.f_frsize;
6467 #else
6468 struct statfs stfs;
6469 if (fstatfs( fd, &stfs ) < 0)
6471 io->u.Status = errno_to_status( errno );
6472 break;
6474 bsize = stfs.f_bsize;
6475 #endif
6476 if (bsize == 2048) /* assume CD-ROM */
6478 info->BytesPerSector = 2048;
6479 info->SectorsPerAllocationUnit = 1;
6481 else
6483 info->BytesPerSector = 512;
6484 info->SectorsPerAllocationUnit = 8;
6486 info->TotalAllocationUnits.QuadPart = bsize * stfs.f_blocks / (info->BytesPerSector * info->SectorsPerAllocationUnit);
6487 info->AvailableAllocationUnits.QuadPart = bsize * stfs.f_bavail / (info->BytesPerSector * info->SectorsPerAllocationUnit);
6488 io->Information = sizeof(*info);
6489 io->u.Status = STATUS_SUCCESS;
6492 break;
6494 case FileFsDeviceInformation:
6495 if (length < sizeof(FILE_FS_DEVICE_INFORMATION))
6496 io->u.Status = STATUS_BUFFER_TOO_SMALL;
6497 else
6499 FILE_FS_DEVICE_INFORMATION *info = buffer;
6501 if ((io->u.Status = get_device_info( fd, info )) == STATUS_SUCCESS)
6502 io->Information = sizeof(*info);
6504 break;
6506 case FileFsAttributeInformation:
6508 static const WCHAR fatW[] = {'F','A','T'};
6509 static const WCHAR fat32W[] = {'F','A','T','3','2'};
6510 static const WCHAR ntfsW[] = {'N','T','F','S'};
6511 static const WCHAR cdfsW[] = {'C','D','F','S'};
6512 static const WCHAR udfW[] = {'U','D','F'};
6514 FILE_FS_ATTRIBUTE_INFORMATION *info = buffer;
6515 struct mountmgr_unix_drive drive;
6516 enum mountmgr_fs_type fs_type = MOUNTMGR_FS_TYPE_NTFS;
6518 if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
6520 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
6521 break;
6524 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) )) fs_type = drive.fs_type;
6525 else
6527 struct statfs stfs;
6529 if (!fstatfs( fd, &stfs ))
6531 #if defined(linux) && defined(HAVE_FSTATFS)
6532 switch (stfs.f_type)
6534 case 0x9660:
6535 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6536 break;
6537 case 0x15013346:
6538 fs_type = MOUNTMGR_FS_TYPE_UDF;
6539 break;
6540 case 0x4d44:
6541 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6542 break;
6544 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6545 if (!strcmp( stfs.f_fstypename, "cd9660" ))
6546 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6547 else if (!strcmp( stfs.f_fstypename, "udf" ))
6548 fs_type = MOUNTMGR_FS_TYPE_UDF;
6549 else if (!strcmp( stfs.f_fstypename, "msdos" )) /* FreeBSD < 5, Apple */
6550 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6551 else if (!strcmp( stfs.f_fstypename, "msdosfs" )) /* FreeBSD >= 5 */
6552 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6553 #endif
6557 switch (fs_type)
6559 case MOUNTMGR_FS_TYPE_ISO9660:
6560 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
6561 info->MaximumComponentNameLength = 221;
6562 info->FileSystemNameLength = min( sizeof(cdfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6563 memcpy(info->FileSystemName, cdfsW, info->FileSystemNameLength);
6564 break;
6565 case MOUNTMGR_FS_TYPE_UDF:
6566 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
6567 info->MaximumComponentNameLength = 255;
6568 info->FileSystemNameLength = min( sizeof(udfW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6569 memcpy(info->FileSystemName, udfW, info->FileSystemNameLength);
6570 break;
6571 case MOUNTMGR_FS_TYPE_FAT:
6572 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6573 info->MaximumComponentNameLength = 255;
6574 info->FileSystemNameLength = min( sizeof(fatW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6575 memcpy(info->FileSystemName, fatW, info->FileSystemNameLength);
6576 break;
6577 case MOUNTMGR_FS_TYPE_FAT32:
6578 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6579 info->MaximumComponentNameLength = 255;
6580 info->FileSystemNameLength = min( sizeof(fat32W), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6581 memcpy(info->FileSystemName, fat32W, info->FileSystemNameLength);
6582 break;
6583 default:
6584 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
6585 info->MaximumComponentNameLength = 255;
6586 info->FileSystemNameLength = min( sizeof(ntfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6587 memcpy(info->FileSystemName, ntfsW, info->FileSystemNameLength);
6588 break;
6591 io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
6592 io->u.Status = STATUS_SUCCESS;
6593 break;
6596 case FileFsVolumeInformation:
6598 FILE_FS_VOLUME_INFORMATION *info = buffer;
6599 ULONGLONG data[64];
6600 struct mountmgr_unix_drive *drive = (struct mountmgr_unix_drive *)data;
6601 const WCHAR *label;
6603 if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
6605 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
6606 break;
6609 if (get_mountmgr_fs_info( handle, fd, drive, sizeof(data) ))
6611 io->u.Status = STATUS_NOT_IMPLEMENTED;
6612 break;
6615 label = (WCHAR *)((char *)drive + drive->label_offset);
6616 info->VolumeCreationTime.QuadPart = 0; /* FIXME */
6617 info->VolumeSerialNumber = drive->serial;
6618 info->VolumeLabelLength = min( wcslen( label ) * sizeof(WCHAR),
6619 length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
6620 info->SupportsObjects = (drive->fs_type == MOUNTMGR_FS_TYPE_NTFS);
6621 memcpy( info->VolumeLabel, label, info->VolumeLabelLength );
6622 io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
6623 io->u.Status = STATUS_SUCCESS;
6624 break;
6627 case FileFsControlInformation:
6628 FIXME( "%p: control info not supported\n", handle );
6629 break;
6631 case FileFsFullSizeInformation:
6632 FIXME( "%p: full size info not supported\n", handle );
6633 break;
6635 case FileFsObjectIdInformation:
6636 FIXME( "%p: object id info not supported\n", handle );
6637 break;
6639 case FileFsMaximumInformation:
6640 FIXME( "%p: maximum info not supported\n", handle );
6641 break;
6643 default:
6644 io->u.Status = STATUS_INVALID_PARAMETER;
6645 break;
6647 if (needs_close) close( fd );
6648 return io->u.Status;
6652 /******************************************************************************
6653 * NtSetVolumeInformationFile (NTDLL.@)
6655 NTSTATUS WINAPI NtSetVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, void *info,
6656 ULONG length, FS_INFORMATION_CLASS class )
6658 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle, io, info, length, class );
6659 return STATUS_SUCCESS;
6663 /******************************************************************
6664 * NtQueryEaFile (NTDLL.@)
6666 NTSTATUS WINAPI NtQueryEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
6667 BOOLEAN single_entry, void *list, ULONG list_len,
6668 ULONG *index, BOOLEAN restart )
6670 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) stub\n",
6671 handle, io, buffer, length, single_entry, list, list_len, index, restart );
6672 return STATUS_ACCESS_DENIED;
6676 /******************************************************************
6677 * NtSetEaFile (NTDLL.@)
6679 NTSTATUS WINAPI NtSetEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length )
6681 FIXME( "(%p,%p,%p,%d) stub\n", handle, io, buffer, length );
6682 return STATUS_ACCESS_DENIED;
6686 /* convert type information from server format; helper for NtQueryObject */
6687 static void *put_object_type_info( OBJECT_TYPE_INFORMATION *p, struct object_type_info *info )
6689 const ULONG align = sizeof(DWORD_PTR) - 1;
6691 memset( p, 0, sizeof(*p) );
6692 p->TypeName.Buffer = (WCHAR *)(p + 1);
6693 p->TypeName.Length = info->name_len;
6694 p->TypeName.MaximumLength = info->name_len + sizeof(WCHAR);
6695 p->TotalNumberOfObjects = info->obj_count;
6696 p->TotalNumberOfHandles = info->handle_count;
6697 p->HighWaterNumberOfObjects = info->obj_max;
6698 p->HighWaterNumberOfHandles = info->handle_max;
6699 p->TypeIndex = info->index + 2;
6700 p->GenericMapping.GenericRead = info->mapping.read;
6701 p->GenericMapping.GenericWrite = info->mapping.write;
6702 p->GenericMapping.GenericExecute = info->mapping.exec;
6703 p->GenericMapping.GenericAll = info->mapping.all;
6704 p->ValidAccessMask = info->valid_access;
6705 memcpy( p->TypeName.Buffer, info + 1, info->name_len );
6706 p->TypeName.Buffer[info->name_len / sizeof(WCHAR)] = 0;
6707 return (char *)(p + 1) + ((p->TypeName.MaximumLength + align) & ~align);
6710 /**************************************************************************
6711 * NtQueryObject (NTDLL.@)
6713 NTSTATUS WINAPI NtQueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
6714 void *ptr, ULONG len, ULONG *used_len )
6716 NTSTATUS status;
6718 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle, info_class, ptr, len, used_len);
6720 if (used_len) *used_len = 0;
6722 switch (info_class)
6724 case ObjectBasicInformation:
6726 OBJECT_BASIC_INFORMATION *p = ptr;
6728 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6730 SERVER_START_REQ( get_object_info )
6732 req->handle = wine_server_obj_handle( handle );
6733 status = wine_server_call( req );
6734 if (status == STATUS_SUCCESS)
6736 memset( p, 0, sizeof(*p) );
6737 p->GrantedAccess = reply->access;
6738 p->PointerCount = reply->ref_count;
6739 p->HandleCount = reply->handle_count;
6740 if (used_len) *used_len = sizeof(*p);
6743 SERVER_END_REQ;
6744 break;
6747 case ObjectNameInformation:
6749 OBJECT_NAME_INFORMATION *p = ptr;
6750 char *unix_name;
6751 WCHAR *nt_name;
6753 /* first try as a file object */
6755 if (!(status = server_get_unix_name( handle, &unix_name )))
6757 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
6759 ULONG size = (wcslen(nt_name) + 1) * sizeof(WCHAR);
6760 if (len < sizeof(*p)) status = STATUS_INFO_LENGTH_MISMATCH;
6761 else if (len < sizeof(*p) + size) status = STATUS_BUFFER_OVERFLOW;
6762 else
6764 p->Name.Buffer = (WCHAR *)(p + 1);
6765 p->Name.Length = size - sizeof(WCHAR);
6766 p->Name.MaximumLength = size;
6767 wcscpy( p->Name.Buffer, nt_name );
6769 if (used_len) *used_len = sizeof(*p) + size;
6770 free( nt_name );
6772 free( unix_name );
6773 break;
6775 else if (status != STATUS_OBJECT_TYPE_MISMATCH) break;
6777 /* not a file, treat as a generic object */
6779 SERVER_START_REQ( get_object_info )
6781 req->handle = wine_server_obj_handle( handle );
6782 if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
6783 status = wine_server_call( req );
6784 if (status == STATUS_SUCCESS)
6786 if (!reply->total) /* no name */
6788 if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
6789 else memset( p, 0, sizeof(*p) );
6790 if (used_len) *used_len = sizeof(*p);
6792 else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
6794 if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
6795 status = STATUS_INFO_LENGTH_MISMATCH;
6797 else
6799 ULONG res = wine_server_reply_size( reply );
6800 p->Name.Buffer = (WCHAR *)(p + 1);
6801 p->Name.Length = res;
6802 p->Name.MaximumLength = res + sizeof(WCHAR);
6803 p->Name.Buffer[res / sizeof(WCHAR)] = 0;
6804 if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
6808 SERVER_END_REQ;
6809 break;
6812 case ObjectTypeInformation:
6814 OBJECT_TYPE_INFORMATION *p = ptr;
6815 char buffer[sizeof(struct object_type_info) + 64];
6816 struct object_type_info *info = (struct object_type_info *)buffer;
6818 SERVER_START_REQ( get_object_type )
6820 req->handle = wine_server_obj_handle( handle );
6821 wine_server_set_reply( req, buffer, sizeof(buffer) );
6822 status = wine_server_call( req );
6824 SERVER_END_REQ;
6825 if (status) break;
6826 if (sizeof(*p) + info->name_len + sizeof(WCHAR) <= len)
6828 put_object_type_info( p, info );
6829 if (used_len) *used_len = sizeof(*p) + p->TypeName.MaximumLength;
6831 else
6833 if (used_len) *used_len = sizeof(*p) + info->name_len + sizeof(WCHAR);
6834 status = STATUS_INFO_LENGTH_MISMATCH;
6836 break;
6839 case ObjectTypesInformation:
6841 OBJECT_TYPES_INFORMATION *types = ptr;
6842 OBJECT_TYPE_INFORMATION *p;
6843 struct object_type_info *buffer;
6844 /* assume at most 32 types, with an average 16-char name */
6845 ULONG size = 32 * (sizeof(struct object_type_info) + 16 * sizeof(WCHAR));
6846 ULONG i, count, pos, total, align = sizeof(DWORD_PTR) - 1;
6848 buffer = malloc( size );
6849 SERVER_START_REQ( get_object_types )
6851 wine_server_set_reply( req, buffer, size );
6852 status = wine_server_call( req );
6853 count = reply->count;
6855 SERVER_END_REQ;
6856 if (!status)
6858 if (len >= sizeof(*types)) types->NumberOfTypes = count;
6859 total = (sizeof(*types) + align) & ~align;
6860 p = (OBJECT_TYPE_INFORMATION *)((char *)ptr + total);
6861 for (i = pos = 0; i < count; i++)
6863 struct object_type_info *info = (struct object_type_info *)((char *)buffer + pos);
6864 pos += sizeof(*info) + ((info->name_len + 3) & ~3);
6865 total += sizeof(*p) + ((info->name_len + sizeof(WCHAR) + align) & ~align);
6866 if (total <= len) p = put_object_type_info( p, info );
6868 if (used_len) *used_len = total;
6869 if (total > len) status = STATUS_INFO_LENGTH_MISMATCH;
6871 else if (status == STATUS_BUFFER_OVERFLOW) FIXME( "size %u too small\n", size );
6873 free( buffer );
6874 break;
6877 case ObjectDataInformation:
6879 OBJECT_DATA_INFORMATION* p = ptr;
6881 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6883 SERVER_START_REQ( set_handle_info )
6885 req->handle = wine_server_obj_handle( handle );
6886 req->flags = 0;
6887 req->mask = 0;
6888 status = wine_server_call( req );
6889 if (status == STATUS_SUCCESS)
6891 p->InheritHandle = (reply->old_flags & HANDLE_FLAG_INHERIT) != 0;
6892 p->ProtectFromClose = (reply->old_flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0;
6893 if (used_len) *used_len = sizeof(*p);
6896 SERVER_END_REQ;
6897 break;
6900 default:
6901 FIXME("Unsupported information class %u\n", info_class);
6902 status = STATUS_NOT_IMPLEMENTED;
6903 break;
6905 return status;
6909 /**************************************************************************
6910 * NtSetInformationObject (NTDLL.@)
6912 NTSTATUS WINAPI NtSetInformationObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
6913 void *ptr, ULONG len )
6915 NTSTATUS status;
6917 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle, info_class, ptr, len);
6919 switch (info_class)
6921 case ObjectDataInformation:
6923 OBJECT_DATA_INFORMATION* p = ptr;
6925 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6927 SERVER_START_REQ( set_handle_info )
6929 req->handle = wine_server_obj_handle( handle );
6930 req->mask = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
6931 if (p->InheritHandle) req->flags |= HANDLE_FLAG_INHERIT;
6932 if (p->ProtectFromClose) req->flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
6933 status = wine_server_call( req );
6935 SERVER_END_REQ;
6936 break;
6939 default:
6940 FIXME("Unsupported information class %u\n", info_class);
6941 status = STATUS_NOT_IMPLEMENTED;
6942 break;
6944 return status;