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
28 #include "wine/port.h"
31 #include <sys/types.h>
46 #ifdef HAVE_SYS_STAT_H
47 # include <sys/stat.h>
49 #ifdef HAVE_SYS_STATVFS_H
50 # include <sys/statvfs.h>
52 #ifdef HAVE_SYS_SYSCALL_H
53 # include <sys/syscall.h>
55 #ifdef HAVE_SYS_SOCKET_H
56 #include <sys/socket.h>
58 #ifdef HAVE_SYS_TIME_H
59 # include <sys/time.h>
61 #ifdef HAVE_SYS_ATTR_H
65 # include <sys/mkdev.h>
66 #elif defined(MAJOR_IN_SYSMACROS)
67 # include <sys/sysmacros.h>
69 #ifdef HAVE_SYS_VNODE_H
71 # include <stdint.h> /* needed for kfreebsd */
73 /* Work around a conflict with Solaris' system list defined in sys/list.h. */
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>
90 #ifdef HAVE_SYS_IOCTL_H
91 #include <sys/ioctl.h>
93 #ifdef HAVE_LINUX_IOCTL_H
94 #include <linux/ioctl.h>
96 #ifdef HAVE_LINUX_MAJOR_H
97 # include <linux/major.h>
99 #ifdef HAVE_SYS_PARAM_H
100 #include <sys/param.h>
102 #ifdef HAVE_SYS_CONF_H
103 #include <sys/conf.h>
105 #ifdef HAVE_SYS_MOUNT_H
106 #include <sys/mount.h>
108 #ifdef HAVE_SYS_STATFS_H
109 #include <sys/statfs.h>
116 #include "ntstatus.h"
117 #define WIN32_NO_STATUS
118 #define NONAMELESSUNION
121 #include "winioctl.h"
122 #include "winternl.h"
123 #include "ddk/ntddk.h"
124 #include "ddk/ntddser.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
148 /* We want the real kernel dirent structure, not the libc one */
153 unsigned short d_reclen
;
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
167 # define O_DIRECTORY 0200000 /* must be directory */
170 #ifndef AT_NO_AUTOMOUNT
171 #define AT_NO_AUTOMOUNT 0x800
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
191 static struct file_identity ignored_files
[MAX_IGNORED_FILES
];
192 static unsigned int ignored_files_count
;
194 union file_directory_info
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 */
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 */
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
);
261 /* disks are char devices on *BSD */
262 return S_ISCHR( st
->st_mode
);
266 static inline void ignore_file( const char *name
)
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
)
287 for (i
= 0; i
< ignored_files_count
; i
++)
288 if (is_same_file( &ignored_files
[i
], st
)) return TRUE
;
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
)
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
] );
321 static inline BOOL
has_wildcard( const UNICODE_STRING
*mask
)
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
;
331 NTSTATUS
errno_to_status( int err
)
333 TRACE( "errno = %d\n", 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
;
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
;
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
;
353 case ENOMEDIUM
: return STATUS_NO_MEDIA_IN_DEVICE
;
355 case ENXIO
: return STATUS_NO_SUCH_DEVICE
;
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
;
365 case ENOEXEC
: /* ?? */
366 case EEXIST
: /* ?? */
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
;
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
;
385 buffer
->size
= new_size
;
386 buffer
->next
= data
->buffer
;
387 data
->buffer
= buffer
;
389 ret
= buffer
->data
+ buffer
->pos
;
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
);
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
);
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
;
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
;
439 /* free the complete directory data structure */
440 static void free_dir_data( struct dir_data
*data
)
442 struct dir_data_buffer
*buffer
, *next
;
446 for (buffer
= data
->buffer
; buffer
; buffer
= next
)
456 /* support for a directory queue for filesystem searches */
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
);
481 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
482 strcpy( name
, dir
->name
);
483 list_remove( &dir
->entry
);
485 return STATUS_SUCCESS
;
487 return STATUS_OBJECT_NAME_NOT_FOUND
;
490 static void flush_dir_queue(void)
494 while ((head
= list_head( &dir_queue
)))
496 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
497 list_remove( &dir
->entry
);
505 static char *unescape_field( char *str
)
509 for (in
= out
= str
; *in
; in
++, out
++)
519 else if (in
[1] == '0' && in
[2] == '4' && in
[3] == '0')
524 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '1')
529 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '2')
534 else if (in
[1] == '1' && in
[2] == '3' && in
[3] == '4')
546 static inline char *get_field( char **str
)
550 ret
= strsep( str
, " \t" );
551 if (*str
) *str
+= strspn( *str
, " \t" );
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];
570 if (!fgets( buf
, sizeof(buf
), f
)) return NULL
;
571 p
= strchr( buf
, '\n' );
573 else /* Partially unread line, move file ptr to end */
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;
602 #define getmntent getmntent_replacement
605 /***********************************************************************
606 * parse_mount_entries
608 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
612 #include <sys/vfstab.h>
613 static char *parse_vfstab_entries( FILE *f
, dev_t dev
, ino_t ino
)
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, ',' );
638 return entry
.vfs_special
;
645 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
647 struct mntent
*entry
;
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, ',' );
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, ',' );
681 return entry
->mnt_fsname
;
687 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
689 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
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
;
710 #include <sys/mnttab.h>
711 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
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, ',' );
737 return entry
.mnt_special
;
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
)
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
);
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
);
771 if ((f
= fopen( "/proc/mounts", "r" )))
773 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
777 if ((f
= fopen( "/etc/mtab", "r" )))
779 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
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
);
789 if (device
) ret
= strdup( device
);
790 mutex_unlock( &mnt_mutex
);
792 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
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
);
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
);
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
);
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
);
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
);
845 if (device
) ret
= strdup( device
);
846 mutex_unlock( &mnt_mutex
);
848 #elif defined(__APPLE__)
849 struct statfs
*mntStat
;
855 static const char path_bsd_device
[] = "/dev/disk";
858 res
= stat( root
, &st
);
859 if (res
== -1) return NULL
;
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 */ );
880 strcpy(ret
, "/dev/r");
881 strcat(ret
, mntStat
[i
].f_mntfromname
+sizeof("/dev/")-1);
885 mutex_unlock( &mnt_mutex
);
888 if (!warned
++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
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
)
906 mutex_lock( &mnt_mutex
);
909 if ((f
= fopen( "/proc/mounts", "r" )))
911 if ((f
= fopen( "/etc/mtab", "r" )))
914 struct mntent
*entry
;
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=" )))
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=" )))
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
);
953 mutex_unlock( &mnt_mutex
);
954 #elif defined(__APPLE__)
955 struct statfs
*entry
;
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
);
971 mutex_unlock( &mnt_mutex
);
974 if (!warned
++) FIXME( "unmounting devices not supported on this platform\n" );
980 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
981 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
994 BOOLEAN case_sensitive
;
1000 vol_capabilities_attr_t caps
;
1003 /***********************************************************************
1006 * Checks if the specified file system is in the cache.
1008 static struct fs_cache
*look_up_fs_cache( dev_t dev
)
1011 for (i
= 0; i
< ARRAY_SIZE( fs_cache
); i
++)
1012 if (fs_cache
[i
].dev
== dev
)
1017 /***********************************************************************
1020 * Adds the specified file system to the cache.
1022 static void add_fs_cache( dev_t dev
, fsid_t fsid
, BOOLEAN case_sensitive
)
1025 struct fs_cache
*entry
= look_up_fs_cache( dev
);
1026 static int once
= 0;
1029 /* Update the cache */
1031 entry
->case_sensitive
= case_sensitive
;
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
;
1046 /* Cache is out of space, warn */
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
)
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
;
1068 attr
.commonattr
= ATTR_CMN_DEVID
|ATTR_CMN_FSID
;
1069 attr
.volattr
= attr
.dirattr
= attr
.fileattr
= attr
.forkattr
= 0;
1071 if (getattrlist( dir
, &attr
, &get_fsid
, sizeof(get_fsid
), 0 ) != 0 ||
1072 get_fsid
.size
!= sizeof(get_fsid
))
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)
1088 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, TRUE
);
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
))
1099 if ((caps
.caps
.capabilities
[VOL_CAPABILITIES_FORMAT
] &
1100 VOL_CAP_FMT_CASE_SENSITIVE
) != VOL_CAP_FMT_CASE_SENSITIVE
)
1104 /* Update the cache */
1105 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, ret
);
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__)
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 ))
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" ))
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
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" ))
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))
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))
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 ))
1172 #elif defined(__linux__)
1173 BOOLEAN sens
= TRUE
;
1178 if ((fd
= open( dir
, O_RDONLY
| O_NONBLOCK
| O_LARGEFILE
)) == -1)
1181 if (ioctl( fd
, EXT2_IOC_GETFLAGS
, &flags
) != -1 && (flags
& EXT4_CASEFOLD_FL
))
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. */
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
;
1213 return get_dir_case_sensitivity_stat( dir
);
1217 /***********************************************************************
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
)
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
;
1249 unsigned short hash
;
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 */
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) */
1289 for (i
= 3, ext
++; (i
> 0) && ext
< end
; i
--, ext
++)
1290 *dst
++ = is_invalid_dos_char(*ext
) ? '_' : *ext
;
1292 return dst
- buffer
;
1296 /***********************************************************************
1299 * Check a long file name against a mask.
1301 * Tests (done in W95 DOS shell - case insensitive):
1302 * *.txt test1.test.txt *
1304 * *.t??????.t* test1.ta.tornado.txt *
1305 * *tornado* test1.ta.tornado.txt *
1306 * t*t test1.ta.tornado.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
)
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
)
1327 while (mask
< mask_end
&& *mask
== '*') mask
++; /* Skip consecutive '*' */
1328 if (mask
== mask_end
) return TRUE
; /* end of mask is all '*', so match */
1331 /* skip to the next match after the joker(s) */
1332 if (is_case_sensitive
)
1333 while (name
< name_end
&& (*name
!= *mask
)) name
++;
1335 while (name
< name_end
&& (towupper(*name
) != towupper(*mask
))) name
++;
1336 next_to_retry
= name
;
1344 if (is_case_sensitive
) mismatch
= (*mask
!= *name
);
1345 else mismatch
= (towupper(*mask
) != towupper(*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 */
1363 /* this scan sequence was a mismatch, so restart
1364 * 1 char after the first char we checked last time */
1366 name
= next_to_retry
;
1368 else return FALSE
; /* bad luck */
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 };
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
;
1400 if (dot
!= -1) return FALSE
;
1405 if (dot
== -1) return (len
<= 8);
1406 if (dot
> 8) return FALSE
;
1407 return (len
- dot
> 1 && len
- dot
< 5);
1411 /***********************************************************************
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;
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 */
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
)
1459 if (S_ISDIR(st
->st_mode
))
1460 attr
= FILE_ATTRIBUTE_DIRECTORY
;
1462 attr
= FILE_ATTRIBUTE_ARCHIVE
;
1463 if (!(st
->st_mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
)))
1464 attr
|= FILE_ATTRIBUTE_READONLY
;
1469 static BOOL
fd_is_mount_point( int fd
, const struct stat
*st
)
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
)
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
;
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
)
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
);
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__ */
1536 #define UTIME_OMIT ((1 << 30) - 2)
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;
1558 if (!&futimens
) return FALSE
;
1560 if (futimens( fd
, tv
) == -1) *status
= errno_to_status( errno
);
1561 else *status
= STATUS_SUCCESS
;
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];
1577 if (set_file_times_precise( fd
, mtime
, atime
, &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;
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;
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;
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
);
1618 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1619 FIXME( "setting file times not supported\n" );
1620 status
= STATUS_NOT_IMPLEMENTED
;
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;
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;
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;
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;
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;
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 )
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
;
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;
1692 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1693 info
->EndOfFile
.QuadPart
= st
->st_size
;
1694 info
->NumberOfLinks
= st
->st_nlink
;
1698 case FileInternalInformation
:
1700 FILE_INTERNAL_INFORMATION
*info
= ptr
;
1701 info
->IndexNumber
.QuadPart
= st
->st_ino
;
1704 case FileEndOfFileInformation
:
1706 FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
1707 info
->EndOfFile
.QuadPart
= S_ISDIR(st
->st_mode
) ? 0 : st
->st_size
;
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
);
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;
1734 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1735 info
->EndOfFile
.QuadPart
= st
->st_size
;
1737 info
->FileAttributes
= attr
;
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
);
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
);
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
);
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;
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
;
1795 if (ret
!= STATUS_BUFFER_OVERFLOW
) break;
1800 static NTSTATUS
fill_name_info( const char *unix_name
, FILE_NAME_INFORMATION
*info
, LONG
*name_len
)
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
);
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
);
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
;
1855 time_t now
= time(NULL
);
1857 mutex_lock( &cache_mutex
);
1858 if (now
!= last_update
)
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
++)
1873 if (!stat( buffer
, &st
))
1875 cache
[i
].dev
= st
.st_dev
;
1876 cache
[i
].ino
= st
.st_ino
;
1889 memcpy( info
, cache
, sizeof(cache
) );
1891 mutex_unlock( &cache_mutex
);
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
);
1904 struct file_identity info
[MAX_DOS_DRIVES
];
1907 if (!get_drives_info( info
)) return -1;
1909 if (stat( path
, &st
) < 0) return -1;
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
);
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
));
1937 if (len
<= 1) break; /* reached root */
1938 while (len
> 1 && path
[len
- 1] != '/') len
--;
1939 while (len
> 1 && path
[len
- 1] == '/') len
--;
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
;
1956 if ((status
= server_get_unix_name( handle
, &unix_name
))) return status
;
1957 letter
= find_dos_device( unix_name
);
1960 memset( drive
, 0, sizeof(*drive
) );
1966 drive
->unix_dev
= st
.st_rdev
? st
.st_rdev
: st
.st_dev
;
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
);
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
;
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 );
2037 case FileDirectoryInformation
:
2038 info
->dir
.FileNameLength
= name_len
;
2041 case FileFullDirectoryInformation
:
2042 info
->full
.EaSize
= 0; /* FIXME */
2043 info
->full
.FileNameLength
= name_len
;
2046 case FileIdFullDirectoryInformation
:
2047 info
->id_full
.EaSize
= 0; /* FIXME */
2048 info
->id_full
.FileNameLength
= name_len
;
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
;
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
;
2065 case FileIdGlobalTxDirectoryInformation
:
2066 info
->id_tx
.TxInfoFlags
= 0;
2067 info
->id_tx
.FileNameLength
= name_len
;
2070 case FileNamesInformation
:
2071 info
->names
.FileNameLength
= name_len
;
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
;
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
;
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
;
2126 long_name
= de
[0].d_name
;
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
;
2135 lseek( fd
, old_pos
, SEEK_SET
);
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"
2155 struct attrreference name_reference
;
2157 char name
[NAME_MAX
* 3 + 1];
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
)
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
)
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
)
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
;
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
)
2244 #ifdef VFAT_IOCTL_READDIR_BOTH
2245 if (!(status
= read_directory_data_vfat( data
, fd
, mask
))) return status
;
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
);
2257 #ifdef HAVE_GETATTRLIST
2258 if (!(status
= read_directory_data_getattrlist( data
, unix_name
))) return status
;
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
);
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
;
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
);
2299 /* sort filenames, but not "." and ".." */
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
);
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
) );
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
)
2330 int entry
= -1, free_entries
[16];
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
;
2353 if (status
== STATUS_SHARING_VIOLATION
) FIXME( "shared directory handle not supported yet\n" );
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
];
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
;
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
),
2393 if (event
|| apc_routine
)
2395 FIXME( "Unsupported yet option\n" );
2396 return STATUS_NOT_IMPLEMENTED
;
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
;
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
;
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
)))
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
);
2470 /***********************************************************************
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
;
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] == '~';
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
);
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
);
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
);
2543 return STATUS_SUCCESS
;
2545 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) == -1)
2551 /* if that did not work, restore previous state of unix_name */
2552 unix_name
[pos
- 1] = 0;
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
);
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
);
2583 return STATUS_SUCCESS
;
2590 unix_name
[pos
- 1] = 0;
2591 return STATUS_OBJECT_PATH_NOT_FOUND
;
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
[] =
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;
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
);
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
) );
2650 memcpy( p
, replace
, replace_len
* sizeof(WCHAR
) );
2652 name
+= prefix_len
+ match_len
;
2653 len
-= prefix_len
+ match_len
;
2654 memcpy( p
, name
, len
* sizeof(WCHAR
) );
2656 attr
->ObjectName
= str
;
2660 /***********************************************************************
2663 static void init_redirects(void)
2665 static const char system_dir
[] = "/dosdevices/c:/windows/system32";
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
) );
2688 /***********************************************************************
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
;
2707 int fd
, needs_close
;
2710 if (server_get_unix_fd( attr
->RootDirectory
, 0, &fd
, &needs_close
, NULL
, NULL
)) return FALSE
;
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
;
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
;
2752 /* there are no redirects on 64-bit */
2753 BOOL
get_redirect( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*redir
)
2755 redir
->Buffer
= NULL
;
2762 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
2764 /***********************************************************************
2767 void init_files(void)
2772 if (is_wow64
) init_redirects();
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" );
2779 ignore_file( "/sys" );
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};
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] );
2803 /******************************************************************************
2806 * Get the Unix path of a DOS device.
2808 static NTSTATUS
get_dos_device( char **unix_name
, int start_pos
)
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
;
2820 if (!stat( *unix_name
, &st
))
2822 TRACE( "-> %s\n", debugstr_a(*unix_name
));
2823 return STATUS_SUCCESS
;
2827 /* now try some defaults for it */
2828 if (!strcmp( dev
, "aux" ))
2830 strcpy( dev
, "com1" );
2833 if (!strcmp( dev
, "prn" ))
2835 strcpy( dev
, "lpt1" );
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
);
2846 *unix_name
= new_name
;
2847 if (!new_name
) return STATUS_BAD_DEVICE_TYPE
;
2848 dev
= NULL
; /* last try */
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
);
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
)
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 '..'? */
2898 /* strip off trailing slashes */
2899 while (prev
> 1 && path
[prev
- 1] == '/') prev
--;
2906 /***********************************************************************
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.
2923 const char *path
= *ppath
;
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
);
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
));
2953 return STATUS_SUCCESS
;
2957 if (len
<= 1) break; /* reached root */
2958 len
= remove_last_componentA( buffer
, len
);
2962 return STATUS_OBJECT_PATH_NOT_FOUND
;
2966 /******************************************************************************
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
)
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 )))
2990 return STATUS_NO_MEMORY
;
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
)
3005 return STATUS_SUCCESS
;
3007 if (!S_ISDIR( st
.st_mode
)) continue;
3008 if ((status
= add_dir_to_queue( name
)) != STATUS_SUCCESS
)
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
;
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
)))
3048 if (type
!= FD_TYPE_DIR
)
3050 status
= STATUS_OBJECT_TYPE_MISMATCH
;
3054 fstat( root_fd
, &root_st
);
3055 if (root_st
.st_ino
== file_id
) /* shortcut for "." */
3057 status
= STATUS_SUCCESS
;
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
;
3072 status
= add_dir_to_queue( "." );
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 );
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
);
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
] = '\\';
3102 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id
), attr
->RootDirectory
);
3105 if (needs_close
) close( root_fd
);
3110 /******************************************************************************
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 };
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 */
3132 if (ptr
+ 1 == end
) return STATUS_OBJECT_NAME_INVALID
; /* "." element */
3133 if (ptr
[1] == '\\') return STATUS_OBJECT_NAME_INVALID
; /* "." element */
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)
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 */
3174 const WCHAR
*end
, *next
;
3177 while (end
< name
+ name_len
&& *end
!= '\\') 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)
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 */
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
;
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
);
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
,
3234 static const WCHAR unixW
[] = {'u','n','i','x'};
3235 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, 0 };
3237 NTSTATUS status
= STATUS_SUCCESS
;
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 */
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;
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
);
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
);
3299 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3301 if (wcschr( prefix
, '/' ))
3304 return STATUS_OBJECT_PATH_NOT_FOUND
;
3307 if (prefix_len
!= 2 || prefix
[1] != ':')
3310 if (lstat( unix_name
, &st
) == -1 && errno
== ENOENT
)
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 */
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
;
3334 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
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
;
3356 int name_len
, unix_len
;
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
;
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
;
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
;
3401 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
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
,
3420 char *buffer
= NULL
;
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
);
3431 if (*size
> strlen(buffer
)) strcpy( nameA
, buffer
);
3432 else status
= STATUS_BUFFER_TOO_SMALL
;
3433 *size
= strlen(buffer
) + 1;
3436 free( redir
.Buffer
);
3441 /******************************************************************
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
= '\\';
3454 while (*p
&& *p
!= '\\') p
++;
3457 /* collapse duplicate backslashes */
3459 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
3469 case '\\': /* .\ component */
3471 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3473 case 0: /* final . */
3478 if (p
[2] == '\\') /* ..\ component */
3484 while (p
> start
&& p
[-1] != '\\') p
--;
3486 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3489 else if (!p
[2]) /* final .. */
3494 while (p
> start
&& p
[-1] != '\\') p
--;
3503 /* skip to the next component */
3504 while (*p
&& *p
!= '\\') p
++;
3507 /* remove last dot in previous dir name */
3508 if (p
> start
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
3513 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3514 while (p
> start
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
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
;
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
);
3549 collapse_path( 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
;
3563 if (name
[0] != '/') return STATUS_INVALID_PARAMETER
; /* relative paths are not supported */
3565 status
= unix_to_nt_file_name( name
, &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;
3577 /***********************************************************************
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 */
3596 if (!wcsnicmp( name
, unixW
, 4 ) && IS_SEPARATOR(name
[4])) /* \\?\unix special name */
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
);
3608 else prefix
= uncW
; /* UNC path */
3610 else if (IS_SEPARATOR(name
[0])) /* absolute path */
3612 root
[4] = curdir
[4];
3615 else if (name
[0] && name
[1] == ':') /* drive letter */
3617 root
[4] = towupper(name
[0]);
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
);
3628 return STATUS_SUCCESS
;
3632 /***********************************************************************
3635 * Unmount the specified device.
3637 static NTSTATUS
unmount_device( HANDLE handle
)
3640 int unix_fd
, needs_close
;
3642 if (!(status
= server_get_unix_fd( handle
, 0, &unix_fd
, &needs_close
, NULL
, NULL
)))
3645 char *mount_point
= NULL
;
3647 if (fstat( unix_fd
, &st
) == -1 || !is_valid_mounted_device( &st
))
3648 status
= STATUS_INVALID_PARAMETER
;
3651 if ((mount_point
= get_device_mount_point( st
.st_rdev
)))
3654 static const char umount
[] = "diskutil unmount >/dev/null 2>&1 ";
3656 static const char umount
[] = "umount >/dev/null 2>&1 ";
3658 char *cmd
= malloc( strlen(mount_point
)+sizeof(umount
));
3661 strcpy( cmd
, umount
);
3662 strcat( cmd
, mount_point
);
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 );
3671 free( mount_point
);
3674 if (needs_close
) close( unix_fd
);
3680 /******************************************************************************
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
;
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
);
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
;
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" );
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
;
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
);
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
)
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
);
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
;
3790 io
->Information
= FILE_CREATED
;
3794 io
->Information
= FILE_OPENED
;
3796 case FILE_OVERWRITE
:
3797 case FILE_OVERWRITE_IF
:
3798 io
->Information
= FILE_OVERWRITTEN
;
3802 else if (io
->u
.Status
== STATUS_TOO_MANY_OPENED_FILES
)
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
)
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
);
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
)
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
;
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
);
3905 /******************************************************************
3906 * NtDeleteFile (NTDLL.@)
3908 NTSTATUS WINAPI
NtDeleteFile( OBJECT_ATTRIBUTES
*attr
)
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
);
3922 /******************************************************************************
3923 * NtQueryFullAttributesFile (NTDLL.@)
3925 NTSTATUS WINAPI
NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES
*attr
,
3926 FILE_NETWORK_OPEN_INFORMATION
*info
)
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
)))
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
;
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
;
3962 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
3963 free( redir
.Buffer
);
3968 /******************************************************************************
3969 * NtQueryAttributesFile (NTDLL.@)
3971 NTSTATUS WINAPI
NtQueryAttributesFile( const OBJECT_ATTRIBUTES
*attr
, FILE_BASIC_INFORMATION
*info
)
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
)))
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
;
3990 status
= fill_file_info( &st
, attributes
, info
, FileBasicInformation
);
3991 if (is_hidden_file( attr
->ObjectName
)) info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
3995 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
3996 free( redir
.Buffer
);
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 */
4076 int fd
, needs_close
= FALSE
;
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 );
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
;
4105 fill_file_info( &st
, attr
, ptr
, class );
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
);
4114 fill_file_info( &st
, attr
, info
, class );
4115 info
->DeletePending
= FALSE
; /* FIXME */
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
;
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 );
4131 case FileEaInformation
:
4133 FILE_EA_INFORMATION
*info
= ptr
;
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 );
4141 case FileAllInformation
:
4143 FILE_ALL_INFORMATION
*info
= ptr
;
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
);
4163 io
->Information
= FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
) + name_len
;
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
);
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
;
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
);
4205 case FileNameInformation
:
4207 FILE_NAME_INFORMATION
*info
= ptr
;
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
);
4215 io
->Information
= FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
) + name_len
;
4219 case FileNetworkOpenInformation
:
4221 FILE_NETWORK_OPEN_INFORMATION
*info
= ptr
;
4224 if (!(io
->u
.Status
= server_get_unix_name( handle
, &unix_name
)))
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
;
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
;
4253 case FileIdInformation
:
4254 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
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
;
4267 case FileAttributeTagInformation
:
4268 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
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
;
4279 FIXME("Unsupported class (%d)\n", class);
4280 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
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
;
4302 case FileBasicInformation
:
4303 if (len
>= sizeof(FILE_BASIC_INFORMATION
))
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
);
4323 if (info
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)
4325 if (S_ISDIR( st
.st_mode
))
4326 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
4328 st
.st_mode
&= ~0222; /* clear write permission bits */
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
;
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
;
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
);
4373 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
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
;
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
);
4396 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
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
);
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
);
4428 io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
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
);
4447 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
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
);
4457 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
4459 else io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4462 case FileAllInformation
:
4463 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4466 case FileValidDataLengthInformation
:
4467 if (len
>= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
))
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
;
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
;
4488 FIXME( "setting valid data length not supported\n" );
4491 if (needs_close
) close( fd
);
4493 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
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
);
4509 io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
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
;
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
;
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
);
4544 free( redir
.Buffer
);
4546 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
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
;
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
;
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
);
4581 free( redir
.Buffer
);
4583 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4587 FIXME("Unsupported class (%d)\n", class);
4588 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
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
;
4604 unsigned int already
;
4609 struct async_fileio_write
4611 struct async_fileio io
;
4613 unsigned int already
;
4617 struct async_fileio_read_changes
4619 struct async_fileio io
;
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
)
4639 struct async_fileio
*next
= fileio_freelist
;
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
);
4652 struct async_fileio
*next
= io
->next
;
4657 if ((io
= malloc( size
)))
4659 io
->callback
= callback
;
4660 io
->handle
= handle
;
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
)
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
);
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
;
4700 if (status
!= STATUS_PENDING
)
4702 io
->u
.Status
= status
;
4703 io
->Information
= information
;
4704 release_fileio( &async
->io
);
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
;
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
)))
4722 result
= virtual_locked_read(fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
-fileio
->already
);
4723 if (needs_close
) close( fd
);
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
;
4738 fileio
->already
+= result
;
4739 if (fileio
->already
>= fileio
->count
|| fileio
->avail_mode
)
4740 status
= STATUS_SUCCESS
;
4742 status
= STATUS_PENDING
;
4746 case STATUS_TIMEOUT
:
4747 case STATUS_IO_TIMEOUT
:
4748 if (fileio
->already
) status
= STATUS_SUCCESS
;
4751 if (status
!= STATUS_PENDING
)
4753 iosb
->u
.Status
= status
;
4754 iosb
->Information
= fileio
->already
;
4755 release_fileio( &fileio
->io
);
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
;
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
)))
4774 if (!fileio
->count
&& (type
== FD_TYPE_MAILSLOT
|| type
== FD_TYPE_SOCKET
))
4775 result
= send( fd
, fileio
->buffer
, 0, 0 );
4777 result
= write( fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
- fileio
->already
);
4779 if (needs_close
) close( fd
);
4783 if (errno
== EAGAIN
|| errno
== EINTR
) status
= STATUS_PENDING
;
4784 else status
= errno_to_status( errno
);
4788 fileio
->already
+= result
;
4789 status
= (fileio
->already
< fileio
->count
) ? STATUS_PENDING
: STATUS_SUCCESS
;
4793 case STATUS_TIMEOUT
:
4794 case STATUS_IO_TIMEOUT
:
4795 if (fileio
->already
) status
= STATUS_SUCCESS
;
4798 if (status
!= STATUS_PENDING
)
4800 iosb
->u
.Status
= status
;
4801 iosb
->Information
= fileio
->already
;
4802 release_fileio( &fileio
->io
);
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
;
4817 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4818 return STATUS_NO_MEMORY
;
4820 async
->buffer
= buffer
;
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
);
4839 if (status
!= STATUS_PENDING
) free( async
);
4841 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
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
;
4855 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4856 return STATUS_NO_MEMORY
;
4858 async
->buffer
= NULL
;
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
;
4877 if (status
!= STATUS_PENDING
) free( async
);
4879 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
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
;
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
)
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
);
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
) );
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;
4946 case FD_TYPE_SERIAL
:
4948 /* GetCommTimeouts */
4952 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
4953 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
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;
4972 if (st
.WriteTotalTimeoutMultiplier
|| st
.WriteTotalTimeoutConstant
)
4974 timeouts
->total
= st
.WriteTotalTimeoutConstant
;
4975 if (st
.WriteTotalTimeoutMultiplier
!= MAXDWORD
)
4976 timeouts
->total
+= count
* st
.WriteTotalTimeoutMultiplier
;
4981 case FD_TYPE_MAILSLOT
:
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
);
4989 if (!(status
= wine_server_call( req
)) &&
4990 reply
->read_timeout
!= TIMEOUT_INFINITE
)
4991 timeouts
->total
= reply
->read_timeout
/ -10000;
4996 case FD_TYPE_SOCKET
:
4998 if (is_read
) timeouts
->interval
= 0; /* return as soon as we got something */
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
)
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
;
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
;
5033 case FD_TYPE_SERIAL
:
5035 /* GetCommTimeouts */
5039 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
5040 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
5042 *avail_mode
= (!st
.ReadTotalTimeoutMultiplier
&&
5043 !st
.ReadTotalTimeoutConstant
&&
5044 st
.ReadIntervalTimeout
== MAXDWORD
);
5047 case FD_TYPE_MAILSLOT
:
5048 case FD_TYPE_SOCKET
:
5053 *avail_mode
= FALSE
;
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
;
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
);
5085 if (status
!= STATUS_PENDING
) free( fileio
);
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
;
5098 wine_server_call( req
);
5103 static NTSTATUS
set_pending_write( HANDLE device
)
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
);
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
;
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
;
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)
5164 status
= errno_to_status( errno
);
5168 if (!async_read
) /* update file pointer position */
5169 lseek( unix_handle
, offset
->QuadPart
+ result
, SEEK_SET
);
5172 status
= (total
|| !length
) ? STATUS_SUCCESS
: STATUS_END_OF_FILE
;
5176 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5178 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5180 status
= STATUS_INVALID_PARAMETER
;
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
);
5201 if ((result
= virtual_locked_read( unix_handle
, (char *)buffer
+ total
, length
- total
)) >= 0)
5204 if (!result
|| total
== length
)
5208 status
= STATUS_SUCCESS
;
5215 case FD_TYPE_DEVICE
:
5216 status
= length
? STATUS_END_OF_FILE
: STATUS_SUCCESS
;
5218 case FD_TYPE_SERIAL
:
5221 status
= STATUS_SUCCESS
;
5226 status
= STATUS_PIPE_BROKEN
;
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
);
5243 if ((status
= get_io_avail_mode( handle
, type
, &avail_mode
))) goto err
;
5244 if (total
&& avail_mode
)
5246 status
= STATUS_SUCCESS
;
5249 status
= register_async_file_read( handle
, event
, apc
, apc_user
, io
,
5250 buffer
, total
, length
, avail_mode
);
5253 else /* synchronous read, wait for the fd to become ready */
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
;
5274 status
= (type
== FD_TYPE_MAILSLOT
) ? STATUS_IO_TIMEOUT
: STATUS_TIMEOUT
;
5277 if (ret
== -1 && errno
!= EINTR
)
5279 status
= errno_to_status( errno
);
5282 /* will now restart the read */
5287 send_completion
= cvalue
!= 0;
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 );
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
);
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
;
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
;
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
);
5351 result
= read( unix_handle
, (char *)segments
->Buffer
+ pos
, min( length
- pos
, page_size
- pos
) );
5355 if (errno
== EINTR
) continue;
5356 status
= errno_to_status( errno
);
5362 if ((pos
+= result
) == page_size
)
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
;
5385 if (needs_close
) close( unix_handle
);
5386 if (event
) NtResetEvent( event
, NULL
);
5387 TRACE("= 0x%08x\n", 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
;
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
;
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
)
5437 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5439 status
= STATUS_INVALID_PARAMETER
;
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
)
5457 if (fstat( unix_handle
, &st
) == -1)
5459 status
= errno_to_status( errno
);
5464 else if (offset
->QuadPart
< 0)
5466 status
= STATUS_INVALID_PARAMETER
;
5470 /* async I/O doesn't make sense on regular files */
5471 while ((result
= pwrite( unix_handle
, buffer
, length
, off
)) == -1)
5475 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5476 else status
= errno_to_status( errno
);
5481 if (!async_write
) /* update file pointer position */
5482 lseek( unix_handle
, off
+ result
, SEEK_SET
);
5485 status
= STATUS_SUCCESS
;
5489 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5492 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5494 status
= STATUS_INVALID_PARAMETER
;
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 );
5505 result
= write( unix_handle
, (const char *)buffer
+ total
, length
- total
);
5510 if (total
== length
)
5512 status
= STATUS_SUCCESS
;
5515 if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5517 else if (errno
!= EAGAIN
)
5519 if (errno
== EINTR
) continue;
5522 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5523 else status
= errno_to_status( errno
);
5530 struct async_fileio_write
*fileio
;
5532 fileio
= (struct async_fileio_write
*)alloc_fileio( sizeof(*fileio
), async_write_proc
, handle
);
5535 status
= STATUS_NO_MEMORY
;
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
);
5551 if (status
!= STATUS_PENDING
) free( fileio
);
5554 else /* synchronous write, wait for the fd to become ready */
5559 if (!timeout_init_done
)
5561 timeout_init_done
= TRUE
;
5562 if ((status
= get_io_timeouts( handle
, type
, length
, FALSE
, &timeouts
)))
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
;
5577 if (ret
== -1 && errno
!= EINTR
)
5579 status
= errno_to_status( errno
);
5582 /* will now restart the write */
5587 send_completion
= cvalue
!= 0;
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 );
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
);
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
;
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
;
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
);
5654 result
= write( unix_handle
, (char *)segments
->Buffer
+ pos
, page_size
- pos
);
5658 if (errno
== EINTR
) continue;
5659 if (errno
== EFAULT
)
5661 status
= STATUS_INVALID_USER_BUFFER
;
5664 status
= errno_to_status( errno
);
5669 status
= STATUS_DISK_FULL
;
5674 if ((pos
+= result
) == page_size
)
5681 send_completion
= cvalue
!= 0;
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 );
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
);
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
);
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
);
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
);
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
);
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
);
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
;
5750 /* Tell Valgrind to ignore any holes in structs we will be passing to the
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) \
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)); \
5766 case FSCTL_PIPE_WAIT
:
5767 IGNORE_STRUCT_HOLE(in_buffer
, in_size
, FILE_PIPE_WAIT_FOR_BUFFER
, TimeoutSpecified
, Name
);
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
)
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
);
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
);
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
;
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
;
5828 io
->Information
= 0;
5829 status
= STATUS_BUFFER_TOO_SMALL
;
5834 case FSCTL_GET_OBJECT_ID
:
5836 FILE_OBJECTID_BUFFER
*info
= out_buffer
;
5837 int fd
, needs_close
;
5840 io
->Information
= 0;
5841 if (out_size
>= sizeof(*info
))
5843 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
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
;
5856 case FSCTL_SET_SPARSE
:
5857 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
5858 io
->Information
= 0;
5859 status
= STATUS_SUCCESS
;
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
;
5871 /******************************************************************************
5872 * NtFlushBuffersFile (NTDLL.@)
5874 NTSTATUS WINAPI
NtFlushBuffersFile( HANDLE handle
, IO_STATUS_BLOCK
*io
)
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
);
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
;
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
)
5914 io
->Information
= 0;
5919 if (ret
!= STATUS_PENDING
) free( async
);
5921 if (wait_handle
) ret
= wait_async( wait_handle
, FALSE
);
5924 if (needs_close
) close( fd
);
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
);
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
);
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
)
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");
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
;
5998 if (ret
!= STATUS_PENDING
)
6000 if (!ret
&& event
) NtSetEvent( event
, NULL
);
6005 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6006 if (handle
) NtClose( handle
);
6007 return STATUS_PENDING
;
6011 NtWaitForSingleObject( handle
, FALSE
, NULL
);
6014 else /* Unix lock conflict, sleep a bit and retry */
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
)
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
);
6053 static NTSTATUS
read_changes_apc( void *user
, IO_STATUS_BLOCK
*iosb
, NTSTATUS status
)
6055 struct async_fileio_read_changes
*fileio
= user
;
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
);
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
);
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
);
6104 status
= STATUS_NOTIFY_ENUM_DIR
;
6109 if (last_entry_offset
) *last_entry_offset
= 0;
6110 size
= fileio
->buffer_size
- left
;
6115 status
= STATUS_NOTIFY_ENUM_DIR
;
6120 if (status
!= STATUS_PENDING
)
6122 iosb
->u
.Status
= status
;
6123 iosb
->Information
= size
;
6124 release_fileio( &fileio
->io
);
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
;
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
);
6174 if (status
!= STATUS_PENDING
) free( fileio
);
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
;
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
;
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)
6219 return !memcmp( buffer
, wine_placeholder
, sizeof(wine_placeholder
) - 1 );
6222 static NTSTATUS
get_device_info( int fd
, FILE_FS_DEVICE_INFORMATION
*info
)
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
;
6232 switch(major(st
.st_rdev
))
6235 info
->DeviceType
= FILE_DEVICE_NULL
;
6238 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6241 info
->DeviceType
= FILE_DEVICE_PARALLEL_PORT
;
6243 case SCSI_TAPE_MAJOR
:
6244 info
->DeviceType
= FILE_DEVICE_TAPE
;
6247 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6250 if (ioctl(fd
, FIODTYPE
, &d_type
) == 0)
6255 info
->DeviceType
= FILE_DEVICE_TAPE
;
6258 info
->DeviceType
= FILE_DEVICE_DISK
;
6261 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6263 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6265 info
->DeviceType
= FILE_DEVICE_NULL
;
6269 /* no special d_type for parallel ports */
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)
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
;
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
;
6312 case 0x1373: /* devfs */
6313 case 0x9fa0: /* procfs */
6314 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
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. */
6322 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6325 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6328 if (fstatfs( fd
, &stfs
) < 0)
6329 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
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
;
6338 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flag
);
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
);
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
)
6353 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6354 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6358 case DKC_INTEL82072
:
6359 case DKC_INTEL82077
:
6360 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6361 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
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. */
6368 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6373 if (!warned
++) FIXME( "device info not properly supported on this platform\n" );
6374 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
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
;
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
;
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
);
6419 if (status
!= STATUS_PENDING
) free( async
);
6420 if (wait_handle
) status
= wait_async( wait_handle
, FALSE
);
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
);
6434 case FileFsSizeInformation
:
6435 if (length
< sizeof(FILE_FS_SIZE_INFORMATION
))
6436 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
6439 FILE_FS_SIZE_INFORMATION
*info
= buffer
;
6441 if (fstat( fd
, &st
) < 0)
6443 io
->u
.Status
= errno_to_status( errno
);
6446 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
6448 io
->u
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
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
);
6462 bsize
= stfs
.f_frsize
;
6465 if (fstatfs( fd
, &stfs
) < 0)
6467 io
->u
.Status
= errno_to_status( errno
);
6470 bsize
= stfs
.f_bsize
;
6472 if (bsize
== 2048) /* assume CD-ROM */
6474 info
->BytesPerSector
= 2048;
6475 info
->SectorsPerAllocationUnit
= 1;
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
;
6490 case FileFsDeviceInformation
:
6491 if (length
< sizeof(FILE_FS_DEVICE_INFORMATION
))
6492 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
6495 FILE_FS_DEVICE_INFORMATION
*info
= buffer
;
6497 if ((io
->u
.Status
= get_device_info( fd
, info
)) == STATUS_SUCCESS
)
6498 io
->Information
= sizeof(*info
);
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
;
6520 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) )) fs_type
= drive
.fs_type
;
6525 if (!fstatfs( fd
, &stfs
))
6527 #if defined(linux) && defined(HAVE_FSTATFS)
6528 switch (stfs
.f_type
)
6531 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6534 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6537 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
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
;
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
);
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
);
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
);
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
);
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
);
6587 io
->Information
= offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) + info
->FileSystemNameLength
;
6588 io
->u
.Status
= STATUS_SUCCESS
;
6592 case FileFsVolumeInformation
:
6594 FILE_FS_VOLUME_INFORMATION
*info
= buffer
;
6596 struct mountmgr_unix_drive
*drive
= (struct mountmgr_unix_drive
*)data
;
6599 if (length
< sizeof(FILE_FS_VOLUME_INFORMATION
))
6601 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
6605 if (get_mountmgr_fs_info( handle
, fd
, drive
, sizeof(data
) ))
6607 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
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
;
6623 case FileFsControlInformation
:
6624 FIXME( "%p: control info not supported\n", handle
);
6627 case FileFsFullSizeInformation
:
6628 FIXME( "%p: full size info not supported\n", handle
);
6631 case FileFsObjectIdInformation
:
6632 FIXME( "%p: object id info not supported\n", handle
);
6635 case FileFsMaximumInformation
:
6636 FIXME( "%p: maximum info not supported\n", handle
);
6640 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
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
)
6714 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle
, info_class
, ptr
, len
, used_len
);
6716 if (used_len
) *used_len
= 0;
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
);
6743 case ObjectNameInformation
:
6745 OBJECT_NAME_INFORMATION
*p
= ptr
;
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
;
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
;
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
;
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
;
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
);
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
;
6829 if (used_len
) *used_len
= sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
);
6830 status
= STATUS_INFO_LENGTH_MISMATCH
;
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
;
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
);
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
);
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
);
6897 FIXME("Unsupported information class %u\n", info_class
);
6898 status
= STATUS_NOT_IMPLEMENTED
;
6905 /**************************************************************************
6906 * NtSetInformationObject (NTDLL.@)
6908 NTSTATUS WINAPI
NtSetInformationObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
6909 void *ptr
, ULONG len
)
6913 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle
, info_class
, ptr
, len
);
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
);
6936 FIXME("Unsupported information class %u\n", info_class
);
6937 status
= STATUS_NOT_IMPLEMENTED
;