server: Use a separate request to retrieve the object name.
[wine.git] / dlls / ntdll / unix / file.c
blob21bcb2153859eefd1497b18853b1dd5cab6ba27d
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 pthread_mutex_t dir_mutex = PTHREAD_MUTEX_INITIALIZER;
245 static pthread_mutex_t mnt_mutex = PTHREAD_MUTEX_INITIALIZER;
247 /* check if a given Unicode char is OK in a DOS short name */
248 static inline BOOL is_invalid_dos_char( WCHAR ch )
250 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,'~','.',0 };
251 if (ch > 0x7f) return TRUE;
252 return wcschr( invalid_chars, ch ) != NULL;
255 /* check if the device can be a mounted volume */
256 static inline BOOL is_valid_mounted_device( const struct stat *st )
258 #if defined(linux) || defined(__sun__)
259 return S_ISBLK( st->st_mode );
260 #else
261 /* disks are char devices on *BSD */
262 return S_ISCHR( st->st_mode );
263 #endif
266 static inline void ignore_file( const char *name )
268 struct stat st;
269 assert( ignored_files_count < MAX_IGNORED_FILES );
270 if (!stat( name, &st ))
272 ignored_files[ignored_files_count].dev = st.st_dev;
273 ignored_files[ignored_files_count].ino = st.st_ino;
274 ignored_files_count++;
278 static inline BOOL is_same_file( const struct file_identity *file, const struct stat *st )
280 return st->st_dev == file->dev && st->st_ino == file->ino;
283 static inline BOOL is_ignored_file( const struct stat *st )
285 unsigned int i;
287 for (i = 0; i < ignored_files_count; i++)
288 if (is_same_file( &ignored_files[i], st )) return TRUE;
289 return FALSE;
292 static inline unsigned int dir_info_align( unsigned int len )
294 return (len + 7) & ~7;
297 static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned int len )
299 switch (class)
301 case FileDirectoryInformation:
302 return offsetof( FILE_DIRECTORY_INFORMATION, FileName[len] );
303 case FileBothDirectoryInformation:
304 return offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[len] );
305 case FileFullDirectoryInformation:
306 return offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[len] );
307 case FileIdBothDirectoryInformation:
308 return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[len] );
309 case FileIdFullDirectoryInformation:
310 return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[len] );
311 case FileIdGlobalTxDirectoryInformation:
312 return offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[len] );
313 case FileNamesInformation:
314 return offsetof( FILE_NAMES_INFORMATION, FileName[len] );
315 default:
316 assert(0);
317 return 0;
321 static inline BOOL has_wildcard( const UNICODE_STRING *mask )
323 int i;
325 if (!mask) return TRUE;
326 for (i = 0; i < mask->Length / sizeof(WCHAR); i++)
327 if (mask->Buffer[i] == '*' || mask->Buffer[i] == '?') return TRUE;
328 return FALSE;
331 NTSTATUS errno_to_status( int err )
333 TRACE( "errno = %d\n", err );
334 switch (err)
336 case EAGAIN: return STATUS_SHARING_VIOLATION;
337 case EBADF: return STATUS_INVALID_HANDLE;
338 case EBUSY: return STATUS_DEVICE_BUSY;
339 case ENOSPC: return STATUS_DISK_FULL;
340 case EPERM:
341 case EROFS:
342 case EACCES: return STATUS_ACCESS_DENIED;
343 case ENOTDIR: return STATUS_OBJECT_PATH_NOT_FOUND;
344 case ENOENT: return STATUS_OBJECT_NAME_NOT_FOUND;
345 case EISDIR: return STATUS_INVALID_DEVICE_REQUEST;
346 case EMFILE:
347 case ENFILE: return STATUS_TOO_MANY_OPENED_FILES;
348 case EINVAL: return STATUS_INVALID_PARAMETER;
349 case ENOTEMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
350 case EPIPE: return STATUS_PIPE_DISCONNECTED;
351 case EIO: return STATUS_DEVICE_NOT_READY;
352 #ifdef ENOMEDIUM
353 case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE;
354 #endif
355 case ENXIO: return STATUS_NO_SUCH_DEVICE;
356 case ENOTTY:
357 case EOPNOTSUPP:return STATUS_NOT_SUPPORTED;
358 case ECONNRESET:return STATUS_PIPE_DISCONNECTED;
359 case EFAULT: return STATUS_ACCESS_VIOLATION;
360 case ESPIPE: return STATUS_ILLEGAL_FUNCTION;
361 case ELOOP: return STATUS_REPARSE_POINT_NOT_RESOLVED;
362 #ifdef ETIME /* Missing on FreeBSD */
363 case ETIME: return STATUS_IO_TIMEOUT;
364 #endif
365 case ENOEXEC: /* ?? */
366 case EEXIST: /* ?? */
367 default:
368 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
369 return STATUS_UNSUCCESSFUL;
373 /* get space from the current directory data buffer, allocating a new one if necessary */
374 static void *get_dir_data_space( struct dir_data *data, unsigned int size )
376 struct dir_data_buffer *buffer = data->buffer;
377 void *ret;
379 if (!buffer || size > buffer->size - buffer->pos)
381 unsigned int new_size = buffer ? buffer->size * 2 : dir_data_buffer_initial_size;
382 if (new_size < size) new_size = size;
383 if (!(buffer = malloc( offsetof( struct dir_data_buffer, data[new_size] ) ))) return NULL;
384 buffer->pos = 0;
385 buffer->size = new_size;
386 buffer->next = data->buffer;
387 data->buffer = buffer;
389 ret = buffer->data + buffer->pos;
390 buffer->pos += size;
391 return ret;
394 /* add a string to the directory data buffer */
395 static const char *add_dir_data_nameA( struct dir_data *data, const char *name )
397 /* keep buffer data WCHAR-aligned */
398 char *ptr = get_dir_data_space( data, (strlen( name ) + sizeof(WCHAR)) & ~(sizeof(WCHAR) - 1) );
399 if (ptr) strcpy( ptr, name );
400 return ptr;
403 /* add a Unicode string to the directory data buffer */
404 static const WCHAR *add_dir_data_nameW( struct dir_data *data, const WCHAR *name )
406 WCHAR *ptr = get_dir_data_space( data, (wcslen( name ) + 1) * sizeof(WCHAR) );
407 if (ptr) wcscpy( ptr, name );
408 return ptr;
411 /* add an entry to the directory names array */
412 static BOOL add_dir_data_names( struct dir_data *data, const WCHAR *long_name,
413 const WCHAR *short_name, const char *unix_name )
415 static const WCHAR empty[1];
416 struct dir_data_names *names = data->names;
418 if (data->count >= data->size)
420 unsigned int new_size = max( data->size * 2, dir_data_names_initial_size );
422 if (!(names = realloc( names, new_size * sizeof(*names) ))) return FALSE;
423 data->size = new_size;
424 data->names = names;
427 if (short_name[0])
429 if (!(names[data->count].short_name = add_dir_data_nameW( data, short_name ))) return FALSE;
431 else names[data->count].short_name = empty;
433 if (!(names[data->count].long_name = add_dir_data_nameW( data, long_name ))) return FALSE;
434 if (!(names[data->count].unix_name = add_dir_data_nameA( data, unix_name ))) return FALSE;
435 data->count++;
436 return TRUE;
439 /* free the complete directory data structure */
440 static void free_dir_data( struct dir_data *data )
442 struct dir_data_buffer *buffer, *next;
444 if (!data) return;
446 for (buffer = data->buffer; buffer; buffer = next)
448 next = buffer->next;
449 free( buffer );
451 free( data->names );
452 free( data );
456 /* support for a directory queue for filesystem searches */
458 struct dir_name
460 struct list entry;
461 char name[1];
464 static struct list dir_queue = LIST_INIT( dir_queue );
466 static NTSTATUS add_dir_to_queue( const char *name )
468 int len = strlen( name ) + 1;
469 struct dir_name *dir = malloc( offsetof( struct dir_name, name[len] ));
470 if (!dir) return STATUS_NO_MEMORY;
471 strcpy( dir->name, name );
472 list_add_tail( &dir_queue, &dir->entry );
473 return STATUS_SUCCESS;
476 static NTSTATUS next_dir_in_queue( char *name )
478 struct list *head = list_head( &dir_queue );
479 if (head)
481 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
482 strcpy( name, dir->name );
483 list_remove( &dir->entry );
484 free( dir );
485 return STATUS_SUCCESS;
487 return STATUS_OBJECT_NAME_NOT_FOUND;
490 static void flush_dir_queue(void)
492 struct list *head;
494 while ((head = list_head( &dir_queue )))
496 struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
497 list_remove( &dir->entry );
498 free( dir );
503 #ifdef __ANDROID__
505 static char *unescape_field( char *str )
507 char *in, *out;
509 for (in = out = str; *in; in++, out++)
511 *out = *in;
512 if (in[0] == '\\')
514 if (in[1] == '\\')
516 out[0] = '\\';
517 in++;
519 else if (in[1] == '0' && in[2] == '4' && in[3] == '0')
521 out[0] = ' ';
522 in += 3;
524 else if (in[1] == '0' && in[2] == '1' && in[3] == '1')
526 out[0] = '\t';
527 in += 3;
529 else if (in[1] == '0' && in[2] == '1' && in[3] == '2')
531 out[0] = '\n';
532 in += 3;
534 else if (in[1] == '1' && in[2] == '3' && in[3] == '4')
536 out[0] = '\\';
537 in += 3;
541 *out = '\0';
543 return str;
546 static inline char *get_field( char **str )
548 char *ret;
550 ret = strsep( str, " \t" );
551 if (*str) *str += strspn( *str, " \t" );
553 return ret;
555 /************************************************************************
556 * getmntent_replacement
558 * getmntent replacement for Android.
560 * NB returned static buffer is not thread safe; protect with mnt_mutex.
562 static struct mntent *getmntent_replacement( FILE *f )
564 static struct mntent entry;
565 static char buf[4096];
566 char *p, *start;
570 if (!fgets( buf, sizeof(buf), f )) return NULL;
571 p = strchr( buf, '\n' );
572 if (p) *p = '\0';
573 else /* Partially unread line, move file ptr to end */
575 char tmp[1024];
576 while (fgets( tmp, sizeof(tmp), f ))
577 if (strchr( tmp, '\n' )) break;
579 start = buf + strspn( buf, " \t" );
580 } while (start[0] == '\0' || start[0] == '#');
582 p = get_field( &start );
583 entry.mnt_fsname = p ? unescape_field( p ) : (char *)"";
585 p = get_field( &start );
586 entry.mnt_dir = p ? unescape_field( p ) : (char *)"";
588 p = get_field( &start );
589 entry.mnt_type = p ? unescape_field( p ) : (char *)"";
591 p = get_field( &start );
592 entry.mnt_opts = p ? unescape_field( p ) : (char *)"";
594 p = get_field( &start );
595 entry.mnt_freq = p ? atoi(p) : 0;
597 p = get_field( &start );
598 entry.mnt_passno = p ? atoi(p) : 0;
600 return &entry;
602 #define getmntent getmntent_replacement
603 #endif
605 /***********************************************************************
606 * parse_mount_entries
608 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
611 #ifdef sun
612 #include <sys/vfstab.h>
613 static char *parse_vfstab_entries( FILE *f, dev_t dev, ino_t ino)
615 struct vfstab entry;
616 struct stat st;
617 char *device;
619 while (! getvfsent( f, &entry ))
621 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
622 if (!strcmp( entry.vfs_fstype, "nfs" ) ||
623 !strcmp( entry.vfs_fstype, "smbfs" ) ||
624 !strcmp( entry.vfs_fstype, "ncpfs" )) continue;
626 if (stat( entry.vfs_mountp, &st ) == -1) continue;
627 if (st.st_dev != dev || st.st_ino != ino) continue;
628 if (!strcmp( entry.vfs_fstype, "fd" ))
630 if ((device = strstr( entry.vfs_mntopts, "dev=" )))
632 char *p = strchr( device + 4, ',' );
633 if (p) *p = 0;
634 return device + 4;
637 else
638 return entry.vfs_special;
640 return NULL;
642 #endif
644 #ifdef linux
645 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
647 struct mntent *entry;
648 struct stat st;
649 char *device;
651 while ((entry = getmntent( f )))
653 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
654 if (!strcmp( entry->mnt_type, "nfs" ) ||
655 !strcmp( entry->mnt_type, "cifs" ) ||
656 !strcmp( entry->mnt_type, "smbfs" ) ||
657 !strcmp( entry->mnt_type, "ncpfs" )) continue;
659 if (stat( entry->mnt_dir, &st ) == -1) continue;
660 if (st.st_dev != dev || st.st_ino != ino) continue;
661 if (!strcmp( entry->mnt_type, "supermount" ))
663 if ((device = strstr( entry->mnt_opts, "dev=" )))
665 char *p = strchr( device + 4, ',' );
666 if (p) *p = 0;
667 return device + 4;
670 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
672 /* if device is a regular file check for a loop mount */
673 if ((device = strstr( entry->mnt_opts, "loop=" )))
675 char *p = strchr( device + 5, ',' );
676 if (p) *p = 0;
677 return device + 5;
680 else
681 return entry->mnt_fsname;
683 return NULL;
685 #endif
687 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
688 #include <fstab.h>
689 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
691 struct fstab *entry;
692 struct stat st;
694 while ((entry = getfsent()))
696 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
697 if (!strcmp( entry->fs_vfstype, "nfs" ) ||
698 !strcmp( entry->fs_vfstype, "smbfs" ) ||
699 !strcmp( entry->fs_vfstype, "ncpfs" )) continue;
701 if (stat( entry->fs_file, &st ) == -1) continue;
702 if (st.st_dev != dev || st.st_ino != ino) continue;
703 return entry->fs_spec;
705 return NULL;
707 #endif
709 #ifdef sun
710 #include <sys/mnttab.h>
711 static char *parse_mount_entries( FILE *f, dev_t dev, ino_t ino )
713 struct mnttab entry;
714 struct stat st;
715 char *device;
718 while (( ! getmntent( f, &entry) ))
720 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
721 if (!strcmp( entry.mnt_fstype, "nfs" ) ||
722 !strcmp( entry.mnt_fstype, "smbfs" ) ||
723 !strcmp( entry.mnt_fstype, "ncpfs" )) continue;
725 if (stat( entry.mnt_mountp, &st ) == -1) continue;
726 if (st.st_dev != dev || st.st_ino != ino) continue;
727 if (!strcmp( entry.mnt_fstype, "fd" ))
729 if ((device = strstr( entry.mnt_mntopts, "dev=" )))
731 char *p = strchr( device + 4, ',' );
732 if (p) *p = 0;
733 return device + 4;
736 else
737 return entry.mnt_special;
739 return NULL;
741 #endif
743 /***********************************************************************
744 * get_default_drive_device
746 * Return the default device to use for a given drive mount point.
748 static char *get_default_drive_device( const char *root )
750 char *ret = NULL;
752 #ifdef linux
753 FILE *f;
754 char *device = NULL;
755 int fd, res = -1;
756 struct stat st;
758 /* try to open it first to force it to get mounted */
759 if ((fd = open( root, O_RDONLY | O_DIRECTORY )) != -1)
761 res = fstat( fd, &st );
762 close( fd );
764 /* now try normal stat just in case */
765 if (res == -1) res = stat( root, &st );
766 if (res == -1) return NULL;
768 mutex_lock( &mnt_mutex );
770 #ifdef __ANDROID__
771 if ((f = fopen( "/proc/mounts", "r" )))
773 device = parse_mount_entries( f, st.st_dev, st.st_ino );
774 fclose( f );
776 #else
777 if ((f = fopen( "/etc/mtab", "r" )))
779 device = parse_mount_entries( f, st.st_dev, st.st_ino );
780 fclose( f );
782 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
783 if (!device && (f = fopen( "/etc/fstab", "r" )))
785 device = parse_mount_entries( f, st.st_dev, st.st_ino );
786 fclose( f );
788 #endif
789 if (device) ret = strdup( device );
790 mutex_unlock( &mnt_mutex );
792 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
793 char *device = NULL;
794 int fd, res = -1;
795 struct stat st;
797 /* try to open it first to force it to get mounted */
798 if ((fd = open( root, O_RDONLY )) != -1)
800 res = fstat( fd, &st );
801 close( fd );
803 /* now try normal stat just in case */
804 if (res == -1) res = stat( root, &st );
805 if (res == -1) return NULL;
807 mutex_lock( &mnt_mutex );
809 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
810 * pass NULL. Leave the argument in for symmetry.
812 device = parse_mount_entries( NULL, st.st_dev, st.st_ino );
813 if (device) ret = strdup( device );
814 mutex_unlock( &mnt_mutex );
816 #elif defined( sun )
817 FILE *f;
818 char *device = NULL;
819 int fd, res = -1;
820 struct stat st;
822 /* try to open it first to force it to get mounted */
823 if ((fd = open( root, O_RDONLY )) != -1)
825 res = fstat( fd, &st );
826 close( fd );
828 /* now try normal stat just in case */
829 if (res == -1) res = stat( root, &st );
830 if (res == -1) return NULL;
832 mutex_lock( &mnt_mutex );
834 if ((f = fopen( "/etc/mnttab", "r" )))
836 device = parse_mount_entries( f, st.st_dev, st.st_ino);
837 fclose( f );
839 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
840 if (!device && (f = fopen( "/etc/vfstab", "r" )))
842 device = parse_vfstab_entries( f, st.st_dev, st.st_ino );
843 fclose( f );
845 if (device) ret = strdup( device );
846 mutex_unlock( &mnt_mutex );
848 #elif defined(__APPLE__)
849 struct statfs *mntStat;
850 struct stat st;
851 int i;
852 int mntSize;
853 dev_t dev;
854 ino_t ino;
855 static const char path_bsd_device[] = "/dev/disk";
856 int res;
858 res = stat( root, &st );
859 if (res == -1) return NULL;
861 dev = st.st_dev;
862 ino = st.st_ino;
864 mutex_lock( &mnt_mutex );
866 mntSize = getmntinfo(&mntStat, MNT_NOWAIT);
868 for (i = 0; i < mntSize && !ret; i++)
870 if (stat(mntStat[i].f_mntonname, &st ) == -1) continue;
871 if (st.st_dev != dev || st.st_ino != ino) continue;
873 /* FIXME add support for mounted network drive */
874 if ( strncmp(mntStat[i].f_mntfromname, path_bsd_device, strlen(path_bsd_device)) == 0)
876 /* set return value to the corresponding raw BSD node */
877 ret = malloc( strlen(mntStat[i].f_mntfromname) + 2 /* 2 : r and \0 */ );
878 if (ret)
880 strcpy(ret, "/dev/r");
881 strcat(ret, mntStat[i].f_mntfromname+sizeof("/dev/")-1);
885 mutex_unlock( &mnt_mutex );
886 #else
887 static int warned;
888 if (!warned++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
889 #endif
890 return ret;
894 /***********************************************************************
895 * get_device_mount_point
897 * Return the current mount point for a device.
899 static char *get_device_mount_point( dev_t dev )
901 char *ret = NULL;
903 #ifdef linux
904 FILE *f;
906 mutex_lock( &mnt_mutex );
908 #ifdef __ANDROID__
909 if ((f = fopen( "/proc/mounts", "r" )))
910 #else
911 if ((f = fopen( "/etc/mtab", "r" )))
912 #endif
914 struct mntent *entry;
915 struct stat st;
916 char *p, *device;
918 while ((entry = getmntent( f )))
920 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
921 if (!strcmp( entry->mnt_type, "nfs" ) ||
922 !strcmp( entry->mnt_type, "cifs" ) ||
923 !strcmp( entry->mnt_type, "smbfs" ) ||
924 !strcmp( entry->mnt_type, "ncpfs" )) continue;
926 if (!strcmp( entry->mnt_type, "supermount" ))
928 if ((device = strstr( entry->mnt_opts, "dev=" )))
930 device += 4;
931 if ((p = strchr( device, ',' ))) *p = 0;
934 else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode))
936 /* if device is a regular file check for a loop mount */
937 if ((device = strstr( entry->mnt_opts, "loop=" )))
939 device += 5;
940 if ((p = strchr( device, ',' ))) *p = 0;
943 else device = entry->mnt_fsname;
945 if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
947 ret = strdup( entry->mnt_dir );
948 break;
951 fclose( f );
953 mutex_unlock( &mnt_mutex );
954 #elif defined(__APPLE__)
955 struct statfs *entry;
956 struct stat st;
957 int i, size;
959 mutex_lock( &mnt_mutex );
961 size = getmntinfo( &entry, MNT_NOWAIT );
962 for (i = 0; i < size; i++)
964 if (stat( entry[i].f_mntfromname, &st ) == -1) continue;
965 if (S_ISBLK(st.st_mode) && st.st_rdev == dev)
967 ret = strdup( entry[i].f_mntonname );
968 break;
971 mutex_unlock( &mnt_mutex );
972 #else
973 static int warned;
974 if (!warned++) FIXME( "unmounting devices not supported on this platform\n" );
975 #endif
976 return ret;
980 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
981 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
983 struct get_fsid
985 ULONG size;
986 dev_t dev;
987 fsid_t fsid;
990 struct fs_cache
992 dev_t dev;
993 fsid_t fsid;
994 BOOLEAN case_sensitive;
995 } fs_cache[64];
997 struct vol_caps
999 ULONG size;
1000 vol_capabilities_attr_t caps;
1003 /***********************************************************************
1004 * look_up_fs_cache
1006 * Checks if the specified file system is in the cache.
1008 static struct fs_cache *look_up_fs_cache( dev_t dev )
1010 int i;
1011 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1012 if (fs_cache[i].dev == dev)
1013 return fs_cache+i;
1014 return NULL;
1017 /***********************************************************************
1018 * add_fs_cache
1020 * Adds the specified file system to the cache.
1022 static void add_fs_cache( dev_t dev, fsid_t fsid, BOOLEAN case_sensitive )
1024 int i;
1025 struct fs_cache *entry = look_up_fs_cache( dev );
1026 static int once = 0;
1027 if (entry)
1029 /* Update the cache */
1030 entry->fsid = fsid;
1031 entry->case_sensitive = case_sensitive;
1032 return;
1035 /* Add a new entry */
1036 for (i = 0; i < ARRAY_SIZE( fs_cache ); i++)
1037 if (fs_cache[i].dev == 0)
1039 /* This entry is empty, use it */
1040 fs_cache[i].dev = dev;
1041 fs_cache[i].fsid = fsid;
1042 fs_cache[i].case_sensitive = case_sensitive;
1043 return;
1046 /* Cache is out of space, warn */
1047 if (!once++)
1048 WARN( "FS cache is out of space, expect performance problems\n" );
1051 /***********************************************************************
1052 * get_dir_case_sensitivity_attr
1054 * Checks if the volume containing the specified directory is case
1055 * sensitive or not. Uses getattrlist(2).
1057 static int get_dir_case_sensitivity_attr( const char *dir )
1059 char *mntpoint;
1060 struct attrlist attr;
1061 struct vol_caps caps;
1062 struct get_fsid get_fsid;
1063 struct fs_cache *entry;
1065 /* First get the FS ID of the volume */
1066 attr.bitmapcount = ATTR_BIT_MAP_COUNT;
1067 attr.reserved = 0;
1068 attr.commonattr = ATTR_CMN_DEVID|ATTR_CMN_FSID;
1069 attr.volattr = attr.dirattr = attr.fileattr = attr.forkattr = 0;
1070 get_fsid.size = 0;
1071 if (getattrlist( dir, &attr, &get_fsid, sizeof(get_fsid), 0 ) != 0 ||
1072 get_fsid.size != sizeof(get_fsid))
1073 return -1;
1074 /* Try to look it up in the cache */
1075 entry = look_up_fs_cache( get_fsid.dev );
1076 if (entry && !memcmp( &entry->fsid, &get_fsid.fsid, sizeof(fsid_t) ))
1077 /* Cache lookup succeeded */
1078 return entry->case_sensitive;
1079 /* Cache is stale at this point, we have to update it */
1081 mntpoint = get_device_mount_point( get_fsid.dev );
1082 /* Now look up the case-sensitivity */
1083 attr.commonattr = 0;
1084 attr.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES;
1085 if (getattrlist( mntpoint, &attr, &caps, sizeof(caps), 0 ) < 0)
1087 free( mntpoint );
1088 add_fs_cache( get_fsid.dev, get_fsid.fsid, TRUE );
1089 return TRUE;
1091 free( mntpoint );
1092 if (caps.size == sizeof(caps) &&
1093 (caps.caps.valid[VOL_CAPABILITIES_FORMAT] &
1094 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING)) ==
1095 (VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING))
1097 BOOLEAN ret;
1099 if ((caps.caps.capabilities[VOL_CAPABILITIES_FORMAT] &
1100 VOL_CAP_FMT_CASE_SENSITIVE) != VOL_CAP_FMT_CASE_SENSITIVE)
1101 ret = FALSE;
1102 else
1103 ret = TRUE;
1104 /* Update the cache */
1105 add_fs_cache( get_fsid.dev, get_fsid.fsid, ret );
1106 return ret;
1108 return FALSE;
1110 #endif
1112 /***********************************************************************
1113 * get_dir_case_sensitivity_stat
1115 * Checks if the volume containing the specified directory is case
1116 * sensitive or not. Uses (f)statfs(2), statvfs(2), fstatat(2), or ioctl(2).
1118 static BOOLEAN get_dir_case_sensitivity_stat( const char *dir )
1120 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1121 struct statfs stfs;
1123 if (statfs( dir, &stfs ) == -1) return TRUE;
1124 /* Assume these file systems are always case insensitive.*/
1125 if (!strcmp( stfs.f_fstypename, "fusefs" ) &&
1126 !strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1127 return FALSE;
1128 /* msdosfs was case-insensitive since FreeBSD 8, if not earlier */
1129 if (!strcmp( stfs.f_fstypename, "msdosfs" ) ||
1130 /* older CIFS protocol versions uppercase filename on the client,
1131 * newer versions should be case-insensitive on the server anyway */
1132 !strcmp( stfs.f_fstypename, "smbfs" ))
1133 return FALSE;
1134 /* no ntfs-3g: modern fusefs has no way to report the filesystem on FreeBSD
1135 * no cd9660 or udf, they're case-sensitive on FreeBSD
1137 #ifdef __APPLE__
1138 if (!strcmp( stfs.f_fstypename, "msdos" ) ||
1139 !strcmp( stfs.f_fstypename, "cd9660" ) ||
1140 !strcmp( stfs.f_fstypename, "udf" ) ||
1141 !strcmp( stfs.f_fstypename, "ntfs" ))
1142 return FALSE;
1143 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1144 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_fssubtype == 0 ||
1145 stfs.f_fssubtype == 1 ||
1146 stfs.f_fssubtype == 128))
1147 return FALSE;
1148 #else
1149 /* The field says "reserved", but a quick look at the kernel source
1150 * tells us that this "reserved" field is really the same as the
1151 * "fssubtype" field from the inode64 structure (see munge_statfs()
1152 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1154 if (!strcmp( stfs.f_fstypename, "hfs" ) && (stfs.f_reserved1 == 0 ||
1155 stfs.f_reserved1 == 1 ||
1156 stfs.f_reserved1 == 128))
1157 return FALSE;
1158 #endif
1159 #endif
1160 return TRUE;
1162 #elif defined(__NetBSD__)
1163 struct statvfs stfs;
1165 if (statvfs( dir, &stfs ) == -1) return TRUE;
1166 /* Only assume CIOPFS is case insensitive. */
1167 if (strcmp( stfs.f_fstypename, "fusefs" ) ||
1168 strncmp( stfs.f_mntfromname, "ciopfs", 5 ))
1169 return FALSE;
1170 return TRUE;
1172 #elif defined(__linux__)
1173 BOOLEAN sens = TRUE;
1174 struct statfs stfs;
1175 struct stat st;
1176 int fd, flags;
1178 if ((fd = open( dir, O_RDONLY | O_NONBLOCK | O_LARGEFILE )) == -1)
1179 return TRUE;
1181 if (ioctl( fd, EXT2_IOC_GETFLAGS, &flags ) != -1 && (flags & EXT4_CASEFOLD_FL))
1183 sens = FALSE;
1185 else if (fstatfs( fd, &stfs ) == 0 && /* CIOPFS is case insensitive. Instead of */
1186 stfs.f_type == 0x65735546 /* FUSE_SUPER_MAGIC */ && /* parsing mtab to discover if the FUSE FS */
1187 fstatat( fd, ".ciopfs", &st, AT_NO_AUTOMOUNT ) == 0) /* is CIOPFS, look for .ciopfs in the dir. */
1189 sens = FALSE;
1192 close( fd );
1193 return sens;
1194 #else
1195 return TRUE;
1196 #endif
1200 /***********************************************************************
1201 * get_dir_case_sensitivity
1203 * Checks if the volume containing the specified directory is case
1204 * sensitive or not. Uses multiple methods, depending on platform.
1206 static BOOLEAN get_dir_case_sensitivity( const char *dir )
1208 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1209 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1210 int case_sensitive = get_dir_case_sensitivity_attr( dir );
1211 if (case_sensitive != -1) return case_sensitive;
1212 #endif
1213 return get_dir_case_sensitivity_stat( dir );
1217 /***********************************************************************
1218 * is_hidden_file
1220 * Check if the specified file should be hidden based on its name and the show dot files option.
1222 static BOOL is_hidden_file( const UNICODE_STRING *name )
1224 WCHAR *p, *end;
1226 if (show_dot_files) return FALSE;
1228 end = p = name->Buffer + name->Length/sizeof(WCHAR);
1229 while (p > name->Buffer && p[-1] == '\\') p--;
1230 while (p > name->Buffer && p[-1] != '\\') p--;
1231 return (p < end && *p == '.');
1235 /***********************************************************************
1236 * hash_short_file_name
1238 * Transform a Unix file name into a hashed DOS name. If the name is not a valid
1239 * DOS name, it is replaced by a hashed version that fits in 8.3 format.
1240 * 'buffer' must be at least 12 characters long.
1241 * Returns length of short name in bytes; short name is NOT null-terminated.
1243 static ULONG hash_short_file_name( const WCHAR *name, int length, LPWSTR buffer )
1245 static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1247 LPCWSTR p, ext, end = name + length;
1248 LPWSTR dst;
1249 unsigned short hash;
1250 int i;
1252 /* Compute the hash code of the file name */
1253 /* If you know something about hash functions, feel free to */
1254 /* insert a better algorithm here... */
1255 if (!is_case_sensitive)
1257 for (p = name, hash = 0xbeef; p < end - 1; p++)
1258 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p) ^ (towlower(p[1]) << 8);
1259 hash = (hash<<3) ^ (hash>>5) ^ towlower(*p); /* Last character */
1261 else
1263 for (p = name, hash = 0xbeef; p < end - 1; p++)
1264 hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
1265 hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
1268 /* Find last dot for start of the extension */
1269 for (p = name + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
1271 /* Copy first 4 chars, replacing invalid chars with '_' */
1272 for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
1274 if (p == end || p == ext) break;
1275 *dst++ = is_invalid_dos_char(*p) ? '_' : *p;
1277 /* Pad to 5 chars with '~' */
1278 while (i-- >= 0) *dst++ = '~';
1280 /* Insert hash code converted to 3 ASCII chars */
1281 *dst++ = hash_chars[(hash >> 10) & 0x1f];
1282 *dst++ = hash_chars[(hash >> 5) & 0x1f];
1283 *dst++ = hash_chars[hash & 0x1f];
1285 /* Copy the first 3 chars of the extension (if any) */
1286 if (ext)
1288 *dst++ = '.';
1289 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
1290 *dst++ = is_invalid_dos_char(*ext) ? '_' : *ext;
1292 return dst - buffer;
1296 /***********************************************************************
1297 * match_filename
1299 * Check a long file name against a mask.
1301 * Tests (done in W95 DOS shell - case insensitive):
1302 * *.txt test1.test.txt *
1303 * *st1* test1.txt *
1304 * *.t??????.t* test1.ta.tornado.txt *
1305 * *tornado* test1.ta.tornado.txt *
1306 * t*t test1.ta.tornado.txt *
1307 * ?est* test1.txt *
1308 * ?est??? test1.txt -
1309 * *test1.txt* test1.txt *
1310 * h?l?o*t.dat hellothisisatest.dat *
1312 static BOOLEAN match_filename( const WCHAR *name, int length, const UNICODE_STRING *mask_str )
1314 BOOL mismatch;
1315 const WCHAR *mask = mask_str->Buffer;
1316 const WCHAR *name_end = name + length;
1317 const WCHAR *mask_end = mask + mask_str->Length / sizeof(WCHAR);
1318 const WCHAR *lastjoker = NULL;
1319 const WCHAR *next_to_retry = NULL;
1321 while (name < name_end && mask < mask_end)
1323 switch(*mask)
1325 case '*':
1326 mask++;
1327 while (mask < mask_end && *mask == '*') mask++; /* Skip consecutive '*' */
1328 if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */
1329 lastjoker = mask;
1331 /* skip to the next match after the joker(s) */
1332 if (is_case_sensitive)
1333 while (name < name_end && (*name != *mask)) name++;
1334 else
1335 while (name < name_end && (towupper(*name) != towupper(*mask))) name++;
1336 next_to_retry = name;
1337 break;
1338 case '?':
1339 case '>':
1340 mask++;
1341 name++;
1342 break;
1343 default:
1344 if (is_case_sensitive) mismatch = (*mask != *name);
1345 else mismatch = (towupper(*mask) != towupper(*name));
1347 if (!mismatch)
1349 mask++;
1350 name++;
1351 if (mask == mask_end)
1353 if (name == name_end) return TRUE;
1354 if (lastjoker) mask = lastjoker;
1357 else /* mismatch ! */
1359 if (lastjoker) /* we had an '*', so we can try unlimitedly */
1361 mask = lastjoker;
1363 /* this scan sequence was a mismatch, so restart
1364 * 1 char after the first char we checked last time */
1365 next_to_retry++;
1366 name = next_to_retry;
1368 else return FALSE; /* bad luck */
1370 break;
1373 while (mask < mask_end && ((*mask == '.') || (*mask == '*')))
1374 mask++; /* Ignore trailing '.' or '*' in mask */
1375 return (name == name_end && mask == mask_end);
1379 /***********************************************************************
1380 * is_legal_8dot3_name
1382 * Simplified version of RtlIsNameLegalDOS8Dot3.
1384 static BOOLEAN is_legal_8dot3_name( const WCHAR *name, int len )
1386 static const WCHAR invalid_chars[] = { INVALID_DOS_CHARS,':','/','\\',0 };
1387 int i, dot = -1;
1389 if (len > 12) return FALSE;
1391 /* a starting . is invalid, except for . and .. */
1392 if (len > 0 && name[0] == '.') return (len == 1 || (len == 2 && name[1] == '.'));
1394 for (i = 0; i < len; i++)
1396 if (name[i] > 0x7f) return FALSE;
1397 if (wcschr( invalid_chars, name[i] )) return FALSE;
1398 if (name[i] == '.')
1400 if (dot != -1) return FALSE;
1401 dot = i;
1405 if (dot == -1) return (len <= 8);
1406 if (dot > 8) return FALSE;
1407 return (len - dot > 1 && len - dot < 5);
1411 /***********************************************************************
1412 * append_entry
1414 * Add a file to the directory data if it matches the mask.
1416 static BOOL append_entry( struct dir_data *data, const char *long_name,
1417 const char *short_name, const UNICODE_STRING *mask )
1419 int long_len, short_len;
1420 WCHAR long_nameW[MAX_DIR_ENTRY_LEN + 1];
1421 WCHAR short_nameW[13];
1423 long_len = ntdll_umbstowcs( long_name, strlen(long_name), long_nameW, ARRAY_SIZE(long_nameW) );
1424 if (long_len == ARRAY_SIZE(long_nameW)) return TRUE;
1425 long_nameW[long_len] = 0;
1427 if (short_name)
1429 short_len = ntdll_umbstowcs( short_name, strlen(short_name),
1430 short_nameW, ARRAY_SIZE( short_nameW ) - 1 );
1432 else /* generate a short name if necessary */
1434 short_len = 0;
1435 if (!is_legal_8dot3_name( long_nameW, long_len ))
1436 short_len = hash_short_file_name( long_nameW, long_len, short_nameW );
1438 short_nameW[short_len] = 0;
1439 wcsupr( short_nameW );
1441 TRACE( "long %s short %s mask %s\n",
1442 debugstr_w( long_nameW ), debugstr_w( short_nameW ), debugstr_us( mask ));
1444 if (mask && !match_filename( long_nameW, long_len, mask ))
1446 if (!short_len) return TRUE; /* no short name to match */
1447 if (!match_filename( short_nameW, short_len, mask )) return TRUE;
1450 return add_dir_data_names( data, long_nameW, short_nameW, long_name );
1454 /* fetch the attributes of a file */
1455 static inline ULONG get_file_attributes( const struct stat *st )
1457 ULONG attr;
1459 if (S_ISDIR(st->st_mode))
1460 attr = FILE_ATTRIBUTE_DIRECTORY;
1461 else
1462 attr = FILE_ATTRIBUTE_ARCHIVE;
1463 if (!(st->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
1464 attr |= FILE_ATTRIBUTE_READONLY;
1465 return attr;
1469 static BOOL fd_is_mount_point( int fd, const struct stat *st )
1471 struct stat parent;
1472 return S_ISDIR( st->st_mode ) && !fstatat( fd, "..", &parent, 0 )
1473 && (parent.st_dev != st->st_dev || parent.st_ino == st->st_ino);
1477 /* get the stat info and file attributes for a file (by file descriptor) */
1478 static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr )
1480 int ret;
1482 *attr = 0;
1483 ret = fstat( fd, st );
1484 if (ret == -1) return ret;
1485 *attr |= get_file_attributes( st );
1486 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1487 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st ))
1488 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1489 return ret;
1493 /* get the stat info and file attributes for a file (by name) */
1494 static int get_file_info( const char *path, struct stat *st, ULONG *attr )
1496 char *parent_path;
1497 int ret;
1499 *attr = 0;
1500 ret = lstat( path, st );
1501 if (ret == -1) return ret;
1502 if (S_ISLNK( st->st_mode ))
1504 ret = stat( path, st );
1505 if (ret == -1) return ret;
1506 /* is a symbolic link and a directory, consider these "reparse points" */
1507 if (S_ISDIR( st->st_mode )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1509 else if (S_ISDIR( st->st_mode ) && (parent_path = malloc( strlen(path) + 4 )))
1511 struct stat parent_st;
1513 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1514 strcpy( parent_path, path );
1515 strcat( parent_path, "/.." );
1516 if (!stat( parent_path, &parent_st )
1517 && (st->st_dev != parent_st.st_dev || st->st_ino == parent_st.st_ino))
1518 *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
1520 free( parent_path );
1522 *attr |= get_file_attributes( st );
1523 return ret;
1527 #if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
1528 static int futimens( int fd, const struct timespec spec[2] )
1530 return syscall( __NR_utimensat, fd, NULL, spec, 0 );
1532 #define HAVE_FUTIMENS
1533 #endif /* __ANDROID__ */
1535 #ifndef UTIME_OMIT
1536 #define UTIME_OMIT ((1 << 30) - 2)
1537 #endif
1539 static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
1540 const LARGE_INTEGER *atime, NTSTATUS *status )
1542 #ifdef HAVE_FUTIMENS
1543 struct timespec tv[2];
1545 tv[0].tv_sec = tv[1].tv_sec = 0;
1546 tv[0].tv_nsec = tv[1].tv_nsec = UTIME_OMIT;
1547 if (atime->QuadPart)
1549 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1550 tv[0].tv_nsec = (atime->QuadPart % 10000000) * 100;
1552 if (mtime->QuadPart)
1554 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1555 tv[1].tv_nsec = (mtime->QuadPart % 10000000) * 100;
1557 #ifdef __APPLE__
1558 if (!&futimens) return FALSE;
1559 #endif
1560 if (futimens( fd, tv ) == -1) *status = errno_to_status( errno );
1561 else *status = STATUS_SUCCESS;
1562 return TRUE;
1563 #else
1564 return FALSE;
1565 #endif
1569 static NTSTATUS set_file_times( int fd, const LARGE_INTEGER *mtime, const LARGE_INTEGER *atime )
1571 NTSTATUS status = STATUS_SUCCESS;
1572 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1573 struct timeval tv[2];
1574 struct stat st;
1575 #endif
1577 if (set_file_times_precise( fd, mtime, atime, &status ))
1578 return status;
1580 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1581 if (!atime->QuadPart || !mtime->QuadPart)
1584 tv[0].tv_sec = tv[0].tv_usec = 0;
1585 tv[1].tv_sec = tv[1].tv_usec = 0;
1586 if (!fstat( fd, &st ))
1588 tv[0].tv_sec = st.st_atime;
1589 tv[1].tv_sec = st.st_mtime;
1590 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1591 tv[0].tv_usec = st.st_atim.tv_nsec / 1000;
1592 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1593 tv[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
1594 #endif
1595 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1596 tv[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1597 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1598 tv[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
1599 #endif
1602 if (atime->QuadPart)
1604 tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
1605 tv[0].tv_usec = (atime->QuadPart % 10000000) / 10;
1607 if (mtime->QuadPart)
1609 tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
1610 tv[1].tv_usec = (mtime->QuadPart % 10000000) / 10;
1612 #ifdef HAVE_FUTIMES
1613 if (futimes( fd, tv ) == -1) status = errno_to_status( errno );
1614 #elif defined(HAVE_FUTIMESAT)
1615 if (futimesat( fd, NULL, tv ) == -1) status = errno_to_status( errno );
1616 #endif
1618 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1619 FIXME( "setting file times not supported\n" );
1620 status = STATUS_NOT_IMPLEMENTED;
1621 #endif
1622 return status;
1626 static inline void get_file_times( const struct stat *st, LARGE_INTEGER *mtime, LARGE_INTEGER *ctime,
1627 LARGE_INTEGER *atime, LARGE_INTEGER *creation )
1629 mtime->QuadPart = ticks_from_time_t( st->st_mtime );
1630 ctime->QuadPart = ticks_from_time_t( st->st_ctime );
1631 atime->QuadPart = ticks_from_time_t( st->st_atime );
1632 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1633 mtime->QuadPart += st->st_mtim.tv_nsec / 100;
1634 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1635 mtime->QuadPart += st->st_mtimespec.tv_nsec / 100;
1636 #endif
1637 #ifdef HAVE_STRUCT_STAT_ST_CTIM
1638 ctime->QuadPart += st->st_ctim.tv_nsec / 100;
1639 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1640 ctime->QuadPart += st->st_ctimespec.tv_nsec / 100;
1641 #endif
1642 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1643 atime->QuadPart += st->st_atim.tv_nsec / 100;
1644 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1645 atime->QuadPart += st->st_atimespec.tv_nsec / 100;
1646 #endif
1647 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1648 creation->QuadPart = ticks_from_time_t( st->st_birthtime );
1649 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
1650 creation->QuadPart += st->st_birthtim.tv_nsec / 100;
1651 #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1652 creation->QuadPart += st->st_birthtimespec.tv_nsec / 100;
1653 #endif
1654 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
1655 creation->QuadPart = ticks_from_time_t( st->__st_birthtime );
1656 #ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
1657 creation->QuadPart += st->__st_birthtim.tv_nsec / 100;
1658 #endif
1659 #else
1660 *creation = *mtime;
1661 #endif
1665 /* fill in the file information that depends on the stat and attribute info */
1666 static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr,
1667 FILE_INFORMATION_CLASS class )
1669 switch (class)
1671 case FileBasicInformation:
1673 FILE_BASIC_INFORMATION *info = ptr;
1675 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1676 &info->LastAccessTime, &info->CreationTime );
1677 info->FileAttributes = attr;
1679 break;
1680 case FileStandardInformation:
1682 FILE_STANDARD_INFORMATION *info = ptr;
1684 if ((info->Directory = S_ISDIR(st->st_mode)))
1686 info->AllocationSize.QuadPart = 0;
1687 info->EndOfFile.QuadPart = 0;
1688 info->NumberOfLinks = 1;
1690 else
1692 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1693 info->EndOfFile.QuadPart = st->st_size;
1694 info->NumberOfLinks = st->st_nlink;
1697 break;
1698 case FileInternalInformation:
1700 FILE_INTERNAL_INFORMATION *info = ptr;
1701 info->IndexNumber.QuadPart = st->st_ino;
1703 break;
1704 case FileEndOfFileInformation:
1706 FILE_END_OF_FILE_INFORMATION *info = ptr;
1707 info->EndOfFile.QuadPart = S_ISDIR(st->st_mode) ? 0 : st->st_size;
1709 break;
1710 case FileAllInformation:
1712 FILE_ALL_INFORMATION *info = ptr;
1713 fill_file_info( st, attr, &info->BasicInformation, FileBasicInformation );
1714 fill_file_info( st, attr, &info->StandardInformation, FileStandardInformation );
1715 fill_file_info( st, attr, &info->InternalInformation, FileInternalInformation );
1717 break;
1718 /* all directory structures start with the FileDirectoryInformation layout */
1719 case FileBothDirectoryInformation:
1720 case FileFullDirectoryInformation:
1721 case FileDirectoryInformation:
1723 FILE_DIRECTORY_INFORMATION *info = ptr;
1725 get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
1726 &info->LastAccessTime, &info->CreationTime );
1727 if (S_ISDIR(st->st_mode))
1729 info->AllocationSize.QuadPart = 0;
1730 info->EndOfFile.QuadPart = 0;
1732 else
1734 info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
1735 info->EndOfFile.QuadPart = st->st_size;
1737 info->FileAttributes = attr;
1739 break;
1740 case FileIdFullDirectoryInformation:
1742 FILE_ID_FULL_DIRECTORY_INFORMATION *info = ptr;
1743 info->FileId.QuadPart = st->st_ino;
1744 fill_file_info( st, attr, info, FileDirectoryInformation );
1746 break;
1747 case FileIdBothDirectoryInformation:
1749 FILE_ID_BOTH_DIRECTORY_INFORMATION *info = ptr;
1750 info->FileId.QuadPart = st->st_ino;
1751 fill_file_info( st, attr, info, FileDirectoryInformation );
1753 break;
1754 case FileIdGlobalTxDirectoryInformation:
1756 FILE_ID_GLOBAL_TX_DIR_INFORMATION *info = ptr;
1757 info->FileId.QuadPart = st->st_ino;
1758 fill_file_info( st, attr, info, FileDirectoryInformation );
1760 break;
1762 default:
1763 return STATUS_INVALID_INFO_CLASS;
1765 return STATUS_SUCCESS;
1769 static NTSTATUS server_get_unix_name( HANDLE handle, char **unix_name )
1771 data_size_t size = 1024;
1772 NTSTATUS ret;
1773 char *name;
1775 for (;;)
1777 if (!(name = malloc( size + 1 ))) return STATUS_NO_MEMORY;
1779 SERVER_START_REQ( get_handle_unix_name )
1781 req->handle = wine_server_obj_handle( handle );
1782 wine_server_set_reply( req, name, size );
1783 ret = wine_server_call( req );
1784 size = reply->name_len;
1786 SERVER_END_REQ;
1788 if (!ret)
1790 name[size] = 0;
1791 *unix_name = name;
1792 break;
1794 free( name );
1795 if (ret != STATUS_BUFFER_OVERFLOW) break;
1797 return ret;
1800 static NTSTATUS fill_name_info( const char *unix_name, FILE_NAME_INFORMATION *info, LONG *name_len )
1802 WCHAR *nt_name;
1803 NTSTATUS status;
1805 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
1807 const WCHAR *ptr = nt_name;
1808 const WCHAR *end = ptr + wcslen( nt_name );
1810 /* Skip the volume mount point. */
1811 while (ptr != end && *ptr == '\\') ++ptr;
1812 while (ptr != end && *ptr != '\\') ++ptr;
1813 while (ptr != end && *ptr == '\\') ++ptr;
1814 while (ptr != end && *ptr != '\\') ++ptr;
1816 info->FileNameLength = (end - ptr) * sizeof(WCHAR);
1817 if (*name_len < info->FileNameLength) status = STATUS_BUFFER_OVERFLOW;
1818 else *name_len = info->FileNameLength;
1820 memcpy( info->FileName, ptr, *name_len );
1821 free( nt_name );
1824 return status;
1828 static NTSTATUS server_get_file_info( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer,
1829 ULONG length, FILE_INFORMATION_CLASS info_class )
1831 SERVER_START_REQ( get_file_info )
1833 req->handle = wine_server_obj_handle( handle );
1834 req->info_class = info_class;
1835 wine_server_set_reply( req, buffer, length );
1836 io->u.Status = wine_server_call( req );
1837 io->Information = wine_server_reply_size( reply );
1839 SERVER_END_REQ;
1840 if (io->u.Status == STATUS_NOT_IMPLEMENTED)
1841 FIXME( "Unsupported info class %x\n", info_class );
1842 return io->u.Status;
1847 /* retrieve device/inode number for all the drives */
1848 static unsigned int get_drives_info( struct file_identity info[MAX_DOS_DRIVES] )
1850 static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
1851 static struct file_identity cache[MAX_DOS_DRIVES];
1852 static time_t last_update;
1853 static unsigned int nb_drives;
1854 unsigned int ret;
1855 time_t now = time(NULL);
1857 mutex_lock( &cache_mutex );
1858 if (now != last_update)
1860 char *buffer, *p;
1861 struct stat st;
1862 unsigned int i;
1864 if ((buffer = malloc( strlen(config_dir) + sizeof("/dosdevices/a:") )))
1866 strcpy( buffer, config_dir );
1867 strcat( buffer, "/dosdevices/a:" );
1868 p = buffer + strlen(buffer) - 2;
1870 for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
1872 *p = 'a' + i;
1873 if (!stat( buffer, &st ))
1875 cache[i].dev = st.st_dev;
1876 cache[i].ino = st.st_ino;
1877 nb_drives++;
1879 else
1881 cache[i].dev = 0;
1882 cache[i].ino = 0;
1885 free( buffer );
1887 last_update = now;
1889 memcpy( info, cache, sizeof(cache) );
1890 ret = nb_drives;
1891 mutex_unlock( &cache_mutex );
1892 return ret;
1896 /* Find a DOS device which can act as the root of "path".
1897 * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
1898 static int find_dos_device( const char *path )
1900 int len = strlen(path);
1901 int drive;
1902 char *buffer;
1903 struct stat st;
1904 struct file_identity info[MAX_DOS_DRIVES];
1905 dev_t dev_id;
1907 if (!get_drives_info( info )) return -1;
1909 if (stat( path, &st ) < 0) return -1;
1910 dev_id = st.st_dev;
1912 /* strip off trailing slashes */
1913 while (len > 1 && path[len - 1] == '/') len--;
1915 /* make a copy of the path */
1916 if (!(buffer = malloc( len + 1 ))) return -1;
1917 memcpy( buffer, path, len );
1918 buffer[len] = 0;
1920 for (;;)
1922 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
1924 if (st.st_dev != dev_id) break;
1926 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1928 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
1930 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
1931 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
1932 free( buffer );
1933 return drive;
1937 if (len <= 1) break; /* reached root */
1938 while (len > 1 && path[len - 1] != '/') len--;
1939 while (len > 1 && path[len - 1] == '/') len--;
1940 buffer[len] = 0;
1942 free( buffer );
1943 return -1;
1946 static NTSTATUS get_mountmgr_fs_info( HANDLE handle, int fd, struct mountmgr_unix_drive *drive, ULONG size )
1948 OBJECT_ATTRIBUTES attr;
1949 UNICODE_STRING string;
1950 char *unix_name;
1951 IO_STATUS_BLOCK io;
1952 HANDLE mountmgr;
1953 NTSTATUS status;
1954 int letter;
1956 if ((status = server_get_unix_name( handle, &unix_name ))) return status;
1957 letter = find_dos_device( unix_name );
1958 free( unix_name );
1960 memset( drive, 0, sizeof(*drive) );
1961 if (letter == -1)
1963 struct stat st;
1965 fstat( fd, &st );
1966 drive->unix_dev = st.st_rdev ? st.st_rdev : st.st_dev;
1968 else
1969 drive->letter = 'a' + letter;
1971 init_unicode_string( &string, MOUNTMGR_DEVICE_NAME );
1972 InitializeObjectAttributes( &attr, &string, 0, NULL, NULL );
1973 status = NtOpenFile( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr, &io,
1974 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT );
1975 if (status) return status;
1977 status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE,
1978 drive, sizeof(*drive), drive, size );
1979 NtClose( mountmgr );
1980 if (status == STATUS_BUFFER_OVERFLOW) status = STATUS_SUCCESS;
1981 else if (status) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status);
1982 return status;
1986 /***********************************************************************
1987 * get_dir_data_entry
1989 * Return a directory entry from the cached data.
1991 static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, IO_STATUS_BLOCK *io,
1992 ULONG max_length, FILE_INFORMATION_CLASS class,
1993 union file_directory_info **last_info )
1995 const struct dir_data_names *names = &dir_data->names[dir_data->pos];
1996 union file_directory_info *info;
1997 struct stat st;
1998 ULONG name_len, start, dir_size, attributes;
2000 if (get_file_info( names->unix_name, &st, &attributes ) == -1)
2002 TRACE( "file no longer exists %s\n", names->unix_name );
2003 return STATUS_SUCCESS;
2005 if (is_ignored_file( &st ))
2007 TRACE( "ignoring file %s\n", names->unix_name );
2008 return STATUS_SUCCESS;
2010 start = dir_info_align( io->Information );
2011 dir_size = dir_info_size( class, 0 );
2012 if (start + dir_size > max_length) return STATUS_MORE_ENTRIES;
2014 max_length -= start + dir_size;
2015 name_len = wcslen( names->long_name ) * sizeof(WCHAR);
2016 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2017 if (*last_info && name_len > max_length) return STATUS_MORE_ENTRIES;
2019 info = (union file_directory_info *)((char *)info_ptr + start);
2020 info->dir.NextEntryOffset = 0;
2021 info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */
2023 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2024 if (class != FileNamesInformation)
2026 if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */
2028 if (!show_dot_files && names->long_name[0] == '.' && names->long_name[1] &&
2029 (names->long_name[1] != '.' || names->long_name[2]))
2030 attributes |= FILE_ATTRIBUTE_HIDDEN;
2032 fill_file_info( &st, attributes, info, class );
2035 switch (class)
2037 case FileDirectoryInformation:
2038 info->dir.FileNameLength = name_len;
2039 break;
2041 case FileFullDirectoryInformation:
2042 info->full.EaSize = 0; /* FIXME */
2043 info->full.FileNameLength = name_len;
2044 break;
2046 case FileIdFullDirectoryInformation:
2047 info->id_full.EaSize = 0; /* FIXME */
2048 info->id_full.FileNameLength = name_len;
2049 break;
2051 case FileBothDirectoryInformation:
2052 info->both.EaSize = 0; /* FIXME */
2053 info->both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2054 memcpy( info->both.ShortName, names->short_name, info->both.ShortNameLength );
2055 info->both.FileNameLength = name_len;
2056 break;
2058 case FileIdBothDirectoryInformation:
2059 info->id_both.EaSize = 0; /* FIXME */
2060 info->id_both.ShortNameLength = wcslen( names->short_name ) * sizeof(WCHAR);
2061 memcpy( info->id_both.ShortName, names->short_name, info->id_both.ShortNameLength );
2062 info->id_both.FileNameLength = name_len;
2063 break;
2065 case FileIdGlobalTxDirectoryInformation:
2066 info->id_tx.TxInfoFlags = 0;
2067 info->id_tx.FileNameLength = name_len;
2068 break;
2070 case FileNamesInformation:
2071 info->names.FileNameLength = name_len;
2072 break;
2074 default:
2075 assert(0);
2076 return 0;
2079 memcpy( (char *)info + dir_size, names->long_name, min( name_len, max_length ) );
2080 io->Information = start + dir_size + min( name_len, max_length );
2081 if (*last_info) (*last_info)->next = (char *)info - (char *)*last_info;
2082 *last_info = info;
2083 return name_len > max_length ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
2086 #ifdef VFAT_IOCTL_READDIR_BOTH
2088 /***********************************************************************
2089 * read_directory_vfat
2091 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2093 static NTSTATUS read_directory_data_vfat( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2095 char *short_name, *long_name;
2096 KERNEL_DIRENT de[2];
2097 NTSTATUS status = STATUS_NO_MEMORY;
2098 off_t old_pos = lseek( fd, 0, SEEK_CUR );
2100 lseek( fd, 0, SEEK_SET );
2102 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1)
2104 if (errno != ENOENT)
2106 status = STATUS_NOT_SUPPORTED;
2107 goto done;
2109 de[0].d_reclen = 0;
2112 if (!append_entry( data, ".", NULL, mask )) goto done;
2113 if (!append_entry( data, "..", NULL, mask )) goto done;
2115 while (de[0].d_reclen)
2117 if (strcmp( de[0].d_name, "." ) && strcmp( de[0].d_name, ".." ))
2119 if (de[1].d_name[0])
2121 short_name = de[0].d_name;
2122 long_name = de[1].d_name;
2124 else
2126 long_name = de[0].d_name;
2127 short_name = NULL;
2129 if (!append_entry( data, long_name, short_name, mask )) goto done;
2131 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)de ) == -1) break;
2133 status = STATUS_SUCCESS;
2134 done:
2135 lseek( fd, old_pos, SEEK_SET );
2136 return status;
2138 #endif /* VFAT_IOCTL_READDIR_BOTH */
2141 #ifdef HAVE_GETATTRLIST
2142 /***********************************************************************
2143 * read_directory_getattrlist
2145 * Read a single file from a directory by determining whether the file
2146 * identified by mask exists using getattrlist.
2148 static NTSTATUS read_directory_data_getattrlist( struct dir_data *data, const char *unix_name )
2150 struct attrlist attrlist;
2151 #include "pshpack4.h"
2152 struct
2154 u_int32_t length;
2155 struct attrreference name_reference;
2156 fsobj_type_t type;
2157 char name[NAME_MAX * 3 + 1];
2158 } buffer;
2159 #include "poppack.h"
2161 memset( &attrlist, 0, sizeof(attrlist) );
2162 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
2163 attrlist.commonattr = ATTR_CMN_NAME | ATTR_CMN_OBJTYPE;
2164 if (getattrlist( unix_name, &attrlist, &buffer, sizeof(buffer), FSOPT_NOFOLLOW ) == -1)
2165 return STATUS_NO_SUCH_FILE;
2166 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2167 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2168 if (buffer.type == VLNK)
2170 u_int32_t dummy;
2171 attrlist.commonattr = 0;
2172 if (getattrlist( unix_name, &attrlist, &dummy, sizeof(dummy), 0 ) == -1)
2173 return STATUS_NO_SUCH_FILE;
2176 TRACE( "found %s\n", buffer.name );
2178 if (!append_entry( data, buffer.name, NULL, NULL )) return STATUS_NO_MEMORY;
2180 return STATUS_SUCCESS;
2182 #endif /* HAVE_GETATTRLIST */
2185 /***********************************************************************
2186 * read_directory_stat
2188 * Read a single file from a directory by determining whether the file
2189 * identified by mask exists using stat.
2191 static NTSTATUS read_directory_data_stat( struct dir_data *data, const char *unix_name )
2193 struct stat st;
2195 /* if the file system is not case sensitive we can't find the actual name through stat() */
2196 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE;
2197 if (stat( unix_name, &st ) == -1) return STATUS_NO_SUCH_FILE;
2199 TRACE( "found %s\n", unix_name );
2201 if (!append_entry( data, unix_name, NULL, NULL )) return STATUS_NO_MEMORY;
2203 return STATUS_SUCCESS;
2207 /***********************************************************************
2208 * read_directory_readdir
2210 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2212 static NTSTATUS read_directory_data_readdir( struct dir_data *data, const UNICODE_STRING *mask )
2214 struct dirent *de;
2215 NTSTATUS status = STATUS_NO_MEMORY;
2216 DIR *dir = opendir( "." );
2218 if (!dir) return STATUS_NO_SUCH_FILE;
2220 if (!append_entry( data, ".", NULL, mask )) goto done;
2221 if (!append_entry( data, "..", NULL, mask )) goto done;
2222 while ((de = readdir( dir )))
2224 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2225 if (!append_entry( data, de->d_name, NULL, mask )) goto done;
2227 status = STATUS_SUCCESS;
2229 done:
2230 closedir( dir );
2231 return status;
2235 /***********************************************************************
2236 * read_directory_data
2238 * Read the full contents of a directory, using one of the above helper functions.
2240 static NTSTATUS read_directory_data( struct dir_data *data, int fd, const UNICODE_STRING *mask )
2242 NTSTATUS status;
2244 #ifdef VFAT_IOCTL_READDIR_BOTH
2245 if (!(status = read_directory_data_vfat( data, fd, mask ))) return status;
2246 #endif
2248 if (!has_wildcard( mask ))
2250 /* convert the mask to a Unix name and check for it */
2251 char unix_name[MAX_DIR_ENTRY_LEN * 3 + 1];
2252 int ret = ntdll_wcstoumbs( mask->Buffer, mask->Length / sizeof(WCHAR),
2253 unix_name, sizeof(unix_name) - 1, TRUE );
2254 if (ret > 0)
2256 unix_name[ret] = 0;
2257 #ifdef HAVE_GETATTRLIST
2258 if (!(status = read_directory_data_getattrlist( data, unix_name ))) return status;
2259 #endif
2260 if (!(status = read_directory_data_stat( data, unix_name ))) return status;
2264 return read_directory_data_readdir( data, mask );
2268 /* compare file names for directory sorting */
2269 static int name_compare( const void *a, const void *b )
2271 const struct dir_data_names *file_a = (const struct dir_data_names *)a;
2272 const struct dir_data_names *file_b = (const struct dir_data_names *)b;
2273 int ret = wcsicmp( file_a->long_name, file_b->long_name );
2274 if (!ret) ret = wcscmp( file_a->long_name, file_b->long_name );
2275 return ret;
2279 /***********************************************************************
2280 * init_cached_dir_data
2282 * Initialize the cached directory contents.
2284 static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const UNICODE_STRING *mask )
2286 struct dir_data *data;
2287 struct stat st;
2288 NTSTATUS status;
2289 unsigned int i;
2291 if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY;
2293 if ((status = read_directory_data( data, fd, mask )))
2295 free_dir_data( data );
2296 return status;
2299 /* sort filenames, but not "." and ".." */
2300 i = 0;
2301 if (i < data->count && !strcmp( data->names[i].unix_name, "." )) i++;
2302 if (i < data->count && !strcmp( data->names[i].unix_name, ".." )) i++;
2303 if (i < data->count) qsort( data->names + i, data->count - i, sizeof(*data->names), name_compare );
2305 if (data->count)
2307 fstat( fd, &st );
2308 data->id.dev = st.st_dev;
2309 data->id.ino = st.st_ino;
2312 TRACE( "mask %s found %u files\n", debugstr_us( mask ), data->count );
2313 for (i = 0; i < data->count; i++)
2314 TRACE( "%s %s\n", debugstr_w(data->names[i].long_name), debugstr_w(data->names[i].short_name) );
2316 *data_ret = data;
2317 return data->count ? STATUS_SUCCESS : STATUS_NO_SUCH_FILE;
2321 /***********************************************************************
2322 * get_cached_dir_data
2324 * Retrieve the cached directory data, or initialize it if necessary.
2326 static NTSTATUS get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd,
2327 const UNICODE_STRING *mask )
2329 unsigned int i;
2330 int entry = -1, free_entries[16];
2331 NTSTATUS status;
2333 SERVER_START_REQ( get_directory_cache_entry )
2335 req->handle = wine_server_obj_handle( handle );
2336 wine_server_set_reply( req, free_entries, sizeof(free_entries) );
2337 if (!(status = wine_server_call( req ))) entry = reply->entry;
2339 for (i = 0; i < wine_server_reply_size( reply ) / sizeof(*free_entries); i++)
2341 int free_idx = free_entries[i];
2342 if (free_idx < dir_data_cache_size)
2344 free_dir_data( dir_data_cache[free_idx] );
2345 dir_data_cache[free_idx] = NULL;
2349 SERVER_END_REQ;
2351 if (status)
2353 if (status == STATUS_SHARING_VIOLATION) FIXME( "shared directory handle not supported yet\n" );
2354 return status;
2357 if (entry >= dir_data_cache_size)
2359 unsigned int size = max( dir_data_cache_initial_size, max( dir_data_cache_size * 2, entry + 1 ) );
2360 struct dir_data **new_cache = realloc( dir_data_cache, size * sizeof(*new_cache) );
2362 if (!new_cache) return STATUS_NO_MEMORY;
2363 memset( new_cache + dir_data_cache_size, 0, (size - dir_data_cache_size) * sizeof(*new_cache) );
2364 dir_data_cache = new_cache;
2365 dir_data_cache_size = size;
2368 if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask );
2370 *data_ret = dir_data_cache[entry];
2371 return status;
2375 /******************************************************************************
2376 * NtQueryDirectoryFile (NTDLL.@)
2378 NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine,
2379 void *apc_context, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
2380 FILE_INFORMATION_CLASS info_class, BOOLEAN single_entry,
2381 UNICODE_STRING *mask, BOOLEAN restart_scan )
2383 int cwd, fd, needs_close;
2384 enum server_fd_type type;
2385 struct dir_data *data;
2386 NTSTATUS status;
2388 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2389 handle, event, apc_routine, apc_context, io, buffer,
2390 length, info_class, single_entry, debugstr_us(mask),
2391 restart_scan);
2393 if (event || apc_routine)
2395 FIXME( "Unsupported yet option\n" );
2396 return STATUS_NOT_IMPLEMENTED;
2398 switch (info_class)
2400 case FileDirectoryInformation:
2401 case FileBothDirectoryInformation:
2402 case FileFullDirectoryInformation:
2403 case FileIdBothDirectoryInformation:
2404 case FileIdFullDirectoryInformation:
2405 case FileIdGlobalTxDirectoryInformation:
2406 case FileNamesInformation:
2407 if (length < dir_info_align( dir_info_size( info_class, 1 ))) return STATUS_INFO_LENGTH_MISMATCH;
2408 break;
2409 case FileObjectIdInformation:
2410 if (length != sizeof(FILE_OBJECTID_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2411 return STATUS_INVALID_INFO_CLASS;
2412 case FileQuotaInformation:
2413 if (length != sizeof(FILE_QUOTA_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2414 return STATUS_INVALID_INFO_CLASS;
2415 case FileReparsePointInformation:
2416 if (length != sizeof(FILE_REPARSE_POINT_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2417 return STATUS_INVALID_INFO_CLASS;
2418 default:
2419 return STATUS_INVALID_INFO_CLASS;
2421 if (!buffer) return STATUS_ACCESS_VIOLATION;
2423 if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, &type, NULL )))
2424 return status;
2426 if (type != FD_TYPE_DIR)
2428 if (needs_close) close( fd );
2429 return STATUS_INVALID_PARAMETER;
2432 io->Information = 0;
2434 mutex_lock( &dir_mutex );
2436 cwd = open( ".", O_RDONLY );
2437 if (fchdir( fd ) != -1)
2439 if (!(status = get_cached_dir_data( handle, &data, fd, mask )))
2441 union file_directory_info *last_info = NULL;
2443 if (restart_scan) data->pos = 0;
2445 while (!status && data->pos < data->count)
2447 status = get_dir_data_entry( data, buffer, io, length, info_class, &last_info );
2448 if (!status || status == STATUS_BUFFER_OVERFLOW) data->pos++;
2449 if (single_entry && last_info) break;
2452 if (!last_info) status = STATUS_NO_MORE_FILES;
2453 else if (status == STATUS_MORE_ENTRIES) status = STATUS_SUCCESS;
2455 io->u.Status = status;
2457 if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
2459 else status = errno_to_status( errno );
2461 mutex_unlock( &dir_mutex );
2463 if (needs_close) close( fd );
2464 if (cwd != -1) close( cwd );
2465 TRACE( "=> %x (%ld)\n", status, io->Information );
2466 return status;
2470 /***********************************************************************
2471 * find_file_in_dir
2473 * Find a file in a directory the hard way, by doing a case-insensitive search.
2474 * The file found is appended to unix_name at pos.
2475 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2477 static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, int length,
2478 BOOLEAN check_case )
2480 WCHAR buffer[MAX_DIR_ENTRY_LEN];
2481 BOOLEAN is_name_8_dot_3;
2482 DIR *dir;
2483 struct dirent *de;
2484 struct stat st;
2485 int ret;
2487 /* try a shortcut for this directory */
2489 unix_name[pos++] = '/';
2490 ret = ntdll_wcstoumbs( name, length, unix_name + pos, MAX_DIR_ENTRY_LEN + 1, TRUE );
2491 if (ret >= 0 && ret <= MAX_DIR_ENTRY_LEN)
2493 unix_name[pos + ret] = 0;
2494 if (!stat( unix_name, &st )) return STATUS_SUCCESS;
2496 if (check_case) goto not_found; /* we want an exact match */
2498 if (pos > 1) unix_name[pos - 1] = 0;
2499 else unix_name[1] = 0; /* keep the initial slash */
2501 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2503 is_name_8_dot_3 = is_legal_8dot3_name( name, length );
2504 #ifndef VFAT_IOCTL_READDIR_BOTH
2505 is_name_8_dot_3 = is_name_8_dot_3 && length >= 8 && name[4] == '~';
2506 #endif
2508 if (!is_name_8_dot_3 && !get_dir_case_sensitivity( unix_name )) goto not_found;
2510 /* now look for it through the directory */
2512 #ifdef VFAT_IOCTL_READDIR_BOTH
2513 if (is_name_8_dot_3)
2515 int fd = open( unix_name, O_RDONLY | O_DIRECTORY );
2516 if (fd != -1)
2518 KERNEL_DIRENT kde[2];
2520 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) != -1)
2522 unix_name[pos - 1] = '/';
2523 while (kde[0].d_reclen)
2525 if (kde[1].d_name[0])
2527 ret = ntdll_umbstowcs( kde[1].d_name, strlen(kde[1].d_name),
2528 buffer, MAX_DIR_ENTRY_LEN );
2529 if (ret == length && !wcsnicmp( buffer, name, ret ))
2531 strcpy( unix_name + pos, kde[1].d_name );
2532 close( fd );
2533 return STATUS_SUCCESS;
2536 ret = ntdll_umbstowcs( kde[0].d_name, strlen(kde[0].d_name),
2537 buffer, MAX_DIR_ENTRY_LEN );
2538 if (ret == length && !wcsnicmp( buffer, name, ret ))
2540 strcpy( unix_name + pos,
2541 kde[1].d_name[0] ? kde[1].d_name : kde[0].d_name );
2542 close( fd );
2543 return STATUS_SUCCESS;
2545 if (ioctl( fd, VFAT_IOCTL_READDIR_BOTH, (long)kde ) == -1)
2547 close( fd );
2548 goto not_found;
2551 /* if that did not work, restore previous state of unix_name */
2552 unix_name[pos - 1] = 0;
2554 close( fd );
2556 /* fall through to normal handling */
2558 #endif /* VFAT_IOCTL_READDIR_BOTH */
2560 if (!(dir = opendir( unix_name ))) return errno_to_status( errno );
2562 unix_name[pos - 1] = '/';
2563 while ((de = readdir( dir )))
2565 ret = ntdll_umbstowcs( de->d_name, strlen(de->d_name), buffer, MAX_DIR_ENTRY_LEN );
2566 if (ret == length && !wcsnicmp( buffer, name, ret ))
2568 strcpy( unix_name + pos, de->d_name );
2569 closedir( dir );
2570 return STATUS_SUCCESS;
2573 if (!is_name_8_dot_3) continue;
2575 if (!is_legal_8dot3_name( buffer, ret ))
2577 WCHAR short_nameW[12];
2578 ret = hash_short_file_name( buffer, ret, short_nameW );
2579 if (ret == length && !wcsnicmp( short_nameW, name, length ))
2581 strcpy( unix_name + pos, de->d_name );
2582 closedir( dir );
2583 return STATUS_SUCCESS;
2587 closedir( dir );
2589 not_found:
2590 unix_name[pos - 1] = 0;
2591 return STATUS_OBJECT_PATH_NOT_FOUND;
2595 #ifndef _WIN64
2597 static const WCHAR catrootW[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2598 static const WCHAR catroot2W[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2599 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};
2600 static const WCHAR driversetcW[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2601 static const WCHAR logfilesW[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2602 static const WCHAR spoolW[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2603 static const WCHAR system32W[] = {'s','y','s','t','e','m','3','2',0};
2604 static const WCHAR syswow64W[] = {'s','y','s','w','o','w','6','4',0};
2605 static const WCHAR sysnativeW[] = {'s','y','s','n','a','t','i','v','e',0};
2606 static const WCHAR regeditW[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2607 static const WCHAR syswow64_regeditW[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
2608 static const WCHAR windirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\',0};
2609 static const WCHAR syswow64dirW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','w','o','w','6','4','\\'};
2611 static const WCHAR * const no_redirect[] =
2613 catrootW,
2614 catroot2W,
2615 driversstoreW,
2616 driversetcW,
2617 logfilesW,
2618 spoolW
2621 static struct file_identity windir, sysdir;
2623 static inline ULONG starts_with_path( const WCHAR *name, ULONG name_len, const WCHAR *prefix )
2625 ULONG len = wcslen( prefix );
2627 if (name_len < len) return 0;
2628 if (wcsnicmp( name, prefix, len )) return 0;
2629 if (name_len > len && name[len] != '\\') return 0;
2630 return len;
2633 static BOOL replace_path( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *str, ULONG prefix_len,
2634 const WCHAR *match, const WCHAR *replace )
2636 const WCHAR *name = attr->ObjectName->Buffer;
2637 ULONG match_len, replace_len, len = attr->ObjectName->Length / sizeof(WCHAR);
2638 WCHAR *p;
2640 if (!starts_with_path( name + prefix_len, len - prefix_len, match )) return FALSE;
2642 match_len = wcslen( match );
2643 replace_len = wcslen( replace );
2644 str->Length = (len + replace_len - match_len) * sizeof(WCHAR);
2645 str->MaximumLength = str->Length + sizeof(WCHAR);
2646 if (!(p = str->Buffer = malloc( str->MaximumLength ))) return FALSE;
2648 memcpy( p, name, prefix_len * sizeof(WCHAR) );
2649 p += prefix_len;
2650 memcpy( p, replace, replace_len * sizeof(WCHAR) );
2651 p += replace_len;
2652 name += prefix_len + match_len;
2653 len -= prefix_len + match_len;
2654 memcpy( p, name, len * sizeof(WCHAR) );
2655 p[len] = 0;
2656 attr->ObjectName = str;
2657 return TRUE;
2660 /***********************************************************************
2661 * init_redirects
2663 static void init_redirects(void)
2665 static const char system_dir[] = "/dosdevices/c:/windows/system32";
2666 char *dir;
2667 struct stat st;
2669 if (!(dir = malloc( strlen(config_dir) + sizeof(system_dir) ))) return;
2670 strcpy( dir, config_dir );
2671 strcat( dir, system_dir );
2672 if (!stat( dir, &st ))
2674 sysdir.dev = st.st_dev;
2675 sysdir.ino = st.st_ino;
2677 *strrchr( dir, '/' ) = 0;
2678 if (!stat( dir, &st ))
2680 windir.dev = st.st_dev;
2681 windir.ino = st.st_ino;
2683 else ERR( "%s: %s\n", dir, strerror(errno) );
2684 free( dir );
2688 /***********************************************************************
2689 * get_redirect
2691 BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir )
2693 const WCHAR *name = attr->ObjectName->Buffer;
2694 unsigned int i, prefix_len = 0, len = attr->ObjectName->Length / sizeof(WCHAR);
2696 redir->Buffer = NULL;
2697 if (!NtCurrentTeb64()) return FALSE;
2698 if (!len) return FALSE;
2700 if (!attr->RootDirectory)
2702 prefix_len = wcslen( windirW );
2703 if (len < prefix_len || wcsnicmp( name, windirW, prefix_len )) return FALSE;
2705 else
2707 int fd, needs_close;
2708 struct stat st;
2710 if (server_get_unix_fd( attr->RootDirectory, 0, &fd, &needs_close, NULL, NULL )) return FALSE;
2711 fstat( fd, &st );
2712 if (needs_close) close( fd );
2713 if (!is_same_file( &windir, &st ))
2715 if (!is_same_file( &sysdir, &st )) return FALSE;
2716 if (NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE;
2717 if (name[0] == '\\') return FALSE;
2719 /* only check for paths that should NOT be redirected */
2720 for (i = 0; i < ARRAY_SIZE( no_redirect ); i++)
2721 if (starts_with_path( name, len, no_redirect[i] + 9 /* "system32\\" */)) return FALSE;
2723 /* redirect everything else */
2724 redir->Length = sizeof(syswow64dirW) + len * sizeof(WCHAR);
2725 redir->MaximumLength = redir->Length + sizeof(WCHAR);
2726 if (!(redir->Buffer = malloc( redir->MaximumLength ))) return FALSE;
2727 memcpy( redir->Buffer, syswow64dirW, sizeof(syswow64dirW) );
2728 memcpy( redir->Buffer + ARRAY_SIZE(syswow64dirW), name, len * sizeof(WCHAR) );
2729 redir->Buffer[redir->Length / sizeof(WCHAR)] = 0;
2730 attr->RootDirectory = 0;
2731 attr->ObjectName = redir;
2732 return TRUE;
2736 /* sysnative is redirected even when redirection is disabled */
2738 if (replace_path( attr, redir, prefix_len, sysnativeW, system32W )) return TRUE;
2740 if (NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE;
2742 for (i = 0; i < ARRAY_SIZE( no_redirect ); i++)
2743 if (starts_with_path( name + prefix_len, len - prefix_len, no_redirect[i] )) return FALSE;
2745 if (replace_path( attr, redir, prefix_len, system32W, syswow64W )) return TRUE;
2746 if (replace_path( attr, redir, prefix_len, regeditW, syswow64_regeditW )) return TRUE;
2747 return FALSE;
2750 #else /* _WIN64 */
2752 /* there are no redirects on 64-bit */
2753 BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir )
2755 redir->Buffer = NULL;
2756 return FALSE;
2759 #endif
2762 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
2764 /***********************************************************************
2765 * init_files
2767 void init_files(void)
2769 HANDLE key;
2771 #ifndef _WIN64
2772 if (is_wow64) init_redirects();
2773 #endif
2774 /* a couple of directories that we don't want to return in directory searches */
2775 ignore_file( config_dir );
2776 ignore_file( "/dev" );
2777 ignore_file( "/proc" );
2778 #ifdef linux
2779 ignore_file( "/sys" );
2780 #endif
2781 /* retrieve initial umask */
2782 start_umask = umask( 0777 );
2783 umask( start_umask );
2785 if (!open_hkcu_key( "Software\\Wine", &key ))
2787 static WCHAR showdotfilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
2788 char tmp[80];
2789 DWORD dummy;
2790 UNICODE_STRING nameW;
2792 init_unicode_string( &nameW, showdotfilesW );
2793 if (!NtQueryValueKey( key, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
2795 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
2796 show_dot_files = IS_OPTION_TRUE( str[0] );
2798 NtClose( key );
2803 /******************************************************************************
2804 * get_dos_device
2806 * Get the Unix path of a DOS device.
2808 static NTSTATUS get_dos_device( char **unix_name, int start_pos )
2810 struct stat st;
2811 char *new_name, *dev = *unix_name + start_pos;
2813 /* special case for drive devices */
2814 if (dev[0] && dev[1] == ':' && !dev[2]) strcpy( dev + 1, "::" );
2816 if (strchr( dev, '/' )) goto failed;
2818 for (;;)
2820 if (!stat( *unix_name, &st ))
2822 TRACE( "-> %s\n", debugstr_a(*unix_name));
2823 return STATUS_SUCCESS;
2825 if (!dev) break;
2827 /* now try some defaults for it */
2828 if (!strcmp( dev, "aux" ))
2830 strcpy( dev, "com1" );
2831 continue;
2833 if (!strcmp( dev, "prn" ))
2835 strcpy( dev, "lpt1" );
2836 continue;
2839 new_name = NULL;
2840 if (dev[1] == ':' && dev[2] == ':') /* drive device */
2842 dev[2] = 0; /* remove last ':' to get the drive mount point symlink */
2843 new_name = get_default_drive_device( *unix_name );
2845 free( *unix_name );
2846 *unix_name = new_name;
2847 if (!new_name) return STATUS_BAD_DEVICE_TYPE;
2848 dev = NULL; /* last try */
2850 failed:
2851 free( *unix_name );
2852 *unix_name = NULL;
2853 return STATUS_BAD_DEVICE_TYPE;
2857 /* return the length of the DOS namespace prefix if any */
2858 static inline int get_dos_prefix_len( const UNICODE_STRING *name )
2860 static const WCHAR nt_prefixW[] = {'\\','?','?','\\'};
2861 static const WCHAR dosdev_prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2863 if (name->Length >= sizeof(nt_prefixW) &&
2864 !memcmp( name->Buffer, nt_prefixW, sizeof(nt_prefixW) ))
2865 return ARRAY_SIZE( nt_prefixW );
2867 if (name->Length >= sizeof(dosdev_prefixW) &&
2868 !wcsnicmp( name->Buffer, dosdev_prefixW, ARRAY_SIZE( dosdev_prefixW )))
2869 return ARRAY_SIZE( dosdev_prefixW );
2871 return 0;
2875 /***********************************************************************
2876 * remove_last_componentA
2878 * Remove the last component of the path. Helper for find_drive_rootA.
2880 static inline unsigned int remove_last_componentA( const char *path, unsigned int len )
2882 int level = 0;
2884 while (level < 1)
2886 /* find start of the last path component */
2887 unsigned int prev = len;
2888 if (prev <= 1) break; /* reached root */
2889 while (prev > 1 && path[prev - 1] != '/') prev--;
2890 /* does removing it take us up a level? */
2891 if (len - prev != 1 || path[prev] != '.') /* not '.' */
2893 if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.') /* is it '..'? */
2894 level--;
2895 else
2896 level++;
2898 /* strip off trailing slashes */
2899 while (prev > 1 && path[prev - 1] == '/') prev--;
2900 len = prev;
2902 return len;
2906 /***********************************************************************
2907 * find_drive_rootA
2909 * Find a drive for which the root matches the beginning of the given path.
2910 * This can be used to translate a Unix path into a drive + DOS path.
2911 * Return value is the drive, or -1 on error. On success, ppath is modified
2912 * to point to the beginning of the DOS path.
2914 static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_ret )
2916 /* Starting with the full path, check if the device and inode match any of
2917 * the wine 'drives'. If not then remove the last path component and try
2918 * again. If the last component was a '..' then skip a normal component
2919 * since it's a directory that's ascended back out of.
2921 int drive;
2922 char *buffer;
2923 const char *path = *ppath;
2924 struct stat st;
2925 struct file_identity info[MAX_DOS_DRIVES];
2927 /* get device and inode of all drives */
2928 if (!get_drives_info( info )) return STATUS_OBJECT_PATH_NOT_FOUND;
2930 /* strip off trailing slashes */
2931 while (len > 1 && path[len - 1] == '/') len--;
2933 /* make a copy of the path */
2934 if (!(buffer = malloc( len + 1 ))) return STATUS_NO_MEMORY;
2935 memcpy( buffer, path, len );
2936 buffer[len] = 0;
2938 for (;;)
2940 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
2942 /* Find the drive */
2943 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
2945 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
2947 if (len == 1) len = 0; /* preserve root slash in returned path */
2948 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2949 debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
2950 *ppath += len;
2951 *drive_ret = drive;
2952 free( buffer );
2953 return STATUS_SUCCESS;
2957 if (len <= 1) break; /* reached root */
2958 len = remove_last_componentA( buffer, len );
2959 buffer[len] = 0;
2961 free( buffer );
2962 return STATUS_OBJECT_PATH_NOT_FOUND;
2966 /******************************************************************************
2967 * find_file_id
2969 * Recursively search directories from the dir queue for a given inode.
2971 static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev )
2973 unsigned int pos;
2974 DIR *dir;
2975 struct dirent *de;
2976 NTSTATUS status;
2977 struct stat st;
2978 char *name = *unix_name;
2980 while (!(status = next_dir_in_queue( name )))
2982 if (!(dir = opendir( name ))) continue;
2983 TRACE( "searching %s for %s\n", debugstr_a(name), wine_dbgstr_longlong(file_id) );
2984 pos = strlen( name );
2985 if (pos + MAX_DIR_ENTRY_LEN >= *len / sizeof(WCHAR))
2987 if (!(name = realloc( name, *len * 2 )))
2989 closedir( dir );
2990 return STATUS_NO_MEMORY;
2992 *len *= 2;
2993 *unix_name = name;
2995 name[pos++] = '/';
2996 while ((de = readdir( dir )))
2998 if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
2999 strcpy( name + pos, de->d_name );
3000 if (lstat( name, &st ) == -1) continue;
3001 if (st.st_dev != dev) continue;
3002 if (st.st_ino == file_id)
3004 closedir( dir );
3005 return STATUS_SUCCESS;
3007 if (!S_ISDIR( st.st_mode )) continue;
3008 if ((status = add_dir_to_queue( name )) != STATUS_SUCCESS)
3010 closedir( dir );
3011 return status;
3014 closedir( dir );
3016 return status;
3020 /******************************************************************************
3021 * file_id_to_unix_file_name
3023 * Lookup a file from its file id instead of its name.
3025 static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **unix_name_ret,
3026 UNICODE_STRING *nt_name )
3028 enum server_fd_type type;
3029 int old_cwd, root_fd, needs_close;
3030 char *unix_name;
3031 ULONG len;
3032 NTSTATUS status;
3033 ULONGLONG file_id;
3034 struct stat st, root_st;
3036 nt_name->Buffer = NULL;
3037 if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
3038 if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
3039 memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
3041 len = 2 * MAX_DIR_ENTRY_LEN + 4;
3042 if (!(unix_name = malloc( len ))) return STATUS_NO_MEMORY;
3043 strcpy( unix_name, "." );
3045 if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3046 goto done;
3048 if (type != FD_TYPE_DIR)
3050 status = STATUS_OBJECT_TYPE_MISMATCH;
3051 goto done;
3054 fstat( root_fd, &root_st );
3055 if (root_st.st_ino == file_id) /* shortcut for "." */
3057 status = STATUS_SUCCESS;
3058 goto done;
3061 mutex_lock( &dir_mutex );
3062 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3064 /* shortcut for ".." */
3065 if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
3067 strcpy( unix_name, ".." );
3068 status = STATUS_SUCCESS;
3070 else
3072 status = add_dir_to_queue( "." );
3073 if (!status)
3074 status = find_file_id( &unix_name, &len, file_id, root_st.st_dev );
3075 if (!status) /* get rid of "./" prefix */
3076 memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 );
3077 flush_dir_queue();
3079 if (fchdir( old_cwd ) == -1) chdir( "/" );
3081 else status = errno_to_status( errno );
3082 mutex_unlock( &dir_mutex );
3083 if (old_cwd != -1) close( old_cwd );
3085 done:
3086 if (status == STATUS_SUCCESS)
3088 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name) );
3089 *unix_name_ret = unix_name;
3091 nt_name->MaximumLength = (strlen(unix_name) + 1) * sizeof(WCHAR);
3092 if ((nt_name->Buffer = malloc( nt_name->MaximumLength )))
3094 DWORD i, len = ntdll_umbstowcs( unix_name, strlen(unix_name), nt_name->Buffer, strlen(unix_name) );
3095 nt_name->Buffer[len] = 0;
3096 nt_name->Length = len * sizeof(WCHAR);
3097 for (i = 0; i < len; i++) if (nt_name->Buffer[i] == '/') nt_name->Buffer[i] = '\\';
3100 else
3102 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
3103 free( unix_name );
3105 if (needs_close) close( root_fd );
3106 return status;
3110 /******************************************************************************
3111 * lookup_unix_name
3113 * Helper for nt_to_unix_file_name
3115 static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer, int unix_len, int pos,
3116 UINT disposition, BOOL is_unix )
3118 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, '/', 0 };
3119 NTSTATUS status;
3120 int ret;
3121 struct stat st;
3122 char *unix_name = *buffer;
3123 const WCHAR *ptr, *end;
3125 /* check syntax of individual components */
3127 for (ptr = name, end = name + name_len; ptr < end; ptr++)
3129 if (*ptr == '\\') return STATUS_OBJECT_NAME_INVALID; /* duplicate backslash */
3130 if (*ptr == '.')
3132 if (ptr + 1 == end) return STATUS_OBJECT_NAME_INVALID; /* "." element */
3133 if (ptr[1] == '\\') return STATUS_OBJECT_NAME_INVALID; /* "." element */
3134 if (ptr[1] == '.')
3136 if (ptr + 2 == end) return STATUS_OBJECT_NAME_INVALID; /* ".." element */
3137 if (ptr[2] == '\\') return STATUS_OBJECT_NAME_INVALID; /* ".." element */
3140 /* check for invalid characters (all chars except 0 are valid for unix) */
3141 for ( ; ptr < end && *ptr != '\\'; ptr++)
3143 if (!*ptr) return STATUS_OBJECT_NAME_INVALID;
3144 if (is_unix) continue;
3145 if (*ptr < 32 || wcschr( invalid_charsW, *ptr )) return STATUS_OBJECT_NAME_INVALID;
3149 /* try a shortcut first */
3151 unix_name[pos] = '/';
3152 ret = ntdll_wcstoumbs( name, name_len, unix_name + pos + 1, unix_len - pos - 1, TRUE );
3153 if (ret >= 0 && ret < unix_len - pos - 1)
3155 char *p;
3156 unix_name[pos + 1 + ret] = 0;
3157 for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
3158 if (!stat( unix_name, &st ))
3160 if (disposition == FILE_CREATE) return STATUS_OBJECT_NAME_COLLISION;
3161 return STATUS_SUCCESS;
3165 if (!name_len) /* empty name -> drive root doesn't exist */
3166 return STATUS_OBJECT_PATH_NOT_FOUND;
3167 if (is_unix && (disposition == FILE_OPEN || disposition == FILE_OVERWRITE))
3168 return STATUS_OBJECT_NAME_NOT_FOUND;
3170 /* now do it component by component */
3172 while (name_len)
3174 const WCHAR *end, *next;
3176 end = name;
3177 while (end < name + name_len && *end != '\\') end++;
3178 next = end;
3179 if (next < name + name_len) next++;
3180 name_len -= next - name;
3182 /* grow the buffer if needed */
3184 if (unix_len - pos < MAX_DIR_ENTRY_LEN + 2)
3186 char *new_name;
3187 unix_len += 2 * MAX_DIR_ENTRY_LEN;
3188 if (!(new_name = realloc( unix_name, unix_len ))) return STATUS_NO_MEMORY;
3189 unix_name = *buffer = new_name;
3192 status = find_file_in_dir( unix_name, pos, name, end - name, is_unix );
3194 /* if this is the last element, not finding it is not necessarily fatal */
3195 if (!name_len)
3197 if (status == STATUS_OBJECT_PATH_NOT_FOUND)
3199 status = STATUS_OBJECT_NAME_NOT_FOUND;
3200 if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3202 ret = ntdll_wcstoumbs( name, end - name, unix_name + pos + 1, MAX_DIR_ENTRY_LEN + 1, TRUE );
3203 if (ret > 0 && ret <= MAX_DIR_ENTRY_LEN)
3205 unix_name[pos] = '/';
3206 unix_name[pos + 1 + ret] = 0;
3207 status = STATUS_NO_SUCH_FILE;
3208 break;
3212 else if (status == STATUS_SUCCESS && disposition == FILE_CREATE)
3214 status = STATUS_OBJECT_NAME_COLLISION;
3218 if (status != STATUS_SUCCESS) break;
3220 pos += strlen( unix_name + pos );
3221 name = next;
3224 return status;
3228 /******************************************************************************
3229 * nt_to_unix_file_name_no_root
3231 static NTSTATUS nt_to_unix_file_name_no_root( const UNICODE_STRING *nameW, char **unix_name_ret,
3232 UINT disposition )
3234 static const WCHAR unixW[] = {'u','n','i','x'};
3235 static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
3237 NTSTATUS status = STATUS_SUCCESS;
3238 const WCHAR *name;
3239 struct stat st;
3240 char *unix_name;
3241 int pos, ret, name_len, unix_len, prefix_len;
3242 WCHAR prefix[MAX_DIR_ENTRY_LEN + 1];
3243 BOOLEAN is_unix = FALSE;
3245 name = nameW->Buffer;
3246 name_len = nameW->Length / sizeof(WCHAR);
3248 if (!name_len || name[0] != '\\') return STATUS_OBJECT_PATH_SYNTAX_BAD;
3250 if (!(pos = get_dos_prefix_len( nameW )))
3251 return STATUS_BAD_DEVICE_TYPE; /* no DOS prefix, assume NT native name */
3253 name += pos;
3254 name_len -= pos;
3256 if (!name_len) return STATUS_OBJECT_NAME_INVALID;
3258 /* check for sub-directory */
3259 for (pos = 0; pos < name_len && pos <= MAX_DIR_ENTRY_LEN; pos++)
3261 if (name[pos] == '\\') break;
3262 if (name[pos] < 32 || wcschr( invalid_charsW, name[pos] ))
3263 return STATUS_OBJECT_NAME_INVALID;
3264 prefix[pos] = (name[pos] >= 'A' && name[pos] <= 'Z') ? name[pos] + 'a' - 'A' : name[pos];
3266 if (pos > MAX_DIR_ENTRY_LEN) return STATUS_OBJECT_NAME_INVALID;
3268 if (pos >= 4 && !memcmp( prefix, unixW, sizeof(unixW) ))
3270 /* allow slash for unix namespace */
3271 if (pos > 4 && prefix[4] == '/') pos = 4;
3272 is_unix = pos == 4;
3274 prefix_len = pos;
3275 prefix[prefix_len] = 0;
3277 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3278 unix_len += strlen(config_dir) + sizeof("/dosdevices/");
3279 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3280 strcpy( unix_name, config_dir );
3281 strcat( unix_name, "/dosdevices/" );
3282 pos = strlen(unix_name);
3284 ret = ntdll_wcstoumbs( prefix, prefix_len, unix_name + pos, unix_len - pos - 1, TRUE );
3285 if (ret <= 0)
3287 free( unix_name );
3288 return STATUS_OBJECT_NAME_INVALID;
3291 if (prefix_len == name_len) /* no subdir, plain DOS device */
3293 unix_name[pos + ret] = 0;
3294 *unix_name_ret = unix_name;
3295 return get_dos_device( unix_name_ret, pos );
3297 pos += ret;
3299 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3301 if (wcschr( prefix, '/' ))
3303 free( unix_name );
3304 return STATUS_OBJECT_PATH_NOT_FOUND;
3307 if (prefix_len != 2 || prefix[1] != ':')
3309 unix_name[pos] = 0;
3310 if (lstat( unix_name, &st ) == -1 && errno == ENOENT)
3312 if (!is_unix)
3314 free( unix_name );
3315 return STATUS_BAD_DEVICE_TYPE;
3317 pos = 0; /* fall back to unix root */
3321 prefix_len++; /* skip initial backslash */
3322 if (name_len > prefix_len && name[prefix_len] == '\\') prefix_len++; /* allow a second backslash */
3323 name += prefix_len;
3324 name_len -= prefix_len;
3326 status = lookup_unix_name( name, name_len, &unix_name, unix_len, pos, disposition, is_unix );
3327 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3329 TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
3330 *unix_name_ret = unix_name;
3332 else
3334 TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
3335 free( unix_name );
3337 return status;
3341 /******************************************************************************
3342 * nt_to_unix_file_name
3344 * Convert a file name from NT namespace to Unix namespace.
3346 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3347 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3348 * returned, but the unix name is still filled in properly.
3350 NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition )
3352 enum server_fd_type type;
3353 int old_cwd, root_fd, needs_close;
3354 const WCHAR *name;
3355 char *unix_name;
3356 int name_len, unix_len;
3357 NTSTATUS status;
3359 if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
3360 return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition );
3362 name = attr->ObjectName->Buffer;
3363 name_len = attr->ObjectName->Length / sizeof(WCHAR);
3365 if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER;
3367 unix_len = name_len * 3 + MAX_DIR_ENTRY_LEN + 3;
3368 if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
3369 unix_name[0] = '.';
3371 if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
3373 if (type != FD_TYPE_DIR)
3375 if (needs_close) close( root_fd );
3376 status = STATUS_BAD_DEVICE_TYPE;
3378 else
3380 mutex_lock( &dir_mutex );
3381 if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
3383 status = lookup_unix_name( name, name_len, &unix_name, unix_len, 1, disposition, FALSE );
3384 if (fchdir( old_cwd ) == -1) chdir( "/" );
3386 else status = errno_to_status( errno );
3387 mutex_unlock( &dir_mutex );
3388 if (old_cwd != -1) close( old_cwd );
3389 if (needs_close) close( root_fd );
3392 else if (status == STATUS_OBJECT_TYPE_MISMATCH) status = STATUS_BAD_DEVICE_TYPE;
3394 if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
3396 TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
3397 *name_ret = unix_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;
3422 UNICODE_STRING redir;
3423 OBJECT_ATTRIBUTES attr;
3425 InitializeObjectAttributes( &attr, (UNICODE_STRING *)nameW, OBJ_CASE_INSENSITIVE, 0, NULL );
3426 get_redirect( &attr, &redir );
3427 status = nt_to_unix_file_name( &attr, &buffer, disposition );
3429 if (buffer)
3431 if (*size > strlen(buffer)) strcpy( nameA, buffer );
3432 else status = STATUS_BUFFER_TOO_SMALL;
3433 *size = strlen(buffer) + 1;
3434 free( buffer );
3436 free( redir.Buffer );
3437 return status;
3441 /******************************************************************
3442 * collapse_path
3444 * Get rid of . and .. components in the path.
3446 static void collapse_path( WCHAR *path )
3448 WCHAR *p, *start, *next;
3450 /* convert every / into a \ */
3451 for (p = path; *p; p++) if (*p == '/') *p = '\\';
3453 p = path + 4;
3454 while (*p && *p != '\\') p++;
3455 start = p + 1;
3457 /* collapse duplicate backslashes */
3458 next = start;
3459 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
3460 *next = 0;
3462 p = start;
3463 while (*p)
3465 if (*p == '.')
3467 switch(p[1])
3469 case '\\': /* .\ component */
3470 next = p + 2;
3471 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3472 continue;
3473 case 0: /* final . */
3474 if (p > start) p--;
3475 *p = 0;
3476 continue;
3477 case '.':
3478 if (p[2] == '\\') /* ..\ component */
3480 next = p + 3;
3481 if (p > start)
3483 p--;
3484 while (p > start && p[-1] != '\\') p--;
3486 memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
3487 continue;
3489 else if (!p[2]) /* final .. */
3491 if (p > start)
3493 p--;
3494 while (p > start && p[-1] != '\\') p--;
3495 if (p > start) p--;
3497 *p = 0;
3498 continue;
3500 break;
3503 /* skip to the next component */
3504 while (*p && *p != '\\') p++;
3505 if (*p == '\\')
3507 /* remove last dot in previous dir name */
3508 if (p > start && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
3509 else p++;
3513 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3514 while (p > start && (p[-1] == ' ' || p[-1] == '.')) p--;
3515 *p = 0;
3519 /******************************************************************
3520 * unix_to_nt_file_name
3522 NTSTATUS unix_to_nt_file_name( const char *name, WCHAR **nt )
3524 static const WCHAR unix_prefixW[] = {'\\','?','?','\\','u','n','i','x',0};
3525 WCHAR dos_prefixW[] = {'\\','?','?','\\','A',':','\\',0};
3526 const WCHAR *prefix = unix_prefixW;
3527 unsigned int lenW, lenA = strlen(name);
3528 const char *path = name;
3529 NTSTATUS status;
3530 WCHAR *buffer;
3531 int drive;
3533 status = find_drive_rootA( &path, lenA, &drive );
3534 lenA -= path - name;
3536 if (status == STATUS_SUCCESS)
3538 while (lenA && path[0] == '/') { lenA--; path++; }
3539 dos_prefixW[4] += drive;
3540 prefix = dos_prefixW;
3542 else if (status != STATUS_OBJECT_PATH_NOT_FOUND) return status;
3544 lenW = wcslen( prefix );
3545 if (!(buffer = malloc( (lenA + lenW + 1) * sizeof(WCHAR) ))) return STATUS_NO_MEMORY;
3546 memcpy( buffer, prefix, lenW * sizeof(WCHAR) );
3547 lenW += ntdll_umbstowcs( path, lenA, buffer + lenW, lenA );
3548 buffer[lenW] = 0;
3549 collapse_path( buffer );
3550 *nt = buffer;
3551 return STATUS_SUCCESS;
3555 /******************************************************************
3556 * wine_unix_to_nt_file_name
3558 NTSTATUS CDECL wine_unix_to_nt_file_name( const char *name, WCHAR *buffer, SIZE_T *size )
3560 WCHAR *nt_name = NULL;
3561 NTSTATUS status;
3563 if (name[0] != '/') return STATUS_INVALID_PARAMETER; /* relative paths are not supported */
3565 status = unix_to_nt_file_name( name, &nt_name );
3566 if (nt_name)
3568 if (*size > wcslen(nt_name)) wcscpy( buffer, nt_name );
3569 else status = STATUS_BUFFER_TOO_SMALL;
3570 *size = wcslen(nt_name) + 1;
3571 free( nt_name );
3573 return status;
3577 /***********************************************************************
3578 * get_full_path
3580 * Simplified version of RtlGetFullPathName_U.
3582 NTSTATUS get_full_path( const WCHAR *name, const WCHAR *curdir, WCHAR **path )
3584 static const WCHAR uncW[] = {'\\','?','?','\\','U','N','C','\\',0};
3585 static const WCHAR devW[] = {'\\','?','?','\\',0};
3586 static const WCHAR unixW[] = {'u','n','i','x'};
3587 WCHAR *ret, root[] = {'\\','?','?','\\','C',':','\\',0};
3588 NTSTATUS status = STATUS_SUCCESS;
3589 const WCHAR *prefix;
3591 if (IS_SEPARATOR(name[0]) && IS_SEPARATOR(name[1])) /* \\ prefix */
3593 if ((name[2] == '.' || name[2] == '?') && IS_SEPARATOR(name[3])) /* \\?\ device */
3595 name += 4;
3596 if (!wcsnicmp( name, unixW, 4 ) && IS_SEPARATOR(name[4])) /* \\?\unix special name */
3598 char *unix_name;
3599 name += 4;
3600 unix_name = malloc( wcslen(name) * 3 + 1 );
3601 ntdll_wcstoumbs( name, wcslen(name) + 1, unix_name, wcslen(name) * 3 + 1, FALSE );
3602 status = unix_to_nt_file_name( unix_name, path );
3603 free( unix_name );
3604 return status;
3606 prefix = devW;
3608 else prefix = uncW; /* UNC path */
3610 else if (IS_SEPARATOR(name[0])) /* absolute path */
3612 root[4] = curdir[4];
3613 prefix = root;
3615 else if (name[0] && name[1] == ':') /* drive letter */
3617 root[4] = towupper(name[0]);
3618 name += 2;
3619 prefix = root;
3621 else prefix = curdir; /* relative path */
3623 ret = malloc( (wcslen(prefix) + wcslen(name) + 1) * sizeof(WCHAR) );
3624 wcscpy( ret, prefix );
3625 wcscat( ret, name );
3626 collapse_path( ret );
3627 *path = ret;
3628 return STATUS_SUCCESS;
3632 /***********************************************************************
3633 * unmount_device
3635 * Unmount the specified device.
3637 static NTSTATUS unmount_device( HANDLE handle )
3639 NTSTATUS status;
3640 int unix_fd, needs_close;
3642 if (!(status = server_get_unix_fd( handle, 0, &unix_fd, &needs_close, NULL, NULL )))
3644 struct stat st;
3645 char *mount_point = NULL;
3647 if (fstat( unix_fd, &st ) == -1 || !is_valid_mounted_device( &st ))
3648 status = STATUS_INVALID_PARAMETER;
3649 else
3651 if ((mount_point = get_device_mount_point( st.st_rdev )))
3653 #ifdef __APPLE__
3654 static const char umount[] = "diskutil unmount >/dev/null 2>&1 ";
3655 #else
3656 static const char umount[] = "umount >/dev/null 2>&1 ";
3657 #endif
3658 char *cmd = malloc( strlen(mount_point)+sizeof(umount));
3659 if (cmd)
3661 strcpy( cmd, umount );
3662 strcat( cmd, mount_point );
3663 system( cmd );
3664 free( cmd );
3665 #ifdef linux
3666 /* umount will fail to release the loop device since we still have
3667 a handle to it, so we release it here */
3668 if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3669 #endif
3671 free( mount_point );
3674 if (needs_close) close( unix_fd );
3676 return status;
3680 /******************************************************************************
3681 * open_unix_file
3683 * Helper for NtCreateFile that takes a Unix path.
3685 NTSTATUS open_unix_file( HANDLE *handle, const char *unix_name, ACCESS_MASK access,
3686 OBJECT_ATTRIBUTES *attr, ULONG attributes, ULONG sharing, ULONG disposition,
3687 ULONG options, void *ea_buffer, ULONG ea_length )
3689 struct object_attributes *objattr;
3690 NTSTATUS status;
3691 data_size_t len;
3693 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3695 SERVER_START_REQ( create_file )
3697 req->access = access;
3698 req->sharing = sharing;
3699 req->create = disposition;
3700 req->options = options;
3701 req->attrs = attributes;
3702 wine_server_add_data( req, objattr, len );
3703 wine_server_add_data( req, unix_name, strlen(unix_name) );
3704 status = wine_server_call( req );
3705 *handle = wine_server_ptr_handle( reply->handle );
3707 SERVER_END_REQ;
3708 free( objattr );
3709 return status;
3713 /******************************************************************************
3714 * NtCreateFile (NTDLL.@)
3716 NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
3717 IO_STATUS_BLOCK *io, LARGE_INTEGER *alloc_size,
3718 ULONG attributes, ULONG sharing, ULONG disposition,
3719 ULONG options, void *ea_buffer, ULONG ea_length )
3721 OBJECT_ATTRIBUTES new_attr;
3722 UNICODE_STRING nt_name;
3723 char *unix_name;
3724 BOOL created = FALSE;
3726 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
3727 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
3728 handle, access, debugstr_us(attr->ObjectName), attr->Attributes,
3729 attr->RootDirectory, attr->SecurityDescriptor, io, alloc_size,
3730 attributes, sharing, disposition, options, ea_buffer, ea_length );
3732 if (!attr || !attr->ObjectName) return STATUS_INVALID_PARAMETER;
3734 if (alloc_size) FIXME( "alloc_size not supported\n" );
3736 new_attr = *attr;
3737 if (options & FILE_OPEN_BY_FILE_ID)
3739 io->u.Status = file_id_to_unix_file_name( &new_attr, &unix_name, &nt_name );
3740 if (!io->u.Status) new_attr.ObjectName = &nt_name;
3742 else
3744 get_redirect( &new_attr, &nt_name );
3745 io->u.Status = nt_to_unix_file_name( &new_attr, &unix_name, disposition );
3748 if (io->u.Status == STATUS_BAD_DEVICE_TYPE)
3750 SERVER_START_REQ( open_file_object )
3752 req->access = access;
3753 req->attributes = attr->Attributes;
3754 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
3755 req->sharing = sharing;
3756 req->options = options;
3757 wine_server_add_data( req, new_attr.ObjectName->Buffer, new_attr.ObjectName->Length );
3758 io->u.Status = wine_server_call( req );
3759 *handle = wine_server_ptr_handle( reply->handle );
3761 SERVER_END_REQ;
3762 if (io->u.Status == STATUS_SUCCESS) io->Information = FILE_OPENED;
3763 free( nt_name.Buffer );
3764 return io->u.Status;
3767 if (io->u.Status == STATUS_NO_SUCH_FILE && disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
3769 created = TRUE;
3770 io->u.Status = STATUS_SUCCESS;
3773 if (io->u.Status == STATUS_SUCCESS)
3775 io->u.Status = open_unix_file( handle, unix_name, access, &new_attr, attributes,
3776 sharing, disposition, options, ea_buffer, ea_length );
3777 free( unix_name );
3779 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), io->u.Status );
3781 if (io->u.Status == STATUS_SUCCESS)
3783 if (created) io->Information = FILE_CREATED;
3784 else switch(disposition)
3786 case FILE_SUPERSEDE:
3787 io->Information = FILE_SUPERSEDED;
3788 break;
3789 case FILE_CREATE:
3790 io->Information = FILE_CREATED;
3791 break;
3792 case FILE_OPEN:
3793 case FILE_OPEN_IF:
3794 io->Information = FILE_OPENED;
3795 break;
3796 case FILE_OVERWRITE:
3797 case FILE_OVERWRITE_IF:
3798 io->Information = FILE_OVERWRITTEN;
3799 break;
3802 else if (io->u.Status == STATUS_TOO_MANY_OPENED_FILES)
3804 static int once;
3805 if (!once++) ERR_(winediag)( "Too many open files, ulimit -n probably needs to be increased\n" );
3808 free( nt_name.Buffer );
3809 return io->u.Status;
3813 /******************************************************************************
3814 * NtOpenFile (NTDLL.@)
3816 NTSTATUS WINAPI NtOpenFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr,
3817 IO_STATUS_BLOCK *io, ULONG sharing, ULONG options )
3819 return NtCreateFile( handle, access, attr, io, NULL, 0, sharing, FILE_OPEN, options, NULL, 0 );
3823 /******************************************************************************
3824 * NtCreateMailslotFile (NTDLL.@)
3826 NTSTATUS WINAPI NtCreateMailslotFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
3827 IO_STATUS_BLOCK *io, ULONG options, ULONG quota, ULONG msg_size,
3828 LARGE_INTEGER *timeout )
3830 NTSTATUS status;
3831 data_size_t len;
3832 struct object_attributes *objattr;
3834 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
3835 handle, access, attr, io, options, quota, msg_size, timeout );
3837 if (!handle) return STATUS_ACCESS_VIOLATION;
3838 if (!attr) return STATUS_INVALID_PARAMETER;
3840 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3842 SERVER_START_REQ( create_mailslot )
3844 req->access = access;
3845 req->max_msgsize = msg_size;
3846 req->read_timeout = timeout ? timeout->QuadPart : -1;
3847 wine_server_add_data( req, objattr, len );
3848 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
3850 SERVER_END_REQ;
3852 free( objattr );
3853 return status;
3857 /******************************************************************
3858 * NtCreateNamedPipeFile (NTDLL.@)
3860 NTSTATUS WINAPI NtCreateNamedPipeFile( HANDLE *handle, ULONG access, OBJECT_ATTRIBUTES *attr,
3861 IO_STATUS_BLOCK *io, ULONG sharing, ULONG dispo, ULONG options,
3862 ULONG pipe_type, ULONG read_mode, ULONG completion_mode,
3863 ULONG max_inst, ULONG inbound_quota, ULONG outbound_quota,
3864 LARGE_INTEGER *timeout )
3866 NTSTATUS status;
3867 data_size_t len;
3868 struct object_attributes *objattr;
3870 if (!attr) return STATUS_INVALID_PARAMETER;
3872 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
3873 handle, access, debugstr_us(attr->ObjectName), io, sharing, dispo,
3874 options, pipe_type, read_mode, completion_mode, max_inst, inbound_quota,
3875 outbound_quota, timeout );
3877 /* assume we only get relative timeout */
3878 if (timeout->QuadPart > 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout->QuadPart) );
3880 if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
3882 SERVER_START_REQ( create_named_pipe )
3884 req->access = access;
3885 req->options = options;
3886 req->sharing = sharing;
3887 req->flags =
3888 (pipe_type ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0) |
3889 (read_mode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0) |
3890 (completion_mode ? NAMED_PIPE_NONBLOCKING_MODE : 0);
3891 req->maxinstances = max_inst;
3892 req->outsize = outbound_quota;
3893 req->insize = inbound_quota;
3894 req->timeout = timeout->QuadPart;
3895 wine_server_add_data( req, objattr, len );
3896 if (!(status = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
3898 SERVER_END_REQ;
3900 free( objattr );
3901 return status;
3905 /******************************************************************
3906 * NtDeleteFile (NTDLL.@)
3908 NTSTATUS WINAPI NtDeleteFile( OBJECT_ATTRIBUTES *attr )
3910 HANDLE handle;
3911 NTSTATUS status;
3912 IO_STATUS_BLOCK io;
3914 status = NtCreateFile( &handle, GENERIC_READ | GENERIC_WRITE | DELETE, attr, &io, NULL, 0,
3915 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN,
3916 FILE_DELETE_ON_CLOSE, NULL, 0 );
3917 if (status == STATUS_SUCCESS) NtClose( handle );
3918 return status;
3922 /******************************************************************************
3923 * NtQueryFullAttributesFile (NTDLL.@)
3925 NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr,
3926 FILE_NETWORK_OPEN_INFORMATION *info )
3928 char *unix_name;
3929 NTSTATUS status;
3930 UNICODE_STRING redir;
3931 OBJECT_ATTRIBUTES new_attr = *attr;
3933 get_redirect( &new_attr, &redir );
3934 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
3936 ULONG attributes;
3937 struct stat st;
3939 if (get_file_info( unix_name, &st, &attributes ) == -1)
3940 status = errno_to_status( errno );
3941 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3942 status = STATUS_INVALID_INFO_CLASS;
3943 else
3945 FILE_BASIC_INFORMATION basic;
3946 FILE_STANDARD_INFORMATION std;
3948 fill_file_info( &st, attributes, &basic, FileBasicInformation );
3949 fill_file_info( &st, attributes, &std, FileStandardInformation );
3951 info->CreationTime = basic.CreationTime;
3952 info->LastAccessTime = basic.LastAccessTime;
3953 info->LastWriteTime = basic.LastWriteTime;
3954 info->ChangeTime = basic.ChangeTime;
3955 info->AllocationSize = std.AllocationSize;
3956 info->EndOfFile = std.EndOfFile;
3957 info->FileAttributes = basic.FileAttributes;
3958 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
3960 free( unix_name );
3962 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
3963 free( redir.Buffer );
3964 return status;
3968 /******************************************************************************
3969 * NtQueryAttributesFile (NTDLL.@)
3971 NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC_INFORMATION *info )
3973 char *unix_name;
3974 NTSTATUS status;
3975 UNICODE_STRING redir;
3976 OBJECT_ATTRIBUTES new_attr = *attr;
3978 get_redirect( &new_attr, &redir );
3979 if (!(status = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN )))
3981 ULONG attributes;
3982 struct stat st;
3984 if (get_file_info( unix_name, &st, &attributes ) == -1)
3985 status = errno_to_status( errno );
3986 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
3987 status = STATUS_INVALID_INFO_CLASS;
3988 else
3990 status = fill_file_info( &st, attributes, info, FileBasicInformation );
3991 if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
3993 free( unix_name );
3995 else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
3996 free( redir.Buffer );
3997 return status;
4001 /******************************************************************************
4002 * NtQueryInformationFile (NTDLL.@)
4004 NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4005 void *ptr, LONG len, FILE_INFORMATION_CLASS class )
4007 static const size_t info_sizes[] =
4010 sizeof(FILE_DIRECTORY_INFORMATION), /* FileDirectoryInformation */
4011 sizeof(FILE_FULL_DIRECTORY_INFORMATION), /* FileFullDirectoryInformation */
4012 sizeof(FILE_BOTH_DIRECTORY_INFORMATION), /* FileBothDirectoryInformation */
4013 sizeof(FILE_BASIC_INFORMATION), /* FileBasicInformation */
4014 sizeof(FILE_STANDARD_INFORMATION), /* FileStandardInformation */
4015 sizeof(FILE_INTERNAL_INFORMATION), /* FileInternalInformation */
4016 sizeof(FILE_EA_INFORMATION), /* FileEaInformation */
4017 0, /* FileAccessInformation */
4018 sizeof(FILE_NAME_INFORMATION), /* FileNameInformation */
4019 sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
4020 0, /* FileLinkInformation */
4021 sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR), /* FileNamesInformation */
4022 sizeof(FILE_DISPOSITION_INFORMATION), /* FileDispositionInformation */
4023 sizeof(FILE_POSITION_INFORMATION), /* FilePositionInformation */
4024 sizeof(FILE_FULL_EA_INFORMATION), /* FileFullEaInformation */
4025 0, /* FileModeInformation */
4026 sizeof(FILE_ALIGNMENT_INFORMATION), /* FileAlignmentInformation */
4027 sizeof(FILE_ALL_INFORMATION), /* FileAllInformation */
4028 sizeof(FILE_ALLOCATION_INFORMATION), /* FileAllocationInformation */
4029 sizeof(FILE_END_OF_FILE_INFORMATION), /* FileEndOfFileInformation */
4030 0, /* FileAlternateNameInformation */
4031 sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
4032 sizeof(FILE_PIPE_INFORMATION), /* FilePipeInformation */
4033 sizeof(FILE_PIPE_LOCAL_INFORMATION), /* FilePipeLocalInformation */
4034 0, /* FilePipeRemoteInformation */
4035 sizeof(FILE_MAILSLOT_QUERY_INFORMATION), /* FileMailslotQueryInformation */
4036 0, /* FileMailslotSetInformation */
4037 0, /* FileCompressionInformation */
4038 0, /* FileObjectIdInformation */
4039 0, /* FileCompletionInformation */
4040 0, /* FileMoveClusterInformation */
4041 0, /* FileQuotaInformation */
4042 0, /* FileReparsePointInformation */
4043 sizeof(FILE_NETWORK_OPEN_INFORMATION), /* FileNetworkOpenInformation */
4044 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), /* FileAttributeTagInformation */
4045 0, /* FileTrackingInformation */
4046 0, /* FileIdBothDirectoryInformation */
4047 0, /* FileIdFullDirectoryInformation */
4048 0, /* FileValidDataLengthInformation */
4049 0, /* FileShortNameInformation */
4050 0, /* FileIoCompletionNotificationInformation, */
4051 0, /* FileIoStatusBlockRangeInformation */
4052 0, /* FileIoPriorityHintInformation */
4053 0, /* FileSfioReserveInformation */
4054 0, /* FileSfioVolumeInformation */
4055 0, /* FileHardLinkInformation */
4056 0, /* FileProcessIdsUsingFileInformation */
4057 0, /* FileNormalizedNameInformation */
4058 0, /* FileNetworkPhysicalNameInformation */
4059 0, /* FileIdGlobalTxDirectoryInformation */
4060 0, /* FileIsRemoteDeviceInformation */
4061 0, /* FileAttributeCacheInformation */
4062 0, /* FileNumaNodeInformation */
4063 0, /* FileStandardLinkInformation */
4064 0, /* FileRemoteProtocolInformation */
4065 0, /* FileRenameInformationBypassAccessCheck */
4066 0, /* FileLinkInformationBypassAccessCheck */
4067 0, /* FileVolumeNameInformation */
4068 sizeof(FILE_ID_INFORMATION), /* FileIdInformation */
4069 0, /* FileIdExtdDirectoryInformation */
4070 0, /* FileReplaceCompletionInformation */
4071 0, /* FileHardLinkFullIdInformation */
4072 0, /* FileIdExtdBothDirectoryInformation */
4075 struct stat st;
4076 int fd, needs_close = FALSE;
4077 ULONG attr;
4078 unsigned int options;
4080 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class);
4082 io->Information = 0;
4084 if (class <= 0 || class >= FileMaximumInformation)
4085 return io->u.Status = STATUS_INVALID_INFO_CLASS;
4086 if (!info_sizes[class])
4087 return server_get_file_info( handle, io, ptr, len, class );
4088 if (len < info_sizes[class])
4089 return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4091 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options )))
4093 if (io->u.Status != STATUS_BAD_DEVICE_TYPE) return io->u.Status;
4094 return server_get_file_info( handle, io, ptr, len, class );
4097 switch (class)
4099 case FileBasicInformation:
4100 if (fd_get_file_info( fd, options, &st, &attr ) == -1)
4101 io->u.Status = errno_to_status( errno );
4102 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4103 io->u.Status = STATUS_INVALID_INFO_CLASS;
4104 else
4105 fill_file_info( &st, attr, ptr, class );
4106 break;
4107 case FileStandardInformation:
4109 FILE_STANDARD_INFORMATION *info = ptr;
4111 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4112 else
4114 fill_file_info( &st, attr, info, class );
4115 info->DeletePending = FALSE; /* FIXME */
4118 break;
4119 case FilePositionInformation:
4121 FILE_POSITION_INFORMATION *info = ptr;
4122 off_t res = lseek( fd, 0, SEEK_CUR );
4123 if (res == (off_t)-1) io->u.Status = errno_to_status( errno );
4124 else info->CurrentByteOffset.QuadPart = res;
4126 break;
4127 case FileInternalInformation:
4128 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4129 else fill_file_info( &st, attr, ptr, class );
4130 break;
4131 case FileEaInformation:
4133 FILE_EA_INFORMATION *info = ptr;
4134 info->EaSize = 0;
4136 break;
4137 case FileEndOfFileInformation:
4138 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4139 else fill_file_info( &st, attr, ptr, class );
4140 break;
4141 case FileAllInformation:
4143 FILE_ALL_INFORMATION *info = ptr;
4144 char *unix_name;
4146 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4147 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4148 io->u.Status = STATUS_INVALID_INFO_CLASS;
4149 else if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4151 LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
4153 fill_file_info( &st, attr, info, FileAllInformation );
4154 info->StandardInformation.DeletePending = FALSE; /* FIXME */
4155 info->EaInformation.EaSize = 0;
4156 info->AccessInformation.AccessFlags = 0; /* FIXME */
4157 info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
4158 info->ModeInformation.Mode = 0; /* FIXME */
4159 info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
4161 io->u.Status = fill_name_info( unix_name, &info->NameInformation, &name_len );
4162 free( unix_name );
4163 io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len;
4166 break;
4167 case FileMailslotQueryInformation:
4169 FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;
4171 SERVER_START_REQ( set_mailslot_info )
4173 req->handle = wine_server_obj_handle( handle );
4174 req->flags = 0;
4175 io->u.Status = wine_server_call( req );
4176 if( io->u.Status == STATUS_SUCCESS )
4178 info->MaximumMessageSize = reply->max_msgsize;
4179 info->MailslotQuota = 0;
4180 info->NextMessageSize = 0;
4181 info->MessagesAvailable = 0;
4182 info->ReadTimeout.QuadPart = reply->read_timeout;
4185 SERVER_END_REQ;
4186 if (!io->u.Status)
4188 char *tmpbuf;
4189 ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
4190 if (size > 0x10000) size = 0x10000;
4191 if ((tmpbuf = malloc( size )))
4193 if (!server_get_unix_fd( handle, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
4195 int res = recv( fd, tmpbuf, size, MSG_PEEK );
4196 info->MessagesAvailable = (res > 0);
4197 info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
4198 if (needs_close) close( fd );
4200 free( tmpbuf );
4204 break;
4205 case FileNameInformation:
4207 FILE_NAME_INFORMATION *info = ptr;
4208 char *unix_name;
4210 if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4212 LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
4213 io->u.Status = fill_name_info( unix_name, info, &name_len );
4214 free( unix_name );
4215 io->Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + name_len;
4218 break;
4219 case FileNetworkOpenInformation:
4221 FILE_NETWORK_OPEN_INFORMATION *info = ptr;
4222 char *unix_name;
4224 if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
4226 ULONG attributes;
4227 struct stat st;
4229 if (get_file_info( unix_name, &st, &attributes ) == -1)
4230 io->u.Status = errno_to_status( errno );
4231 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
4232 io->u.Status = STATUS_INVALID_INFO_CLASS;
4233 else
4235 FILE_BASIC_INFORMATION basic;
4236 FILE_STANDARD_INFORMATION std;
4238 fill_file_info( &st, attributes, &basic, FileBasicInformation );
4239 fill_file_info( &st, attributes, &std, FileStandardInformation );
4241 info->CreationTime = basic.CreationTime;
4242 info->LastAccessTime = basic.LastAccessTime;
4243 info->LastWriteTime = basic.LastWriteTime;
4244 info->ChangeTime = basic.ChangeTime;
4245 info->AllocationSize = std.AllocationSize;
4246 info->EndOfFile = std.EndOfFile;
4247 info->FileAttributes = basic.FileAttributes;
4249 free( unix_name );
4252 break;
4253 case FileIdInformation:
4254 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4255 else
4257 struct mountmgr_unix_drive drive;
4258 FILE_ID_INFORMATION *info = ptr;
4260 info->VolumeSerialNumber = 0;
4261 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) ))
4262 info->VolumeSerialNumber = drive.serial;
4263 memset( &info->FileId, 0, sizeof(info->FileId) );
4264 *(ULONGLONG *)&info->FileId = st.st_ino;
4266 break;
4267 case FileAttributeTagInformation:
4268 if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
4269 else
4271 FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
4272 info->FileAttributes = attr;
4273 info->ReparseTag = 0; /* FIXME */
4274 if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
4275 info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
4277 break;
4278 default:
4279 FIXME("Unsupported class (%d)\n", class);
4280 io->u.Status = STATUS_NOT_IMPLEMENTED;
4281 break;
4283 if (needs_close) close( fd );
4284 if (io->u.Status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
4285 return io->u.Status;
4289 /******************************************************************************
4290 * NtSetInformationFile (NTDLL.@)
4292 NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
4293 void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
4295 int fd, needs_close;
4297 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class );
4299 io->u.Status = STATUS_SUCCESS;
4300 switch (class)
4302 case FileBasicInformation:
4303 if (len >= sizeof(FILE_BASIC_INFORMATION))
4305 struct stat st;
4306 const FILE_BASIC_INFORMATION *info = ptr;
4307 LARGE_INTEGER mtime, atime;
4309 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4310 return io->u.Status;
4312 mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart;
4313 atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
4315 if (atime.QuadPart || mtime.QuadPart)
4316 io->u.Status = set_file_times( fd, &mtime, &atime );
4318 if (io->u.Status == STATUS_SUCCESS && info->FileAttributes)
4320 if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
4321 else
4323 if (info->FileAttributes & FILE_ATTRIBUTE_READONLY)
4325 if (S_ISDIR( st.st_mode))
4326 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
4327 else
4328 st.st_mode &= ~0222; /* clear write permission bits */
4330 else
4332 /* add write permission only where we already have read permission */
4333 st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~start_umask);
4335 if (fchmod( fd, st.st_mode ) == -1) io->u.Status = errno_to_status( errno );
4339 if (needs_close) close( fd );
4341 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4342 break;
4344 case FilePositionInformation:
4345 if (len >= sizeof(FILE_POSITION_INFORMATION))
4347 const FILE_POSITION_INFORMATION *info = ptr;
4349 if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
4350 return io->u.Status;
4352 if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
4353 io->u.Status = errno_to_status( errno );
4355 if (needs_close) close( fd );
4357 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4358 break;
4360 case FileEndOfFileInformation:
4361 if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
4363 const FILE_END_OF_FILE_INFORMATION *info = ptr;
4365 SERVER_START_REQ( set_fd_eof_info )
4367 req->handle = wine_server_obj_handle( handle );
4368 req->eof = info->EndOfFile.QuadPart;
4369 io->u.Status = wine_server_call( req );
4371 SERVER_END_REQ;
4373 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4374 break;
4376 case FilePipeInformation:
4377 if (len >= sizeof(FILE_PIPE_INFORMATION))
4379 FILE_PIPE_INFORMATION *info = ptr;
4381 if ((info->CompletionMode | info->ReadMode) & ~1)
4383 io->u.Status = STATUS_INVALID_PARAMETER;
4384 break;
4387 SERVER_START_REQ( set_named_pipe_info )
4389 req->handle = wine_server_obj_handle( handle );
4390 req->flags = (info->CompletionMode ? NAMED_PIPE_NONBLOCKING_MODE : 0) |
4391 (info->ReadMode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0);
4392 io->u.Status = wine_server_call( req );
4394 SERVER_END_REQ;
4396 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4397 break;
4399 case FileMailslotSetInformation:
4401 FILE_MAILSLOT_SET_INFORMATION *info = ptr;
4403 SERVER_START_REQ( set_mailslot_info )
4405 req->handle = wine_server_obj_handle( handle );
4406 req->flags = MAILSLOT_SET_READ_TIMEOUT;
4407 req->read_timeout = info->ReadTimeout.QuadPart;
4408 io->u.Status = wine_server_call( req );
4410 SERVER_END_REQ;
4412 break;
4414 case FileCompletionInformation:
4415 if (len >= sizeof(FILE_COMPLETION_INFORMATION))
4417 FILE_COMPLETION_INFORMATION *info = ptr;
4419 SERVER_START_REQ( set_completion_info )
4421 req->handle = wine_server_obj_handle( handle );
4422 req->chandle = wine_server_obj_handle( info->CompletionPort );
4423 req->ckey = info->CompletionKey;
4424 io->u.Status = wine_server_call( req );
4426 SERVER_END_REQ;
4427 } else
4428 io->u.Status = STATUS_INVALID_PARAMETER_3;
4429 break;
4431 case FileIoCompletionNotificationInformation:
4432 if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION))
4434 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr;
4436 if (info->Flags & FILE_SKIP_SET_USER_EVENT_ON_FAST_IO)
4437 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4439 SERVER_START_REQ( set_fd_completion_mode )
4441 req->handle = wine_server_obj_handle( handle );
4442 req->flags = info->Flags;
4443 io->u.Status = wine_server_call( req );
4445 SERVER_END_REQ;
4446 } else
4447 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4448 break;
4450 case FileIoPriorityHintInformation:
4451 if (len >= sizeof(FILE_IO_PRIORITY_HINT_INFO))
4453 FILE_IO_PRIORITY_HINT_INFO *info = ptr;
4454 if (info->PriorityHint < MaximumIoPriorityHintType)
4455 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info->PriorityHint );
4456 else
4457 io->u.Status = STATUS_INVALID_PARAMETER;
4459 else io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
4460 break;
4462 case FileAllInformation:
4463 io->u.Status = STATUS_INVALID_INFO_CLASS;
4464 break;
4466 case FileValidDataLengthInformation:
4467 if (len >= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION))
4469 struct stat st;
4470 const FILE_VALID_DATA_LENGTH_INFORMATION *info = ptr;
4472 if ((io->u.Status = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL )))
4473 return io->u.Status;
4475 if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
4476 else if (info->ValidDataLength.QuadPart <= 0 || (off_t)info->ValidDataLength.QuadPart > st.st_size)
4477 io->u.Status = STATUS_INVALID_PARAMETER;
4478 else
4480 #ifdef HAVE_FALLOCATE
4481 if (fallocate( fd, 0, 0, (off_t)info->ValidDataLength.QuadPart ) == -1)
4483 NTSTATUS status = errno_to_status( errno );
4484 if (status == STATUS_NOT_SUPPORTED) WARN( "fallocate not supported on this filesystem\n" );
4485 else io->u.Status = status;
4487 #else
4488 FIXME( "setting valid data length not supported\n" );
4489 #endif
4491 if (needs_close) close( fd );
4493 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4494 break;
4496 case FileDispositionInformation:
4497 if (len >= sizeof(FILE_DISPOSITION_INFORMATION))
4499 FILE_DISPOSITION_INFORMATION *info = ptr;
4501 SERVER_START_REQ( set_fd_disp_info )
4503 req->handle = wine_server_obj_handle( handle );
4504 req->unlink = info->DoDeleteFile;
4505 io->u.Status = wine_server_call( req );
4507 SERVER_END_REQ;
4508 } else
4509 io->u.Status = STATUS_INVALID_PARAMETER_3;
4510 break;
4512 case FileRenameInformation:
4513 if (len >= sizeof(FILE_RENAME_INFORMATION))
4515 FILE_RENAME_INFORMATION *info = ptr;
4516 UNICODE_STRING name_str, redir;
4517 OBJECT_ATTRIBUTES attr;
4518 char *unix_name;
4520 name_str.Buffer = info->FileName;
4521 name_str.Length = info->FileNameLength;
4522 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4523 InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL );
4524 get_redirect( &attr, &redir );
4526 io->u.Status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF );
4527 if (io->u.Status == STATUS_SUCCESS || io->u.Status == STATUS_NO_SUCH_FILE)
4529 SERVER_START_REQ( set_fd_name_info )
4531 req->handle = wine_server_obj_handle( handle );
4532 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4533 req->namelen = attr.ObjectName->Length;
4534 req->link = FALSE;
4535 req->replace = info->ReplaceIfExists;
4536 wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length );
4537 wine_server_add_data( req, unix_name, strlen(unix_name) );
4538 io->u.Status = wine_server_call( req );
4540 SERVER_END_REQ;
4542 free( unix_name );
4544 free( redir.Buffer );
4546 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4547 break;
4549 case FileLinkInformation:
4550 if (len >= sizeof(FILE_LINK_INFORMATION))
4552 FILE_LINK_INFORMATION *info = ptr;
4553 UNICODE_STRING name_str, redir;
4554 OBJECT_ATTRIBUTES attr;
4555 char *unix_name;
4557 name_str.Buffer = info->FileName;
4558 name_str.Length = info->FileNameLength;
4559 name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
4560 InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL );
4561 get_redirect( &attr, &redir );
4563 io->u.Status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF );
4564 if (io->u.Status == STATUS_SUCCESS || io->u.Status == STATUS_NO_SUCH_FILE)
4566 SERVER_START_REQ( set_fd_name_info )
4568 req->handle = wine_server_obj_handle( handle );
4569 req->rootdir = wine_server_obj_handle( attr.RootDirectory );
4570 req->namelen = attr.ObjectName->Length;
4571 req->link = TRUE;
4572 req->replace = info->ReplaceIfExists;
4573 wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length );
4574 wine_server_add_data( req, unix_name, strlen(unix_name) );
4575 io->u.Status = wine_server_call( req );
4577 SERVER_END_REQ;
4579 free( unix_name );
4581 free( redir.Buffer );
4583 else io->u.Status = STATUS_INVALID_PARAMETER_3;
4584 break;
4586 default:
4587 FIXME("Unsupported class (%d)\n", class);
4588 io->u.Status = STATUS_NOT_IMPLEMENTED;
4589 break;
4591 io->Information = 0;
4592 return io->u.Status;
4596 /***********************************************************************
4597 * Asynchronous file I/O *
4600 struct async_fileio_read
4602 struct async_fileio io;
4603 char *buffer;
4604 unsigned int already;
4605 unsigned int count;
4606 BOOL avail_mode;
4609 struct async_fileio_write
4611 struct async_fileio io;
4612 const char *buffer;
4613 unsigned int already;
4614 unsigned int count;
4617 struct async_fileio_read_changes
4619 struct async_fileio io;
4620 void *buffer;
4621 ULONG buffer_size;
4622 ULONG data_size;
4623 char data[1];
4626 struct async_irp
4628 struct async_fileio io;
4629 void *buffer; /* buffer for output */
4630 ULONG size; /* size of buffer */
4633 static struct async_fileio *fileio_freelist;
4635 void release_fileio( struct async_fileio *io )
4637 for (;;)
4639 struct async_fileio *next = fileio_freelist;
4640 io->next = next;
4641 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist, io, next ) == next) return;
4645 struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle )
4647 /* first free remaining previous fileinfos */
4648 struct async_fileio *io = InterlockedExchangePointer( (void **)&fileio_freelist, NULL );
4650 while (io)
4652 struct async_fileio *next = io->next;
4653 free( io );
4654 io = next;
4657 if ((io = malloc( size )))
4659 io->callback = callback;
4660 io->handle = handle;
4662 return io;
4665 static async_data_t server_async( HANDLE handle, struct async_fileio *user, HANDLE event,
4666 PIO_APC_ROUTINE apc, void *apc_context, IO_STATUS_BLOCK *io )
4668 async_data_t async;
4669 async.handle = wine_server_obj_handle( handle );
4670 async.user = wine_server_client_ptr( user );
4671 async.iosb = wine_server_client_ptr( io );
4672 async.event = wine_server_obj_handle( event );
4673 async.apc = wine_server_client_ptr( apc );
4674 async.apc_context = wine_server_client_ptr( apc_context );
4675 return async;
4678 static NTSTATUS wait_async( HANDLE handle, BOOL alertable )
4680 return NtWaitForSingleObject( handle, alertable, NULL );
4683 /* callback for irp async I/O completion */
4684 static NTSTATUS irp_completion( void *user, IO_STATUS_BLOCK *io, NTSTATUS status )
4686 struct async_irp *async = user;
4687 ULONG information = 0;
4689 if (status == STATUS_ALERTED)
4691 SERVER_START_REQ( get_async_result )
4693 req->user_arg = wine_server_client_ptr( async );
4694 wine_server_set_reply( req, async->buffer, async->size );
4695 status = virtual_locked_server_call( req );
4696 information = reply->size;
4698 SERVER_END_REQ;
4700 if (status != STATUS_PENDING)
4702 io->u.Status = status;
4703 io->Information = information;
4704 release_fileio( &async->io );
4706 return status;
4709 static NTSTATUS async_read_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
4711 struct async_fileio_read *fileio = user;
4712 int fd, needs_close, result;
4714 switch (status)
4716 case STATUS_ALERTED: /* got some new data */
4717 /* check to see if the data is ready (non-blocking) */
4718 if ((status = server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd,
4719 &needs_close, NULL, NULL )))
4720 break;
4722 result = virtual_locked_read(fd, &fileio->buffer[fileio->already], fileio->count-fileio->already);
4723 if (needs_close) close( fd );
4725 if (result < 0)
4727 if (errno == EAGAIN || errno == EINTR)
4728 status = STATUS_PENDING;
4729 else /* check to see if the transfer is complete */
4730 status = errno_to_status( errno );
4732 else if (result == 0)
4734 status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN;
4736 else
4738 fileio->already += result;
4739 if (fileio->already >= fileio->count || fileio->avail_mode)
4740 status = STATUS_SUCCESS;
4741 else
4742 status = STATUS_PENDING;
4744 break;
4746 case STATUS_TIMEOUT:
4747 case STATUS_IO_TIMEOUT:
4748 if (fileio->already) status = STATUS_SUCCESS;
4749 break;
4751 if (status != STATUS_PENDING)
4753 iosb->u.Status = status;
4754 iosb->Information = fileio->already;
4755 release_fileio( &fileio->io );
4757 return status;
4760 static NTSTATUS async_write_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
4762 struct async_fileio_write *fileio = user;
4763 int result, fd, needs_close;
4764 enum server_fd_type type;
4766 switch (status)
4768 case STATUS_ALERTED:
4769 /* write some data (non-blocking) */
4770 if ((status = server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd,
4771 &needs_close, &type, NULL )))
4772 break;
4774 if (!fileio->count && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET))
4775 result = send( fd, fileio->buffer, 0, 0 );
4776 else
4777 result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already );
4779 if (needs_close) close( fd );
4781 if (result < 0)
4783 if (errno == EAGAIN || errno == EINTR) status = STATUS_PENDING;
4784 else status = errno_to_status( errno );
4786 else
4788 fileio->already += result;
4789 status = (fileio->already < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS;
4791 break;
4793 case STATUS_TIMEOUT:
4794 case STATUS_IO_TIMEOUT:
4795 if (fileio->already) status = STATUS_SUCCESS;
4796 break;
4798 if (status != STATUS_PENDING)
4800 iosb->u.Status = status;
4801 iosb->Information = fileio->already;
4802 release_fileio( &fileio->io );
4804 return status;
4807 /* do a read call through the server */
4808 static NTSTATUS server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
4809 IO_STATUS_BLOCK *io, void *buffer, ULONG size,
4810 LARGE_INTEGER *offset, ULONG *key )
4812 struct async_irp *async;
4813 NTSTATUS status;
4814 HANDLE wait_handle;
4815 ULONG options;
4817 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4818 return STATUS_NO_MEMORY;
4820 async->buffer = buffer;
4821 async->size = size;
4823 SERVER_START_REQ( read )
4825 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4826 req->pos = offset ? offset->QuadPart : 0;
4827 wine_server_set_reply( req, buffer, size );
4828 status = virtual_locked_server_call( req );
4829 wait_handle = wine_server_ptr_handle( reply->wait );
4830 options = reply->options;
4831 if (wait_handle && status != STATUS_PENDING)
4833 io->u.Status = status;
4834 io->Information = wine_server_reply_size( reply );
4837 SERVER_END_REQ;
4839 if (status != STATUS_PENDING) free( async );
4841 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
4842 return status;
4845 /* do a write call through the server */
4846 static NTSTATUS server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
4847 IO_STATUS_BLOCK *io, const void *buffer, ULONG size,
4848 LARGE_INTEGER *offset, ULONG *key )
4850 struct async_irp *async;
4851 NTSTATUS status;
4852 HANDLE wait_handle;
4853 ULONG options;
4855 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4856 return STATUS_NO_MEMORY;
4858 async->buffer = NULL;
4859 async->size = 0;
4861 SERVER_START_REQ( write )
4863 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4864 req->pos = offset ? offset->QuadPart : 0;
4865 wine_server_add_data( req, buffer, size );
4866 status = wine_server_call( req );
4867 wait_handle = wine_server_ptr_handle( reply->wait );
4868 options = reply->options;
4869 if (wait_handle && status != STATUS_PENDING)
4871 io->u.Status = status;
4872 io->Information = reply->size;
4875 SERVER_END_REQ;
4877 if (status != STATUS_PENDING) free( async );
4879 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
4880 return status;
4883 /* do an ioctl call through the server */
4884 static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
4885 PIO_APC_ROUTINE apc, PVOID apc_context,
4886 IO_STATUS_BLOCK *io, ULONG code,
4887 const void *in_buffer, ULONG in_size,
4888 PVOID out_buffer, ULONG out_size )
4890 struct async_irp *async;
4891 NTSTATUS status;
4892 HANDLE wait_handle;
4893 ULONG options;
4895 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
4896 return STATUS_NO_MEMORY;
4897 async->buffer = out_buffer;
4898 async->size = out_size;
4900 SERVER_START_REQ( ioctl )
4902 req->code = code;
4903 req->async = server_async( handle, &async->io, event, apc, apc_context, io );
4904 wine_server_add_data( req, in_buffer, in_size );
4905 if ((code & 3) != METHOD_BUFFERED) wine_server_add_data( req, out_buffer, out_size );
4906 wine_server_set_reply( req, out_buffer, out_size );
4907 status = virtual_locked_server_call( req );
4908 wait_handle = wine_server_ptr_handle( reply->wait );
4909 options = reply->options;
4910 if (wait_handle && status != STATUS_PENDING)
4912 io->u.Status = status;
4913 io->Information = wine_server_reply_size( reply );
4916 SERVER_END_REQ;
4918 if (status == STATUS_NOT_SUPPORTED)
4919 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
4920 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
4922 if (status != STATUS_PENDING) free( async );
4924 if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
4925 return status;
4929 struct io_timeouts
4931 int interval; /* max interval between two bytes */
4932 int total; /* total timeout for the whole operation */
4933 int end_time; /* absolute time of end of operation */
4936 /* retrieve the I/O timeouts to use for a given handle */
4937 static NTSTATUS get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read,
4938 struct io_timeouts *timeouts )
4940 NTSTATUS status = STATUS_SUCCESS;
4942 timeouts->interval = timeouts->total = -1;
4944 switch(type)
4946 case FD_TYPE_SERIAL:
4948 /* GetCommTimeouts */
4949 SERIAL_TIMEOUTS st;
4950 IO_STATUS_BLOCK io;
4952 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
4953 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
4954 if (status) break;
4956 if (is_read)
4958 if (st.ReadIntervalTimeout)
4959 timeouts->interval = st.ReadIntervalTimeout;
4961 if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant)
4963 timeouts->total = st.ReadTotalTimeoutConstant;
4964 if (st.ReadTotalTimeoutMultiplier != MAXDWORD)
4965 timeouts->total += count * st.ReadTotalTimeoutMultiplier;
4967 else if (st.ReadIntervalTimeout == MAXDWORD)
4968 timeouts->interval = timeouts->total = 0;
4970 else /* write */
4972 if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant)
4974 timeouts->total = st.WriteTotalTimeoutConstant;
4975 if (st.WriteTotalTimeoutMultiplier != MAXDWORD)
4976 timeouts->total += count * st.WriteTotalTimeoutMultiplier;
4979 break;
4981 case FD_TYPE_MAILSLOT:
4982 if (is_read)
4984 timeouts->interval = 0; /* return as soon as we got something */
4985 SERVER_START_REQ( set_mailslot_info )
4987 req->handle = wine_server_obj_handle( handle );
4988 req->flags = 0;
4989 if (!(status = wine_server_call( req )) &&
4990 reply->read_timeout != TIMEOUT_INFINITE)
4991 timeouts->total = reply->read_timeout / -10000;
4993 SERVER_END_REQ;
4995 break;
4996 case FD_TYPE_SOCKET:
4997 case FD_TYPE_CHAR:
4998 if (is_read) timeouts->interval = 0; /* return as soon as we got something */
4999 break;
5000 default:
5001 break;
5003 if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total;
5004 return STATUS_SUCCESS;
5008 /* retrieve the timeout for the next wait, in milliseconds */
5009 static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already )
5011 int ret = -1;
5013 if (timeouts->total != -1)
5015 ret = timeouts->end_time - NtGetTickCount();
5016 if (ret < 0) ret = 0;
5018 if (already && timeouts->interval != -1)
5020 if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval;
5022 return ret;
5026 /* retrieve the avail_mode flag for async reads */
5027 static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode )
5029 NTSTATUS status = STATUS_SUCCESS;
5031 switch(type)
5033 case FD_TYPE_SERIAL:
5035 /* GetCommTimeouts */
5036 SERIAL_TIMEOUTS st;
5037 IO_STATUS_BLOCK io;
5039 status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io,
5040 IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) );
5041 if (status) break;
5042 *avail_mode = (!st.ReadTotalTimeoutMultiplier &&
5043 !st.ReadTotalTimeoutConstant &&
5044 st.ReadIntervalTimeout == MAXDWORD);
5045 break;
5047 case FD_TYPE_MAILSLOT:
5048 case FD_TYPE_SOCKET:
5049 case FD_TYPE_CHAR:
5050 *avail_mode = TRUE;
5051 break;
5052 default:
5053 *avail_mode = FALSE;
5054 break;
5056 return status;
5059 /* register an async I/O for a file read; helper for NtReadFile */
5060 static NTSTATUS register_async_file_read( HANDLE handle, HANDLE event,
5061 PIO_APC_ROUTINE apc, void *apc_user,
5062 IO_STATUS_BLOCK *iosb, void *buffer,
5063 ULONG already, ULONG length, BOOL avail_mode )
5065 struct async_fileio_read *fileio;
5066 NTSTATUS status;
5068 if (!(fileio = (struct async_fileio_read *)alloc_fileio( sizeof(*fileio), async_read_proc, handle )))
5069 return STATUS_NO_MEMORY;
5071 fileio->already = already;
5072 fileio->count = length;
5073 fileio->buffer = buffer;
5074 fileio->avail_mode = avail_mode;
5076 SERVER_START_REQ( register_async )
5078 req->type = ASYNC_TYPE_READ;
5079 req->count = length;
5080 req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb );
5081 status = wine_server_call( req );
5083 SERVER_END_REQ;
5085 if (status != STATUS_PENDING) free( fileio );
5086 return status;
5089 void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async )
5091 SERVER_START_REQ( add_fd_completion )
5093 req->handle = wine_server_obj_handle( handle );
5094 req->cvalue = value;
5095 req->status = status;
5096 req->information = info;
5097 req->async = async;
5098 wine_server_call( req );
5100 SERVER_END_REQ;
5103 static NTSTATUS set_pending_write( HANDLE device )
5105 NTSTATUS status;
5107 SERVER_START_REQ( set_serial_info )
5109 req->handle = wine_server_obj_handle( device );
5110 req->flags = SERIALINFO_PENDING_WRITE;
5111 status = wine_server_call( req );
5113 SERVER_END_REQ;
5114 return status;
5118 /******************************************************************************
5119 * NtReadFile (NTDLL.@)
5121 NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5122 IO_STATUS_BLOCK *io, void *buffer, ULONG length,
5123 LARGE_INTEGER *offset, ULONG *key )
5125 int result, unix_handle, needs_close;
5126 unsigned int options;
5127 struct io_timeouts timeouts;
5128 NTSTATUS status, ret_status;
5129 ULONG total = 0;
5130 enum server_fd_type type;
5131 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5132 BOOL send_completion = FALSE, async_read, timeout_init_done = FALSE;
5134 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5135 handle, event, apc, apc_user, io, buffer, length, offset, key );
5137 if (!io) return STATUS_ACCESS_VIOLATION;
5139 status = server_get_unix_fd( handle, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5140 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5142 if (!virtual_check_buffer_for_write( buffer, length )) return STATUS_ACCESS_VIOLATION;
5144 if (status == STATUS_BAD_DEVICE_TYPE)
5145 return server_read_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5147 async_read = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5149 if (type == FD_TYPE_FILE)
5151 if (async_read && (!offset || offset->QuadPart < 0))
5153 status = STATUS_INVALID_PARAMETER;
5154 goto done;
5157 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5159 /* async I/O doesn't make sense on regular files */
5160 while ((result = virtual_locked_pread( unix_handle, buffer, length, offset->QuadPart )) == -1)
5162 if (errno != EINTR)
5164 status = errno_to_status( errno );
5165 goto done;
5168 if (!async_read) /* update file pointer position */
5169 lseek( unix_handle, offset->QuadPart + result, SEEK_SET );
5171 total = result;
5172 status = (total || !length) ? STATUS_SUCCESS : STATUS_END_OF_FILE;
5173 goto done;
5176 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5178 if (async_read && (!offset || offset->QuadPart < 0))
5180 status = STATUS_INVALID_PARAMETER;
5181 goto done;
5185 if (type == FD_TYPE_SERIAL && async_read && length)
5187 /* an asynchronous serial port read with a read interval timeout needs to
5188 skip the synchronous read to make sure that the server starts the read
5189 interval timer after the first read */
5190 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5191 if (timeouts.interval)
5193 status = register_async_file_read( handle, event, apc, apc_user, io,
5194 buffer, total, length, FALSE );
5195 goto err;
5199 for (;;)
5201 if ((result = virtual_locked_read( unix_handle, (char *)buffer + total, length - total )) >= 0)
5203 total += result;
5204 if (!result || total == length)
5206 if (total)
5208 status = STATUS_SUCCESS;
5209 goto done;
5211 switch (type)
5213 case FD_TYPE_FILE:
5214 case FD_TYPE_CHAR:
5215 case FD_TYPE_DEVICE:
5216 status = length ? STATUS_END_OF_FILE : STATUS_SUCCESS;
5217 goto done;
5218 case FD_TYPE_SERIAL:
5219 if (!length)
5221 status = STATUS_SUCCESS;
5222 goto done;
5224 break;
5225 default:
5226 status = STATUS_PIPE_BROKEN;
5227 goto err;
5230 else if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5232 else if (errno != EAGAIN)
5234 if (errno == EINTR) continue;
5235 if (!total) status = errno_to_status( errno );
5236 goto err;
5239 if (async_read)
5241 BOOL avail_mode;
5243 if ((status = get_io_avail_mode( handle, type, &avail_mode ))) goto err;
5244 if (total && avail_mode)
5246 status = STATUS_SUCCESS;
5247 goto done;
5249 status = register_async_file_read( handle, event, apc, apc_user, io,
5250 buffer, total, length, avail_mode );
5251 goto err;
5253 else /* synchronous read, wait for the fd to become ready */
5255 struct pollfd pfd;
5256 int ret, timeout;
5258 if (!timeout_init_done)
5260 timeout_init_done = TRUE;
5261 if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err;
5262 if (event) NtResetEvent( event, NULL );
5264 timeout = get_next_io_timeout( &timeouts, total );
5266 pfd.fd = unix_handle;
5267 pfd.events = POLLIN;
5269 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5271 if (total) /* return with what we got so far */
5272 status = STATUS_SUCCESS;
5273 else
5274 status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT;
5275 goto done;
5277 if (ret == -1 && errno != EINTR)
5279 status = errno_to_status( errno );
5280 goto done;
5282 /* will now restart the read */
5286 done:
5287 send_completion = cvalue != 0;
5289 err:
5290 if (needs_close) close( unix_handle );
5291 if (status == STATUS_SUCCESS || (status == STATUS_END_OF_FILE && (!async_read || type == FD_TYPE_FILE)))
5293 io->u.Status = status;
5294 io->Information = total;
5295 TRACE("= SUCCESS (%u)\n", total);
5296 if (event) NtSetEvent( event, NULL );
5297 if (apc && (!status || async_read)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5298 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5300 else
5302 TRACE("= 0x%08x\n", status);
5303 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5306 ret_status = async_read && type == FD_TYPE_FILE && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE)
5307 ? STATUS_PENDING : status;
5309 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5310 return ret_status;
5314 /******************************************************************************
5315 * NtReadFileScatter (NTDLL.@)
5317 NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5318 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5319 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5321 int result, unix_handle, needs_close;
5322 unsigned int options;
5323 NTSTATUS status;
5324 ULONG pos = 0, total = 0;
5325 enum server_fd_type type;
5326 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5327 BOOL send_completion = FALSE;
5329 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5330 file, event, apc, apc_user, io, segments, length, offset, key );
5332 if (!io) return STATUS_ACCESS_VIOLATION;
5334 status = server_get_unix_fd( file, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options );
5335 if (status) return status;
5337 if ((type != FD_TYPE_FILE) ||
5338 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5339 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5341 status = STATUS_INVALID_PARAMETER;
5342 goto error;
5345 while (length)
5347 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5348 result = pread( unix_handle, (char *)segments->Buffer + pos,
5349 min( length - pos, page_size - pos ), offset->QuadPart + total );
5350 else
5351 result = read( unix_handle, (char *)segments->Buffer + pos, min( length - pos, page_size - pos ) );
5353 if (result == -1)
5355 if (errno == EINTR) continue;
5356 status = errno_to_status( errno );
5357 break;
5359 if (!result) break;
5360 total += result;
5361 length -= result;
5362 if ((pos += result) == page_size)
5364 pos = 0;
5365 segments++;
5369 if (total == 0) status = STATUS_END_OF_FILE;
5371 send_completion = cvalue != 0;
5373 if (needs_close) close( unix_handle );
5374 io->u.Status = status;
5375 io->Information = total;
5376 TRACE("= 0x%08x (%u)\n", status, total);
5377 if (event) NtSetEvent( event, NULL );
5378 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5379 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5380 if (send_completion) add_completion( file, cvalue, status, total, TRUE );
5382 return STATUS_PENDING;
5384 error:
5385 if (needs_close) close( unix_handle );
5386 if (event) NtResetEvent( event, NULL );
5387 TRACE("= 0x%08x\n", status);
5388 return status;
5392 /******************************************************************************
5393 * NtWriteFile (NTDLL.@)
5395 NTSTATUS WINAPI NtWriteFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5396 IO_STATUS_BLOCK *io, const void *buffer, ULONG length,
5397 LARGE_INTEGER *offset, ULONG *key )
5399 int result, unix_handle, needs_close;
5400 unsigned int options;
5401 struct io_timeouts timeouts;
5402 NTSTATUS status, ret_status;
5403 ULONG total = 0;
5404 enum server_fd_type type;
5405 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5406 BOOL send_completion = FALSE, async_write, append_write = FALSE, timeout_init_done = FALSE;
5407 LARGE_INTEGER offset_eof;
5409 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5410 handle, event, apc, apc_user, io, buffer, length, offset, key );
5412 if (!io) return STATUS_ACCESS_VIOLATION;
5414 status = server_get_unix_fd( handle, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5415 if (status == STATUS_ACCESS_DENIED)
5417 status = server_get_unix_fd( handle, FILE_APPEND_DATA, &unix_handle,
5418 &needs_close, &type, &options );
5419 append_write = TRUE;
5421 if (status && status != STATUS_BAD_DEVICE_TYPE) return status;
5423 async_write = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT));
5425 if (!virtual_check_buffer_for_read( buffer, length ))
5427 status = STATUS_INVALID_USER_BUFFER;
5428 goto done;
5431 if (status == STATUS_BAD_DEVICE_TYPE)
5432 return server_write_file( handle, event, apc, apc_user, io, buffer, length, offset, key );
5434 if (type == FD_TYPE_FILE)
5436 if (async_write &&
5437 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5439 status = STATUS_INVALID_PARAMETER;
5440 goto done;
5443 if (append_write)
5445 offset_eof.QuadPart = FILE_WRITE_TO_END_OF_FILE;
5446 offset = &offset_eof;
5449 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5451 off_t off = offset->QuadPart;
5453 if (offset->QuadPart == FILE_WRITE_TO_END_OF_FILE)
5455 struct stat st;
5457 if (fstat( unix_handle, &st ) == -1)
5459 status = errno_to_status( errno );
5460 goto done;
5462 off = st.st_size;
5464 else if (offset->QuadPart < 0)
5466 status = STATUS_INVALID_PARAMETER;
5467 goto done;
5470 /* async I/O doesn't make sense on regular files */
5471 while ((result = pwrite( unix_handle, buffer, length, off )) == -1)
5473 if (errno != EINTR)
5475 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5476 else status = errno_to_status( errno );
5477 goto done;
5481 if (!async_write) /* update file pointer position */
5482 lseek( unix_handle, off + result, SEEK_SET );
5484 total = result;
5485 status = STATUS_SUCCESS;
5486 goto done;
5489 else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE)
5491 if (async_write &&
5492 (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE)))
5494 status = STATUS_INVALID_PARAMETER;
5495 goto done;
5499 for (;;)
5501 /* zero-length writes on sockets may not work with plain write(2) */
5502 if (!length && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET))
5503 result = send( unix_handle, buffer, 0, 0 );
5504 else
5505 result = write( unix_handle, (const char *)buffer + total, length - total );
5507 if (result >= 0)
5509 total += result;
5510 if (total == length)
5512 status = STATUS_SUCCESS;
5513 goto done;
5515 if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */
5517 else if (errno != EAGAIN)
5519 if (errno == EINTR) continue;
5520 if (!total)
5522 if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
5523 else status = errno_to_status( errno );
5525 goto err;
5528 if (async_write)
5530 struct async_fileio_write *fileio;
5532 fileio = (struct async_fileio_write *)alloc_fileio( sizeof(*fileio), async_write_proc, handle );
5533 if (!fileio)
5535 status = STATUS_NO_MEMORY;
5536 goto err;
5538 fileio->already = total;
5539 fileio->count = length;
5540 fileio->buffer = buffer;
5542 SERVER_START_REQ( register_async )
5544 req->type = ASYNC_TYPE_WRITE;
5545 req->count = length;
5546 req->async = server_async( handle, &fileio->io, event, apc, apc_user, io );
5547 status = wine_server_call( req );
5549 SERVER_END_REQ;
5551 if (status != STATUS_PENDING) free( fileio );
5552 goto err;
5554 else /* synchronous write, wait for the fd to become ready */
5556 struct pollfd pfd;
5557 int ret, timeout;
5559 if (!timeout_init_done)
5561 timeout_init_done = TRUE;
5562 if ((status = get_io_timeouts( handle, type, length, FALSE, &timeouts )))
5563 goto err;
5564 if (event) NtResetEvent( event, NULL );
5566 timeout = get_next_io_timeout( &timeouts, total );
5568 pfd.fd = unix_handle;
5569 pfd.events = POLLOUT;
5571 if (!timeout || !(ret = poll( &pfd, 1, timeout )))
5573 /* return with what we got so far */
5574 status = total ? STATUS_SUCCESS : STATUS_TIMEOUT;
5575 goto done;
5577 if (ret == -1 && errno != EINTR)
5579 status = errno_to_status( errno );
5580 goto done;
5582 /* will now restart the write */
5586 done:
5587 send_completion = cvalue != 0;
5589 err:
5590 if (needs_close) close( unix_handle );
5592 if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING))
5593 set_pending_write( handle );
5595 if (status == STATUS_SUCCESS)
5597 io->u.Status = status;
5598 io->Information = total;
5599 TRACE("= SUCCESS (%u)\n", total);
5600 if (event) NtSetEvent( event, NULL );
5601 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5602 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5604 else
5606 TRACE("= 0x%08x\n", status);
5607 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5610 ret_status = async_write && type == FD_TYPE_FILE && status == STATUS_SUCCESS ? STATUS_PENDING : status;
5611 if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING );
5612 return ret_status;
5616 /******************************************************************************
5617 * NtWriteFileGather (NTDLL.@)
5619 NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
5620 IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments,
5621 ULONG length, LARGE_INTEGER *offset, ULONG *key )
5623 int result, unix_handle, needs_close;
5624 unsigned int options;
5625 NTSTATUS status;
5626 ULONG pos = 0, total = 0;
5627 enum server_fd_type type;
5628 ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user;
5629 BOOL send_completion = FALSE;
5631 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5632 file, event, apc, apc_user, io, segments, length, offset, key );
5634 if (length % page_size) return STATUS_INVALID_PARAMETER;
5635 if (!io) return STATUS_ACCESS_VIOLATION;
5637 status = server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options );
5638 if (status) return status;
5640 if ((type != FD_TYPE_FILE) ||
5641 (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ||
5642 !(options & FILE_NO_INTERMEDIATE_BUFFERING))
5644 status = STATUS_INVALID_PARAMETER;
5645 goto done;
5648 while (length)
5650 if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION)
5651 result = pwrite( unix_handle, (char *)segments->Buffer + pos,
5652 page_size - pos, offset->QuadPart + total );
5653 else
5654 result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos );
5656 if (result == -1)
5658 if (errno == EINTR) continue;
5659 if (errno == EFAULT)
5661 status = STATUS_INVALID_USER_BUFFER;
5662 goto done;
5664 status = errno_to_status( errno );
5665 break;
5667 if (!result)
5669 status = STATUS_DISK_FULL;
5670 break;
5672 total += result;
5673 length -= result;
5674 if ((pos += result) == page_size)
5676 pos = 0;
5677 segments++;
5681 send_completion = cvalue != 0;
5683 done:
5684 if (needs_close) close( unix_handle );
5685 if (status == STATUS_SUCCESS)
5687 io->u.Status = status;
5688 io->Information = total;
5689 TRACE("= SUCCESS (%u)\n", total);
5690 if (event) NtSetEvent( event, NULL );
5691 if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
5692 (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 );
5694 else
5696 TRACE("= 0x%08x\n", status);
5697 if (status != STATUS_PENDING && event) NtResetEvent( event, NULL );
5699 if (send_completion) add_completion( file, cvalue, status, total, FALSE );
5700 return status;
5704 /******************************************************************************
5705 * NtDeviceIoControlFile (NTDLL.@)
5707 NTSTATUS WINAPI NtDeviceIoControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5708 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5709 void *out_buffer, ULONG out_size )
5711 ULONG device = (code >> 16);
5712 NTSTATUS status = STATUS_NOT_SUPPORTED;
5714 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5715 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5717 switch (device)
5719 case FILE_DEVICE_BEEP:
5720 case FILE_DEVICE_NETWORK:
5721 status = sock_ioctl( handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5722 break;
5723 case FILE_DEVICE_DISK:
5724 case FILE_DEVICE_CD_ROM:
5725 case FILE_DEVICE_DVD:
5726 case FILE_DEVICE_CONTROLLER:
5727 case FILE_DEVICE_MASS_STORAGE:
5728 status = cdrom_DeviceIoControl( handle, event, apc, apc_context, io, code,
5729 in_buffer, in_size, out_buffer, out_size );
5730 break;
5731 case FILE_DEVICE_SERIAL_PORT:
5732 status = serial_DeviceIoControl( handle, event, apc, apc_context, io, code,
5733 in_buffer, in_size, out_buffer, out_size );
5734 break;
5735 case FILE_DEVICE_TAPE:
5736 status = tape_DeviceIoControl( handle, event, apc, apc_context, io, code,
5737 in_buffer, in_size, out_buffer, out_size );
5738 break;
5741 if (status == STATUS_NOT_SUPPORTED || status == STATUS_BAD_DEVICE_TYPE)
5742 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5743 in_buffer, in_size, out_buffer, out_size );
5745 if (status != STATUS_PENDING) io->u.Status = status;
5746 return status;
5750 /* Tell Valgrind to ignore any holes in structs we will be passing to the
5751 * server */
5752 static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer, ULONG in_size )
5754 #ifdef VALGRIND_MAKE_MEM_DEFINED
5755 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
5756 do { \
5757 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
5758 if ((size) >= FIELD_OFFSET(t, f2)) \
5759 VALGRIND_MAKE_MEM_DEFINED( \
5760 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
5761 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
5762 } while (0)
5764 switch (code)
5766 case FSCTL_PIPE_WAIT:
5767 IGNORE_STRUCT_HOLE(in_buffer, in_size, FILE_PIPE_WAIT_FOR_BUFFER, TimeoutSpecified, Name);
5768 break;
5770 #endif
5774 /******************************************************************************
5775 * NtFsControlFile (NTDLL.@)
5777 NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context,
5778 IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size,
5779 void *out_buffer, ULONG out_size )
5781 NTSTATUS status;
5783 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5784 handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size );
5786 if (!io) return STATUS_INVALID_PARAMETER;
5788 ignore_server_ioctl_struct_holes( code, in_buffer, in_size );
5790 switch (code)
5792 case FSCTL_DISMOUNT_VOLUME:
5793 status = server_ioctl_file( handle, event, apc, apc_context, io, code,
5794 in_buffer, in_size, out_buffer, out_size );
5795 if (!status) status = unmount_device( handle );
5796 return status;
5798 case FSCTL_PIPE_IMPERSONATE:
5799 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
5800 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5801 in_buffer, in_size, out_buffer, out_size );
5803 case FSCTL_IS_VOLUME_MOUNTED:
5804 case FSCTL_LOCK_VOLUME:
5805 case FSCTL_UNLOCK_VOLUME:
5806 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
5807 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
5808 status = STATUS_SUCCESS;
5809 break;
5811 case FSCTL_GET_RETRIEVAL_POINTERS:
5813 RETRIEVAL_POINTERS_BUFFER *buffer = (RETRIEVAL_POINTERS_BUFFER *)out_buffer;
5815 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
5817 if (out_size >= sizeof(RETRIEVAL_POINTERS_BUFFER))
5819 buffer->ExtentCount = 1;
5820 buffer->StartingVcn.QuadPart = 1;
5821 buffer->Extents[0].NextVcn.QuadPart = 0;
5822 buffer->Extents[0].Lcn.QuadPart = 0;
5823 io->Information = sizeof(RETRIEVAL_POINTERS_BUFFER);
5824 status = STATUS_SUCCESS;
5826 else
5828 io->Information = 0;
5829 status = STATUS_BUFFER_TOO_SMALL;
5831 break;
5834 case FSCTL_GET_OBJECT_ID:
5836 FILE_OBJECTID_BUFFER *info = out_buffer;
5837 int fd, needs_close;
5838 struct stat st;
5840 io->Information = 0;
5841 if (out_size >= sizeof(*info))
5843 status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
5844 if (status) break;
5845 fstat( fd, &st );
5846 if (needs_close) close( fd );
5847 memset( info, 0, sizeof(*info) );
5848 memcpy( info->ObjectId, &st.st_dev, sizeof(st.st_dev) );
5849 memcpy( info->ObjectId + 8, &st.st_ino, sizeof(st.st_ino) );
5850 io->Information = sizeof(*info);
5852 else status = STATUS_BUFFER_TOO_SMALL;
5853 break;
5856 case FSCTL_SET_SPARSE:
5857 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
5858 io->Information = 0;
5859 status = STATUS_SUCCESS;
5860 break;
5861 default:
5862 return server_ioctl_file( handle, event, apc, apc_context, io, code,
5863 in_buffer, in_size, out_buffer, out_size );
5866 if (status != STATUS_PENDING) io->u.Status = status;
5867 return status;
5871 /******************************************************************************
5872 * NtFlushBuffersFile (NTDLL.@)
5874 NTSTATUS WINAPI NtFlushBuffersFile( HANDLE handle, IO_STATUS_BLOCK *io )
5876 NTSTATUS ret;
5877 HANDLE wait_handle;
5878 enum server_fd_type type;
5879 int fd, needs_close;
5881 if (!io || !virtual_check_buffer_for_write( io, sizeof(*io) )) return STATUS_ACCESS_VIOLATION;
5883 ret = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, &type, NULL );
5884 if (ret == STATUS_ACCESS_DENIED)
5885 ret = server_get_unix_fd( handle, FILE_APPEND_DATA, &fd, &needs_close, &type, NULL );
5887 if (!ret && (type == FD_TYPE_FILE || type == FD_TYPE_DIR || type == FD_TYPE_CHAR))
5889 if (fsync(fd)) ret = errno_to_status( errno );
5890 io->u.Status = ret;
5891 io->Information = 0;
5893 else if (!ret && type == FD_TYPE_SERIAL)
5895 ret = serial_FlushBuffersFile( fd );
5897 else if (ret != STATUS_ACCESS_DENIED)
5899 struct async_irp *async;
5901 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
5902 return STATUS_NO_MEMORY;
5903 async->buffer = NULL;
5904 async->size = 0;
5906 SERVER_START_REQ( flush )
5908 req->async = server_async( handle, &async->io, NULL, NULL, NULL, io );
5909 ret = wine_server_call( req );
5910 wait_handle = wine_server_ptr_handle( reply->event );
5911 if (wait_handle && ret != STATUS_PENDING)
5913 io->u.Status = ret;
5914 io->Information = 0;
5917 SERVER_END_REQ;
5919 if (ret != STATUS_PENDING) free( async );
5921 if (wait_handle) ret = wait_async( wait_handle, FALSE );
5924 if (needs_close) close( fd );
5925 return ret;
5929 /**************************************************************************
5930 * NtCancelIoFile (NTDLL.@)
5932 NTSTATUS WINAPI NtCancelIoFile( HANDLE handle, IO_STATUS_BLOCK *io_status )
5934 TRACE( "%p %p\n", handle, io_status );
5936 SERVER_START_REQ( cancel_async )
5938 req->handle = wine_server_obj_handle( handle );
5939 req->only_thread = TRUE;
5940 io_status->u.Status = wine_server_call( req );
5942 SERVER_END_REQ;
5943 return io_status->u.Status;
5947 /**************************************************************************
5948 * NtCancelIoFileEx (NTDLL.@)
5950 NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status )
5952 TRACE( "%p %p %p\n", handle, io, io_status );
5954 SERVER_START_REQ( cancel_async )
5956 req->handle = wine_server_obj_handle( handle );
5957 req->iosb = wine_server_client_ptr( io );
5958 io_status->u.Status = wine_server_call( req );
5960 SERVER_END_REQ;
5961 return io_status->u.Status;
5965 /******************************************************************
5966 * NtLockFile (NTDLL.@)
5968 NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void* apc_user,
5969 IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
5970 LARGE_INTEGER *count, ULONG *key, BOOLEAN dont_wait, BOOLEAN exclusive )
5972 static int warn;
5973 NTSTATUS ret;
5974 HANDLE handle;
5975 BOOLEAN async;
5977 if (apc || io_status || key)
5979 FIXME("Unimplemented yet parameter\n");
5980 return STATUS_NOT_IMPLEMENTED;
5982 if (apc_user && !warn++) FIXME("I/O completion on lock not implemented yet\n");
5984 for (;;)
5986 SERVER_START_REQ( lock_file )
5988 req->handle = wine_server_obj_handle( file );
5989 req->offset = offset->QuadPart;
5990 req->count = count->QuadPart;
5991 req->shared = !exclusive;
5992 req->wait = !dont_wait;
5993 ret = wine_server_call( req );
5994 handle = wine_server_ptr_handle( reply->handle );
5995 async = reply->overlapped;
5997 SERVER_END_REQ;
5998 if (ret != STATUS_PENDING)
6000 if (!ret && event) NtSetEvent( event, NULL );
6001 return ret;
6003 if (async)
6005 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6006 if (handle) NtClose( handle );
6007 return STATUS_PENDING;
6009 if (handle)
6011 NtWaitForSingleObject( handle, FALSE, NULL );
6012 NtClose( handle );
6014 else /* Unix lock conflict, sleep a bit and retry */
6016 LARGE_INTEGER time;
6017 time.QuadPart = -100 * (ULONGLONG)10000;
6018 NtDelayExecution( FALSE, &time );
6024 /******************************************************************
6025 * NtUnlockFile (NTDLL.@)
6027 NTSTATUS WINAPI NtUnlockFile( HANDLE handle, IO_STATUS_BLOCK *io_status, LARGE_INTEGER *offset,
6028 LARGE_INTEGER *count, ULONG *key )
6030 NTSTATUS status;
6032 TRACE( "%p %x%08x %x%08x\n",
6033 handle, offset->u.HighPart, offset->u.LowPart, count->u.HighPart, count->u.LowPart );
6035 if (io_status || key)
6037 FIXME("Unimplemented yet parameter\n");
6038 return STATUS_NOT_IMPLEMENTED;
6041 SERVER_START_REQ( unlock_file )
6043 req->handle = wine_server_obj_handle( handle );
6044 req->offset = offset->QuadPart;
6045 req->count = count->QuadPart;
6046 status = wine_server_call( req );
6048 SERVER_END_REQ;
6049 return status;
6053 static NTSTATUS read_changes_apc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status )
6055 struct async_fileio_read_changes *fileio = user;
6056 int size = 0;
6058 if (status == STATUS_ALERTED)
6060 SERVER_START_REQ( read_change )
6062 req->handle = wine_server_obj_handle( fileio->io.handle );
6063 wine_server_set_reply( req, fileio->data, fileio->data_size );
6064 status = wine_server_call( req );
6065 size = wine_server_reply_size( reply );
6067 SERVER_END_REQ;
6069 if (status == STATUS_SUCCESS && fileio->buffer)
6071 FILE_NOTIFY_INFORMATION *pfni = fileio->buffer;
6072 int i, left = fileio->buffer_size;
6073 DWORD *last_entry_offset = NULL;
6074 struct filesystem_event *event = (struct filesystem_event*)fileio->data;
6076 while (size && left >= sizeof(*pfni))
6078 DWORD len = (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR);
6080 /* convert to an NT style path */
6081 for (i = 0; i < event->len; i++)
6082 if (event->name[i] == '/') event->name[i] = '\\';
6084 pfni->Action = event->action;
6085 pfni->FileNameLength = ntdll_umbstowcs( event->name, event->len, pfni->FileName, len );
6086 last_entry_offset = &pfni->NextEntryOffset;
6088 if (pfni->FileNameLength == len) break;
6090 i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
6091 pfni->FileNameLength *= sizeof(WCHAR);
6092 pfni->NextEntryOffset = i;
6093 pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
6094 left -= i;
6096 i = (offsetof(struct filesystem_event, name[event->len])
6097 + sizeof(int)-1) / sizeof(int) * sizeof(int);
6098 event = (struct filesystem_event*)((char*)event + i);
6099 size -= i;
6102 if (size)
6104 status = STATUS_NOTIFY_ENUM_DIR;
6105 size = 0;
6107 else
6109 if (last_entry_offset) *last_entry_offset = 0;
6110 size = fileio->buffer_size - left;
6113 else
6115 status = STATUS_NOTIFY_ENUM_DIR;
6116 size = 0;
6120 if (status != STATUS_PENDING)
6122 iosb->u.Status = status;
6123 iosb->Information = size;
6124 release_fileio( &fileio->io );
6126 return status;
6129 #define FILE_NOTIFY_ALL ( \
6130 FILE_NOTIFY_CHANGE_FILE_NAME | \
6131 FILE_NOTIFY_CHANGE_DIR_NAME | \
6132 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
6133 FILE_NOTIFY_CHANGE_SIZE | \
6134 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6135 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6136 FILE_NOTIFY_CHANGE_CREATION | \
6137 FILE_NOTIFY_CHANGE_SECURITY )
6139 /******************************************************************************
6140 * NtNotifyChangeDirectoryFile (NTDLL.@)
6142 NTSTATUS WINAPI NtNotifyChangeDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
6143 void *apc_context, IO_STATUS_BLOCK *iosb, void *buffer,
6144 ULONG buffer_size, ULONG filter, BOOLEAN subtree )
6146 struct async_fileio_read_changes *fileio;
6147 NTSTATUS status;
6148 ULONG size = max( 4096, buffer_size );
6150 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6151 handle, event, apc, apc_context, iosb, buffer, buffer_size, filter, subtree );
6153 if (!iosb) return STATUS_ACCESS_VIOLATION;
6154 if (filter == 0 || (filter & ~FILE_NOTIFY_ALL)) return STATUS_INVALID_PARAMETER;
6156 fileio = (struct async_fileio_read_changes *)alloc_fileio(
6157 offsetof(struct async_fileio_read_changes, data[size]), read_changes_apc, handle );
6158 if (!fileio) return STATUS_NO_MEMORY;
6160 fileio->buffer = buffer;
6161 fileio->buffer_size = buffer_size;
6162 fileio->data_size = size;
6164 SERVER_START_REQ( read_directory_changes )
6166 req->filter = filter;
6167 req->want_data = (buffer != NULL);
6168 req->subtree = subtree;
6169 req->async = server_async( handle, &fileio->io, event, apc, apc_context, iosb );
6170 status = wine_server_call( req );
6172 SERVER_END_REQ;
6174 if (status != STATUS_PENDING) free( fileio );
6175 return status;
6179 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6180 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6181 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION *info, const char *fstypename,
6182 unsigned int flags )
6184 if (!strcmp("cd9660", fstypename) || !strcmp("udf", fstypename))
6186 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6187 /* Don't assume read-only, let the mount options set it below */
6188 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6190 else if (!strcmp("nfs", fstypename) || !strcmp("nwfs", fstypename) ||
6191 !strcmp("smbfs", fstypename) || !strcmp("afpfs", fstypename))
6193 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6194 info->Characteristics |= FILE_REMOTE_DEVICE;
6196 else if (!strcmp("procfs", fstypename))
6197 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6198 else
6199 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6201 if (flags & MNT_RDONLY)
6202 info->Characteristics |= FILE_READ_ONLY_DEVICE;
6204 if (!(flags & MNT_LOCAL))
6206 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6207 info->Characteristics |= FILE_REMOTE_DEVICE;
6210 #endif
6212 static inline BOOL is_device_placeholder( int fd )
6214 static const char wine_placeholder[] = "Wine device placeholder";
6215 char buffer[sizeof(wine_placeholder)-1];
6217 if (pread( fd, buffer, sizeof(wine_placeholder) - 1, 0 ) != sizeof(wine_placeholder) - 1)
6218 return FALSE;
6219 return !memcmp( buffer, wine_placeholder, sizeof(wine_placeholder) - 1 );
6222 static NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info )
6224 struct stat st;
6226 info->Characteristics = 0;
6227 if (fstat( fd, &st ) < 0) return errno_to_status( errno );
6228 if (S_ISCHR( st.st_mode ))
6230 info->DeviceType = FILE_DEVICE_UNKNOWN;
6231 #ifdef linux
6232 switch(major(st.st_rdev))
6234 case MEM_MAJOR:
6235 info->DeviceType = FILE_DEVICE_NULL;
6236 break;
6237 case TTY_MAJOR:
6238 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6239 break;
6240 case LP_MAJOR:
6241 info->DeviceType = FILE_DEVICE_PARALLEL_PORT;
6242 break;
6243 case SCSI_TAPE_MAJOR:
6244 info->DeviceType = FILE_DEVICE_TAPE;
6245 break;
6247 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6249 int d_type;
6250 if (ioctl(fd, FIODTYPE, &d_type) == 0)
6252 switch(d_type)
6254 case D_TAPE:
6255 info->DeviceType = FILE_DEVICE_TAPE;
6256 break;
6257 case D_DISK:
6258 info->DeviceType = FILE_DEVICE_DISK;
6259 break;
6260 case D_TTY:
6261 info->DeviceType = FILE_DEVICE_SERIAL_PORT;
6262 break;
6263 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6264 case D_MEM:
6265 info->DeviceType = FILE_DEVICE_NULL;
6266 break;
6267 #endif
6269 /* no special d_type for parallel ports */
6272 #endif
6274 else if (S_ISBLK( st.st_mode ))
6276 info->DeviceType = FILE_DEVICE_DISK;
6278 else if (S_ISFIFO( st.st_mode ) || S_ISSOCK( st.st_mode ))
6280 info->DeviceType = FILE_DEVICE_NAMED_PIPE;
6282 else if (is_device_placeholder( fd ))
6284 info->DeviceType = FILE_DEVICE_DISK;
6286 else /* regular file or directory */
6288 #if defined(linux) && defined(HAVE_FSTATFS)
6289 struct statfs stfs;
6291 /* check for floppy disk */
6292 if (major(st.st_dev) == FLOPPY_MAJOR)
6293 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6295 if (fstatfs( fd, &stfs ) < 0) stfs.f_type = 0;
6296 switch (stfs.f_type)
6298 case 0x9660: /* iso9660 */
6299 case 0x9fa1: /* supermount */
6300 case 0x15013346: /* udf */
6301 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6302 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6303 break;
6304 case 0x6969: /* nfs */
6305 case 0xff534d42: /* cifs */
6306 case 0xfe534d42: /* smb2 */
6307 case 0x517b: /* smbfs */
6308 case 0x564c: /* ncpfs */
6309 info->DeviceType = FILE_DEVICE_NETWORK_FILE_SYSTEM;
6310 info->Characteristics |= FILE_REMOTE_DEVICE;
6311 break;
6312 case 0x1373: /* devfs */
6313 case 0x9fa0: /* procfs */
6314 info->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
6315 break;
6316 case 0x01021994: /* tmpfs */
6317 case 0x28cd3d45: /* cramfs */
6318 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6319 * filesystems are rare on Windows, and some programs refuse to
6320 * recognize them as valid. */
6321 default:
6322 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6323 break;
6325 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6326 struct statfs stfs;
6328 if (fstatfs( fd, &stfs ) < 0)
6329 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6330 else
6331 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flags );
6332 #elif defined(__NetBSD__)
6333 struct statvfs stfs;
6335 if (fstatvfs( fd, &stfs) < 0)
6336 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6337 else
6338 get_device_info_fstatfs( info, stfs.f_fstypename, stfs.f_flag );
6339 #elif defined(sun)
6340 /* Use dkio to work out device types */
6342 # include <sys/dkio.h>
6343 # include <sys/vtoc.h>
6344 struct dk_cinfo dkinf;
6345 int retval = ioctl(fd, DKIOCINFO, &dkinf);
6346 if(retval==-1){
6347 WARN("Unable to get disk device type information - assuming a disk like device\n");
6348 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6350 switch (dkinf.dki_ctype)
6352 case DKC_CDROM:
6353 info->DeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
6354 info->Characteristics |= FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE;
6355 break;
6356 case DKC_NCRFLOPPY:
6357 case DKC_SMSFLOPPY:
6358 case DKC_INTEL82072:
6359 case DKC_INTEL82077:
6360 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6361 info->Characteristics |= FILE_REMOVABLE_MEDIA;
6362 break;
6363 case DKC_MD:
6364 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6365 * filesystems are rare on Windows, and some programs refuse to
6366 * recognize them as valid. */
6367 default:
6368 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6371 #else
6372 static int warned;
6373 if (!warned++) FIXME( "device info not properly supported on this platform\n" );
6374 info->DeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
6375 #endif
6376 info->Characteristics |= FILE_DEVICE_IS_MOUNTED;
6378 return STATUS_SUCCESS;
6382 /******************************************************************************
6383 * NtQueryVolumeInformationFile (NTDLL.@)
6385 NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
6386 void *buffer, ULONG length,
6387 FS_INFORMATION_CLASS info_class )
6389 int fd, needs_close;
6390 struct stat st;
6392 io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL );
6393 if (io->u.Status == STATUS_BAD_DEVICE_TYPE)
6395 struct async_irp *async;
6396 HANDLE wait_handle;
6397 NTSTATUS status;
6399 if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle )))
6400 return STATUS_NO_MEMORY;
6401 async->buffer = buffer;
6402 async->size = length;
6404 SERVER_START_REQ( get_volume_info )
6406 req->async = server_async( handle, &async->io, NULL, NULL, NULL, io );
6407 req->handle = wine_server_obj_handle( handle );
6408 req->info_class = info_class;
6409 wine_server_set_reply( req, buffer, length );
6410 status = wine_server_call( req );
6411 if (status != STATUS_PENDING)
6413 io->u.Status = status;
6414 io->Information = wine_server_reply_size( reply );
6416 wait_handle = wine_server_ptr_handle( reply->wait );
6418 SERVER_END_REQ;
6419 if (status != STATUS_PENDING) free( async );
6420 if (wait_handle) status = wait_async( wait_handle, FALSE );
6421 return status;
6423 else if (io->u.Status) return io->u.Status;
6425 io->u.Status = STATUS_NOT_IMPLEMENTED;
6426 io->Information = 0;
6428 switch( info_class )
6430 case FileFsLabelInformation:
6431 FIXME( "%p: label info not supported\n", handle );
6432 break;
6434 case FileFsSizeInformation:
6435 if (length < sizeof(FILE_FS_SIZE_INFORMATION))
6436 io->u.Status = STATUS_BUFFER_TOO_SMALL;
6437 else
6439 FILE_FS_SIZE_INFORMATION *info = buffer;
6441 if (fstat( fd, &st ) < 0)
6443 io->u.Status = errno_to_status( errno );
6444 break;
6446 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
6448 io->u.Status = STATUS_INVALID_DEVICE_REQUEST;
6450 else
6452 ULONGLONG bsize;
6453 /* Linux's fstatvfs is buggy */
6454 #if !defined(linux) || !defined(HAVE_FSTATFS)
6455 struct statvfs stfs;
6457 if (fstatvfs( fd, &stfs ) < 0)
6459 io->u.Status = errno_to_status( errno );
6460 break;
6462 bsize = stfs.f_frsize;
6463 #else
6464 struct statfs stfs;
6465 if (fstatfs( fd, &stfs ) < 0)
6467 io->u.Status = errno_to_status( errno );
6468 break;
6470 bsize = stfs.f_bsize;
6471 #endif
6472 if (bsize == 2048) /* assume CD-ROM */
6474 info->BytesPerSector = 2048;
6475 info->SectorsPerAllocationUnit = 1;
6477 else
6479 info->BytesPerSector = 512;
6480 info->SectorsPerAllocationUnit = 8;
6482 info->TotalAllocationUnits.QuadPart = bsize * stfs.f_blocks / (info->BytesPerSector * info->SectorsPerAllocationUnit);
6483 info->AvailableAllocationUnits.QuadPart = bsize * stfs.f_bavail / (info->BytesPerSector * info->SectorsPerAllocationUnit);
6484 io->Information = sizeof(*info);
6485 io->u.Status = STATUS_SUCCESS;
6488 break;
6490 case FileFsDeviceInformation:
6491 if (length < sizeof(FILE_FS_DEVICE_INFORMATION))
6492 io->u.Status = STATUS_BUFFER_TOO_SMALL;
6493 else
6495 FILE_FS_DEVICE_INFORMATION *info = buffer;
6497 if ((io->u.Status = get_device_info( fd, info )) == STATUS_SUCCESS)
6498 io->Information = sizeof(*info);
6500 break;
6502 case FileFsAttributeInformation:
6504 static const WCHAR fatW[] = {'F','A','T'};
6505 static const WCHAR fat32W[] = {'F','A','T','3','2'};
6506 static const WCHAR ntfsW[] = {'N','T','F','S'};
6507 static const WCHAR cdfsW[] = {'C','D','F','S'};
6508 static const WCHAR udfW[] = {'U','D','F'};
6510 FILE_FS_ATTRIBUTE_INFORMATION *info = buffer;
6511 struct mountmgr_unix_drive drive;
6512 enum mountmgr_fs_type fs_type = MOUNTMGR_FS_TYPE_NTFS;
6514 if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
6516 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
6517 break;
6520 if (!get_mountmgr_fs_info( handle, fd, &drive, sizeof(drive) )) fs_type = drive.fs_type;
6521 else
6523 struct statfs stfs;
6525 if (!fstatfs( fd, &stfs ))
6527 #if defined(linux) && defined(HAVE_FSTATFS)
6528 switch (stfs.f_type)
6530 case 0x9660:
6531 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6532 break;
6533 case 0x15013346:
6534 fs_type = MOUNTMGR_FS_TYPE_UDF;
6535 break;
6536 case 0x4d44:
6537 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6538 break;
6540 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6541 if (!strcmp( stfs.f_fstypename, "cd9660" ))
6542 fs_type = MOUNTMGR_FS_TYPE_ISO9660;
6543 else if (!strcmp( stfs.f_fstypename, "udf" ))
6544 fs_type = MOUNTMGR_FS_TYPE_UDF;
6545 else if (!strcmp( stfs.f_fstypename, "msdos" )) /* FreeBSD < 5, Apple */
6546 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6547 else if (!strcmp( stfs.f_fstypename, "msdosfs" )) /* FreeBSD >= 5 */
6548 fs_type = MOUNTMGR_FS_TYPE_FAT32;
6549 #endif
6553 switch (fs_type)
6555 case MOUNTMGR_FS_TYPE_ISO9660:
6556 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
6557 info->MaximumComponentNameLength = 221;
6558 info->FileSystemNameLength = min( sizeof(cdfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6559 memcpy(info->FileSystemName, cdfsW, info->FileSystemNameLength);
6560 break;
6561 case MOUNTMGR_FS_TYPE_UDF:
6562 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
6563 info->MaximumComponentNameLength = 255;
6564 info->FileSystemNameLength = min( sizeof(udfW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6565 memcpy(info->FileSystemName, udfW, info->FileSystemNameLength);
6566 break;
6567 case MOUNTMGR_FS_TYPE_FAT:
6568 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6569 info->MaximumComponentNameLength = 255;
6570 info->FileSystemNameLength = min( sizeof(fatW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6571 memcpy(info->FileSystemName, fatW, info->FileSystemNameLength);
6572 break;
6573 case MOUNTMGR_FS_TYPE_FAT32:
6574 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
6575 info->MaximumComponentNameLength = 255;
6576 info->FileSystemNameLength = min( sizeof(fat32W), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6577 memcpy(info->FileSystemName, fat32W, info->FileSystemNameLength);
6578 break;
6579 default:
6580 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
6581 info->MaximumComponentNameLength = 255;
6582 info->FileSystemNameLength = min( sizeof(ntfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
6583 memcpy(info->FileSystemName, ntfsW, info->FileSystemNameLength);
6584 break;
6587 io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
6588 io->u.Status = STATUS_SUCCESS;
6589 break;
6592 case FileFsVolumeInformation:
6594 FILE_FS_VOLUME_INFORMATION *info = buffer;
6595 ULONGLONG data[64];
6596 struct mountmgr_unix_drive *drive = (struct mountmgr_unix_drive *)data;
6597 const WCHAR *label;
6599 if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
6601 io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
6602 break;
6605 if (get_mountmgr_fs_info( handle, fd, drive, sizeof(data) ))
6607 io->u.Status = STATUS_NOT_IMPLEMENTED;
6608 break;
6611 label = (WCHAR *)((char *)drive + drive->label_offset);
6612 info->VolumeCreationTime.QuadPart = 0; /* FIXME */
6613 info->VolumeSerialNumber = drive->serial;
6614 info->VolumeLabelLength = min( wcslen( label ) * sizeof(WCHAR),
6615 length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
6616 info->SupportsObjects = (drive->fs_type == MOUNTMGR_FS_TYPE_NTFS);
6617 memcpy( info->VolumeLabel, label, info->VolumeLabelLength );
6618 io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
6619 io->u.Status = STATUS_SUCCESS;
6620 break;
6623 case FileFsControlInformation:
6624 FIXME( "%p: control info not supported\n", handle );
6625 break;
6627 case FileFsFullSizeInformation:
6628 FIXME( "%p: full size info not supported\n", handle );
6629 break;
6631 case FileFsObjectIdInformation:
6632 FIXME( "%p: object id info not supported\n", handle );
6633 break;
6635 case FileFsMaximumInformation:
6636 FIXME( "%p: maximum info not supported\n", handle );
6637 break;
6639 default:
6640 io->u.Status = STATUS_INVALID_PARAMETER;
6641 break;
6643 if (needs_close) close( fd );
6644 return io->u.Status;
6648 /******************************************************************************
6649 * NtSetVolumeInformationFile (NTDLL.@)
6651 NTSTATUS WINAPI NtSetVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, void *info,
6652 ULONG length, FS_INFORMATION_CLASS class )
6654 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle, io, info, length, class );
6655 return STATUS_SUCCESS;
6659 /******************************************************************
6660 * NtQueryEaFile (NTDLL.@)
6662 NTSTATUS WINAPI NtQueryEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
6663 BOOLEAN single_entry, void *list, ULONG list_len,
6664 ULONG *index, BOOLEAN restart )
6666 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) stub\n",
6667 handle, io, buffer, length, single_entry, list, list_len, index, restart );
6668 return STATUS_ACCESS_DENIED;
6672 /******************************************************************
6673 * NtSetEaFile (NTDLL.@)
6675 NTSTATUS WINAPI NtSetEaFile( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length )
6677 FIXME( "(%p,%p,%p,%d) stub\n", handle, io, buffer, length );
6678 return STATUS_ACCESS_DENIED;
6682 /* convert type information from server format; helper for NtQueryObject */
6683 static void *put_object_type_info( OBJECT_TYPE_INFORMATION *p, struct object_type_info *info )
6685 const ULONG align = sizeof(DWORD_PTR) - 1;
6687 memset( p, 0, sizeof(*p) );
6688 p->TypeName.Buffer = (WCHAR *)(p + 1);
6689 p->TypeName.Length = info->name_len;
6690 p->TypeName.MaximumLength = info->name_len + sizeof(WCHAR);
6691 p->TotalNumberOfObjects = info->obj_count;
6692 p->TotalNumberOfHandles = info->handle_count;
6693 p->HighWaterNumberOfObjects = info->obj_max;
6694 p->HighWaterNumberOfHandles = info->handle_max;
6695 p->TypeIndex = info->index + 2;
6696 p->GenericMapping.GenericRead = info->mapping.read;
6697 p->GenericMapping.GenericWrite = info->mapping.write;
6698 p->GenericMapping.GenericExecute = info->mapping.exec;
6699 p->GenericMapping.GenericAll = info->mapping.all;
6700 p->ValidAccessMask = info->valid_access;
6701 memcpy( p->TypeName.Buffer, info + 1, info->name_len );
6702 p->TypeName.Buffer[info->name_len / sizeof(WCHAR)] = 0;
6703 return (char *)(p + 1) + ((p->TypeName.MaximumLength + align) & ~align);
6706 /**************************************************************************
6707 * NtQueryObject (NTDLL.@)
6709 NTSTATUS WINAPI NtQueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
6710 void *ptr, ULONG len, ULONG *used_len )
6712 NTSTATUS status;
6714 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle, info_class, ptr, len, used_len);
6716 if (used_len) *used_len = 0;
6718 switch (info_class)
6720 case ObjectBasicInformation:
6722 OBJECT_BASIC_INFORMATION *p = ptr;
6724 if (len < sizeof(*p)) return STATUS_INFO_LENGTH_MISMATCH;
6726 SERVER_START_REQ( get_object_info )
6728 req->handle = wine_server_obj_handle( handle );
6729 status = wine_server_call( req );
6730 if (status == STATUS_SUCCESS)
6732 memset( p, 0, sizeof(*p) );
6733 p->GrantedAccess = reply->access;
6734 p->PointerCount = reply->ref_count;
6735 p->HandleCount = reply->handle_count;
6736 if (used_len) *used_len = sizeof(*p);
6739 SERVER_END_REQ;
6740 break;
6743 case ObjectNameInformation:
6745 OBJECT_NAME_INFORMATION *p = ptr;
6746 char *unix_name;
6747 WCHAR *nt_name;
6749 /* first try as a file object */
6751 if (!(status = server_get_unix_name( handle, &unix_name )))
6753 if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
6755 ULONG size = (wcslen(nt_name) + 1) * sizeof(WCHAR);
6756 if (len < sizeof(*p)) status = STATUS_INFO_LENGTH_MISMATCH;
6757 else if (len < sizeof(*p) + size) status = STATUS_BUFFER_OVERFLOW;
6758 else
6760 p->Name.Buffer = (WCHAR *)(p + 1);
6761 p->Name.Length = size - sizeof(WCHAR);
6762 p->Name.MaximumLength = size;
6763 wcscpy( p->Name.Buffer, nt_name );
6765 if (used_len) *used_len = sizeof(*p) + size;
6766 free( nt_name );
6768 free( unix_name );
6769 break;
6771 else if (status != STATUS_OBJECT_TYPE_MISMATCH) break;
6773 /* not a file, treat as a generic object */
6775 SERVER_START_REQ( get_object_name )
6777 req->handle = wine_server_obj_handle( handle );
6778 if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
6779 status = wine_server_call( req );
6780 if (status == STATUS_SUCCESS)
6782 if (!reply->total) /* no name */
6784 if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
6785 else memset( p, 0, sizeof(*p) );
6786 if (used_len) *used_len = sizeof(*p);
6788 else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
6790 if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
6791 status = STATUS_INFO_LENGTH_MISMATCH;
6793 else
6795 ULONG res = wine_server_reply_size( reply );
6796 p->Name.Buffer = (WCHAR *)(p + 1);
6797 p->Name.Length = res;
6798 p->Name.MaximumLength = res + sizeof(WCHAR);
6799 p->Name.Buffer[res / sizeof(WCHAR)] = 0;
6800 if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
6804 SERVER_END_REQ;
6805 break;
6808 case ObjectTypeInformation:
6810 OBJECT_TYPE_INFORMATION *p = ptr;
6811 char buffer[sizeof(struct object_type_info) + 64];
6812 struct object_type_info *info = (struct object_type_info *)buffer;
6814 SERVER_START_REQ( get_object_type )
6816 req->handle = wine_server_obj_handle( handle );
6817 wine_server_set_reply( req, buffer, sizeof(buffer) );
6818 status = wine_server_call( req );
6820 SERVER_END_REQ;
6821 if (status) break;
6822 if (sizeof(*p) + info->name_len + sizeof(WCHAR) <= len)
6824 put_object_type_info( p, info );
6825 if (used_len) *used_len = sizeof(*p) + p->TypeName.MaximumLength;
6827 else
6829 if (used_len) *used_len = sizeof(*p) + info->name_len + sizeof(WCHAR);
6830 status = STATUS_INFO_LENGTH_MISMATCH;
6832 break;
6835 case ObjectTypesInformation:
6837 OBJECT_TYPES_INFORMATION *types = ptr;
6838 OBJECT_TYPE_INFORMATION *p;
6839 struct object_type_info *buffer;
6840 /* assume at most 32 types, with an average 16-char name */
6841 ULONG size = 32 * (sizeof(struct object_type_info) + 16 * sizeof(WCHAR));
6842 ULONG i, count, pos, total, align = sizeof(DWORD_PTR) - 1;
6844 buffer = malloc( size );
6845 SERVER_START_REQ( get_object_types )
6847 wine_server_set_reply( req, buffer, size );
6848 status = wine_server_call( req );
6849 count = reply->count;
6851 SERVER_END_REQ;
6852 if (!status)
6854 if (len >= sizeof(*types)) types->NumberOfTypes = count;
6855 total = (sizeof(*types) + align) & ~align;
6856 p = (OBJECT_TYPE_INFORMATION *)((char *)ptr + total);
6857 for (i = pos = 0; i < count; i++)
6859 struct object_type_info *info = (struct object_type_info *)((char *)buffer + pos);
6860 pos += sizeof(*info) + ((info->name_len + 3) & ~3);
6861 total += sizeof(*p) + ((info->name_len + sizeof(WCHAR) + align) & ~align);
6862 if (total <= len) p = put_object_type_info( p, info );
6864 if (used_len) *used_len = total;
6865 if (total > len) status = STATUS_INFO_LENGTH_MISMATCH;
6867 else if (status == STATUS_BUFFER_OVERFLOW) FIXME( "size %u too small\n", size );
6869 free( buffer );
6870 break;
6873 case ObjectDataInformation:
6875 OBJECT_DATA_INFORMATION* p = ptr;
6877 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6879 SERVER_START_REQ( set_handle_info )
6881 req->handle = wine_server_obj_handle( handle );
6882 req->flags = 0;
6883 req->mask = 0;
6884 status = wine_server_call( req );
6885 if (status == STATUS_SUCCESS)
6887 p->InheritHandle = (reply->old_flags & HANDLE_FLAG_INHERIT) != 0;
6888 p->ProtectFromClose = (reply->old_flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0;
6889 if (used_len) *used_len = sizeof(*p);
6892 SERVER_END_REQ;
6893 break;
6896 default:
6897 FIXME("Unsupported information class %u\n", info_class);
6898 status = STATUS_NOT_IMPLEMENTED;
6899 break;
6901 return status;
6905 /**************************************************************************
6906 * NtSetInformationObject (NTDLL.@)
6908 NTSTATUS WINAPI NtSetInformationObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_class,
6909 void *ptr, ULONG len )
6911 NTSTATUS status;
6913 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle, info_class, ptr, len);
6915 switch (info_class)
6917 case ObjectDataInformation:
6919 OBJECT_DATA_INFORMATION* p = ptr;
6921 if (len < sizeof(*p)) return STATUS_INVALID_BUFFER_SIZE;
6923 SERVER_START_REQ( set_handle_info )
6925 req->handle = wine_server_obj_handle( handle );
6926 req->mask = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
6927 if (p->InheritHandle) req->flags |= HANDLE_FLAG_INHERIT;
6928 if (p->ProtectFromClose) req->flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
6929 status = wine_server_call( req );
6931 SERVER_END_REQ;
6932 break;
6935 default:
6936 FIXME("Unsupported information class %u\n", info_class);
6937 status = STATUS_NOT_IMPLEMENTED;
6938 break;
6940 return status;