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 struct file_identity windir
;
246 static pthread_mutex_t dir_mutex
= PTHREAD_MUTEX_INITIALIZER
;
247 static pthread_mutex_t mnt_mutex
= PTHREAD_MUTEX_INITIALIZER
;
249 /* check if a given Unicode char is OK in a DOS short name */
250 static inline BOOL
is_invalid_dos_char( WCHAR ch
)
252 static const WCHAR invalid_chars
[] = { INVALID_DOS_CHARS
,'~','.',0 };
253 if (ch
> 0x7f) return TRUE
;
254 return wcschr( invalid_chars
, ch
) != NULL
;
257 /* check if the device can be a mounted volume */
258 static inline BOOL
is_valid_mounted_device( const struct stat
*st
)
260 #if defined(linux) || defined(__sun__)
261 return S_ISBLK( st
->st_mode
);
263 /* disks are char devices on *BSD */
264 return S_ISCHR( st
->st_mode
);
268 static inline void ignore_file( const char *name
)
271 assert( ignored_files_count
< MAX_IGNORED_FILES
);
272 if (!stat( name
, &st
))
274 ignored_files
[ignored_files_count
].dev
= st
.st_dev
;
275 ignored_files
[ignored_files_count
].ino
= st
.st_ino
;
276 ignored_files_count
++;
280 static inline BOOL
is_same_file( const struct file_identity
*file
, const struct stat
*st
)
282 return st
->st_dev
== file
->dev
&& st
->st_ino
== file
->ino
;
285 static inline BOOL
is_ignored_file( const struct stat
*st
)
289 for (i
= 0; i
< ignored_files_count
; i
++)
290 if (is_same_file( &ignored_files
[i
], st
)) return TRUE
;
294 static inline unsigned int dir_info_align( unsigned int len
)
296 return (len
+ 7) & ~7;
299 static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS
class, unsigned int len
)
303 case FileDirectoryInformation
:
304 return offsetof( FILE_DIRECTORY_INFORMATION
, FileName
[len
] );
305 case FileBothDirectoryInformation
:
306 return offsetof( FILE_BOTH_DIRECTORY_INFORMATION
, FileName
[len
] );
307 case FileFullDirectoryInformation
:
308 return offsetof( FILE_FULL_DIRECTORY_INFORMATION
, FileName
[len
] );
309 case FileIdBothDirectoryInformation
:
310 return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION
, FileName
[len
] );
311 case FileIdFullDirectoryInformation
:
312 return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION
, FileName
[len
] );
313 case FileIdGlobalTxDirectoryInformation
:
314 return offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION
, FileName
[len
] );
315 case FileNamesInformation
:
316 return offsetof( FILE_NAMES_INFORMATION
, FileName
[len
] );
323 static inline BOOL
has_wildcard( const UNICODE_STRING
*mask
)
327 if (!mask
) return TRUE
;
328 for (i
= 0; i
< mask
->Length
/ sizeof(WCHAR
); i
++)
329 if (mask
->Buffer
[i
] == '*' || mask
->Buffer
[i
] == '?') return TRUE
;
333 NTSTATUS
errno_to_status( int err
)
335 TRACE( "errno = %d\n", err
);
338 case EAGAIN
: return STATUS_SHARING_VIOLATION
;
339 case EBADF
: return STATUS_INVALID_HANDLE
;
340 case EBUSY
: return STATUS_DEVICE_BUSY
;
341 case ENOSPC
: return STATUS_DISK_FULL
;
344 case EACCES
: return STATUS_ACCESS_DENIED
;
345 case ENOTDIR
: return STATUS_OBJECT_PATH_NOT_FOUND
;
346 case ENOENT
: return STATUS_OBJECT_NAME_NOT_FOUND
;
347 case EISDIR
: return STATUS_INVALID_DEVICE_REQUEST
;
349 case ENFILE
: return STATUS_TOO_MANY_OPENED_FILES
;
350 case EINVAL
: return STATUS_INVALID_PARAMETER
;
351 case ENOTEMPTY
: return STATUS_DIRECTORY_NOT_EMPTY
;
352 case EPIPE
: return STATUS_PIPE_DISCONNECTED
;
353 case EIO
: return STATUS_DEVICE_NOT_READY
;
355 case ENOMEDIUM
: return STATUS_NO_MEDIA_IN_DEVICE
;
357 case ENXIO
: return STATUS_NO_SUCH_DEVICE
;
359 case EOPNOTSUPP
:return STATUS_NOT_SUPPORTED
;
360 case ECONNRESET
:return STATUS_PIPE_DISCONNECTED
;
361 case EFAULT
: return STATUS_ACCESS_VIOLATION
;
362 case ESPIPE
: return STATUS_ILLEGAL_FUNCTION
;
363 case ELOOP
: return STATUS_REPARSE_POINT_NOT_RESOLVED
;
364 #ifdef ETIME /* Missing on FreeBSD */
365 case ETIME
: return STATUS_IO_TIMEOUT
;
367 case ENOEXEC
: /* ?? */
368 case EEXIST
: /* ?? */
370 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err
);
371 return STATUS_UNSUCCESSFUL
;
375 /* get space from the current directory data buffer, allocating a new one if necessary */
376 static void *get_dir_data_space( struct dir_data
*data
, unsigned int size
)
378 struct dir_data_buffer
*buffer
= data
->buffer
;
381 if (!buffer
|| size
> buffer
->size
- buffer
->pos
)
383 unsigned int new_size
= buffer
? buffer
->size
* 2 : dir_data_buffer_initial_size
;
384 if (new_size
< size
) new_size
= size
;
385 if (!(buffer
= malloc( offsetof( struct dir_data_buffer
, data
[new_size
] ) ))) return NULL
;
387 buffer
->size
= new_size
;
388 buffer
->next
= data
->buffer
;
389 data
->buffer
= buffer
;
391 ret
= buffer
->data
+ buffer
->pos
;
396 /* add a string to the directory data buffer */
397 static const char *add_dir_data_nameA( struct dir_data
*data
, const char *name
)
399 /* keep buffer data WCHAR-aligned */
400 char *ptr
= get_dir_data_space( data
, (strlen( name
) + sizeof(WCHAR
)) & ~(sizeof(WCHAR
) - 1) );
401 if (ptr
) strcpy( ptr
, name
);
405 /* add a Unicode string to the directory data buffer */
406 static const WCHAR
*add_dir_data_nameW( struct dir_data
*data
, const WCHAR
*name
)
408 WCHAR
*ptr
= get_dir_data_space( data
, (wcslen( name
) + 1) * sizeof(WCHAR
) );
409 if (ptr
) wcscpy( ptr
, name
);
413 /* add an entry to the directory names array */
414 static BOOL
add_dir_data_names( struct dir_data
*data
, const WCHAR
*long_name
,
415 const WCHAR
*short_name
, const char *unix_name
)
417 static const WCHAR empty
[1];
418 struct dir_data_names
*names
= data
->names
;
420 if (data
->count
>= data
->size
)
422 unsigned int new_size
= max( data
->size
* 2, dir_data_names_initial_size
);
424 if (!(names
= realloc( names
, new_size
* sizeof(*names
) ))) return FALSE
;
425 data
->size
= new_size
;
431 if (!(names
[data
->count
].short_name
= add_dir_data_nameW( data
, short_name
))) return FALSE
;
433 else names
[data
->count
].short_name
= empty
;
435 if (!(names
[data
->count
].long_name
= add_dir_data_nameW( data
, long_name
))) return FALSE
;
436 if (!(names
[data
->count
].unix_name
= add_dir_data_nameA( data
, unix_name
))) return FALSE
;
441 /* free the complete directory data structure */
442 static void free_dir_data( struct dir_data
*data
)
444 struct dir_data_buffer
*buffer
, *next
;
448 for (buffer
= data
->buffer
; buffer
; buffer
= next
)
458 /* support for a directory queue for filesystem searches */
466 static struct list dir_queue
= LIST_INIT( dir_queue
);
468 static NTSTATUS
add_dir_to_queue( const char *name
)
470 int len
= strlen( name
) + 1;
471 struct dir_name
*dir
= malloc( offsetof( struct dir_name
, name
[len
] ));
472 if (!dir
) return STATUS_NO_MEMORY
;
473 strcpy( dir
->name
, name
);
474 list_add_tail( &dir_queue
, &dir
->entry
);
475 return STATUS_SUCCESS
;
478 static NTSTATUS
next_dir_in_queue( char *name
)
480 struct list
*head
= list_head( &dir_queue
);
483 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
484 strcpy( name
, dir
->name
);
485 list_remove( &dir
->entry
);
487 return STATUS_SUCCESS
;
489 return STATUS_OBJECT_NAME_NOT_FOUND
;
492 static void flush_dir_queue(void)
496 while ((head
= list_head( &dir_queue
)))
498 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
499 list_remove( &dir
->entry
);
507 static char *unescape_field( char *str
)
511 for (in
= out
= str
; *in
; in
++, out
++)
521 else if (in
[1] == '0' && in
[2] == '4' && in
[3] == '0')
526 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '1')
531 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '2')
536 else if (in
[1] == '1' && in
[2] == '3' && in
[3] == '4')
548 static inline char *get_field( char **str
)
552 ret
= strsep( str
, " \t" );
553 if (*str
) *str
+= strspn( *str
, " \t" );
557 /************************************************************************
558 * getmntent_replacement
560 * getmntent replacement for Android.
562 * NB returned static buffer is not thread safe; protect with mnt_mutex.
564 static struct mntent
*getmntent_replacement( FILE *f
)
566 static struct mntent entry
;
567 static char buf
[4096];
572 if (!fgets( buf
, sizeof(buf
), f
)) return NULL
;
573 p
= strchr( buf
, '\n' );
575 else /* Partially unread line, move file ptr to end */
578 while (fgets( tmp
, sizeof(tmp
), f
))
579 if (strchr( tmp
, '\n' )) break;
581 start
= buf
+ strspn( buf
, " \t" );
582 } while (start
[0] == '\0' || start
[0] == '#');
584 p
= get_field( &start
);
585 entry
.mnt_fsname
= p
? unescape_field( p
) : (char *)"";
587 p
= get_field( &start
);
588 entry
.mnt_dir
= p
? unescape_field( p
) : (char *)"";
590 p
= get_field( &start
);
591 entry
.mnt_type
= p
? unescape_field( p
) : (char *)"";
593 p
= get_field( &start
);
594 entry
.mnt_opts
= p
? unescape_field( p
) : (char *)"";
596 p
= get_field( &start
);
597 entry
.mnt_freq
= p
? atoi(p
) : 0;
599 p
= get_field( &start
);
600 entry
.mnt_passno
= p
? atoi(p
) : 0;
604 #define getmntent getmntent_replacement
607 /***********************************************************************
608 * parse_mount_entries
610 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
614 #include <sys/vfstab.h>
615 static char *parse_vfstab_entries( FILE *f
, dev_t dev
, ino_t ino
)
621 while (! getvfsent( f
, &entry
))
623 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
624 if (!strcmp( entry
.vfs_fstype
, "nfs" ) ||
625 !strcmp( entry
.vfs_fstype
, "smbfs" ) ||
626 !strcmp( entry
.vfs_fstype
, "ncpfs" )) continue;
628 if (stat( entry
.vfs_mountp
, &st
) == -1) continue;
629 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
630 if (!strcmp( entry
.vfs_fstype
, "fd" ))
632 if ((device
= strstr( entry
.vfs_mntopts
, "dev=" )))
634 char *p
= strchr( device
+ 4, ',' );
640 return entry
.vfs_special
;
647 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
649 struct mntent
*entry
;
653 while ((entry
= getmntent( f
)))
655 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
656 if (!strcmp( entry
->mnt_type
, "nfs" ) ||
657 !strcmp( entry
->mnt_type
, "cifs" ) ||
658 !strcmp( entry
->mnt_type
, "smbfs" ) ||
659 !strcmp( entry
->mnt_type
, "ncpfs" )) continue;
661 if (stat( entry
->mnt_dir
, &st
) == -1) continue;
662 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
663 if (!strcmp( entry
->mnt_type
, "supermount" ))
665 if ((device
= strstr( entry
->mnt_opts
, "dev=" )))
667 char *p
= strchr( device
+ 4, ',' );
672 else if (!stat( entry
->mnt_fsname
, &st
) && S_ISREG(st
.st_mode
))
674 /* if device is a regular file check for a loop mount */
675 if ((device
= strstr( entry
->mnt_opts
, "loop=" )))
677 char *p
= strchr( device
+ 5, ',' );
683 return entry
->mnt_fsname
;
689 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
691 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
696 while ((entry
= getfsent()))
698 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
699 if (!strcmp( entry
->fs_vfstype
, "nfs" ) ||
700 !strcmp( entry
->fs_vfstype
, "smbfs" ) ||
701 !strcmp( entry
->fs_vfstype
, "ncpfs" )) continue;
703 if (stat( entry
->fs_file
, &st
) == -1) continue;
704 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
705 return entry
->fs_spec
;
712 #include <sys/mnttab.h>
713 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
720 while (( ! getmntent( f
, &entry
) ))
722 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
723 if (!strcmp( entry
.mnt_fstype
, "nfs" ) ||
724 !strcmp( entry
.mnt_fstype
, "smbfs" ) ||
725 !strcmp( entry
.mnt_fstype
, "ncpfs" )) continue;
727 if (stat( entry
.mnt_mountp
, &st
) == -1) continue;
728 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
729 if (!strcmp( entry
.mnt_fstype
, "fd" ))
731 if ((device
= strstr( entry
.mnt_mntopts
, "dev=" )))
733 char *p
= strchr( device
+ 4, ',' );
739 return entry
.mnt_special
;
745 /***********************************************************************
746 * get_default_drive_device
748 * Return the default device to use for a given drive mount point.
750 static char *get_default_drive_device( const char *root
)
760 /* try to open it first to force it to get mounted */
761 if ((fd
= open( root
, O_RDONLY
| O_DIRECTORY
)) != -1)
763 res
= fstat( fd
, &st
);
766 /* now try normal stat just in case */
767 if (res
== -1) res
= stat( root
, &st
);
768 if (res
== -1) return NULL
;
770 mutex_lock( &mnt_mutex
);
773 if ((f
= fopen( "/proc/mounts", "r" )))
775 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
779 if ((f
= fopen( "/etc/mtab", "r" )))
781 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
784 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
785 if (!device
&& (f
= fopen( "/etc/fstab", "r" )))
787 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
791 if (device
) ret
= strdup( device
);
792 mutex_unlock( &mnt_mutex
);
794 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
799 /* try to open it first to force it to get mounted */
800 if ((fd
= open( root
, O_RDONLY
)) != -1)
802 res
= fstat( fd
, &st
);
805 /* now try normal stat just in case */
806 if (res
== -1) res
= stat( root
, &st
);
807 if (res
== -1) return NULL
;
809 mutex_lock( &mnt_mutex
);
811 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
812 * pass NULL. Leave the argument in for symmetry.
814 device
= parse_mount_entries( NULL
, st
.st_dev
, st
.st_ino
);
815 if (device
) ret
= strdup( device
);
816 mutex_unlock( &mnt_mutex
);
824 /* try to open it first to force it to get mounted */
825 if ((fd
= open( root
, O_RDONLY
)) != -1)
827 res
= fstat( fd
, &st
);
830 /* now try normal stat just in case */
831 if (res
== -1) res
= stat( root
, &st
);
832 if (res
== -1) return NULL
;
834 mutex_lock( &mnt_mutex
);
836 if ((f
= fopen( "/etc/mnttab", "r" )))
838 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
841 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
842 if (!device
&& (f
= fopen( "/etc/vfstab", "r" )))
844 device
= parse_vfstab_entries( f
, st
.st_dev
, st
.st_ino
);
847 if (device
) ret
= strdup( device
);
848 mutex_unlock( &mnt_mutex
);
850 #elif defined(__APPLE__)
851 struct statfs
*mntStat
;
857 static const char path_bsd_device
[] = "/dev/disk";
860 res
= stat( root
, &st
);
861 if (res
== -1) return NULL
;
866 mutex_lock( &mnt_mutex
);
868 mntSize
= getmntinfo(&mntStat
, MNT_NOWAIT
);
870 for (i
= 0; i
< mntSize
&& !ret
; i
++)
872 if (stat(mntStat
[i
].f_mntonname
, &st
) == -1) continue;
873 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
875 /* FIXME add support for mounted network drive */
876 if ( strncmp(mntStat
[i
].f_mntfromname
, path_bsd_device
, strlen(path_bsd_device
)) == 0)
878 /* set return value to the corresponding raw BSD node */
879 ret
= malloc( strlen(mntStat
[i
].f_mntfromname
) + 2 /* 2 : r and \0 */ );
882 strcpy(ret
, "/dev/r");
883 strcat(ret
, mntStat
[i
].f_mntfromname
+sizeof("/dev/")-1);
887 mutex_unlock( &mnt_mutex
);
890 if (!warned
++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
896 /***********************************************************************
897 * get_device_mount_point
899 * Return the current mount point for a device.
901 static char *get_device_mount_point( dev_t dev
)
908 mutex_lock( &mnt_mutex
);
911 if ((f
= fopen( "/proc/mounts", "r" )))
913 if ((f
= fopen( "/etc/mtab", "r" )))
916 struct mntent
*entry
;
920 while ((entry
= getmntent( f
)))
922 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
923 if (!strcmp( entry
->mnt_type
, "nfs" ) ||
924 !strcmp( entry
->mnt_type
, "cifs" ) ||
925 !strcmp( entry
->mnt_type
, "smbfs" ) ||
926 !strcmp( entry
->mnt_type
, "ncpfs" )) continue;
928 if (!strcmp( entry
->mnt_type
, "supermount" ))
930 if ((device
= strstr( entry
->mnt_opts
, "dev=" )))
933 if ((p
= strchr( device
, ',' ))) *p
= 0;
936 else if (!stat( entry
->mnt_fsname
, &st
) && S_ISREG(st
.st_mode
))
938 /* if device is a regular file check for a loop mount */
939 if ((device
= strstr( entry
->mnt_opts
, "loop=" )))
942 if ((p
= strchr( device
, ',' ))) *p
= 0;
945 else device
= entry
->mnt_fsname
;
947 if (device
&& !stat( device
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== dev
)
949 ret
= strdup( entry
->mnt_dir
);
955 mutex_unlock( &mnt_mutex
);
956 #elif defined(__APPLE__)
957 struct statfs
*entry
;
961 mutex_lock( &mnt_mutex
);
963 size
= getmntinfo( &entry
, MNT_NOWAIT
);
964 for (i
= 0; i
< size
; i
++)
966 if (stat( entry
[i
].f_mntfromname
, &st
) == -1) continue;
967 if (S_ISBLK(st
.st_mode
) && st
.st_rdev
== dev
)
969 ret
= strdup( entry
[i
].f_mntonname
);
973 mutex_unlock( &mnt_mutex
);
976 if (!warned
++) FIXME( "unmounting devices not supported on this platform\n" );
982 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
983 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
996 BOOLEAN case_sensitive
;
1002 vol_capabilities_attr_t caps
;
1005 /***********************************************************************
1008 * Checks if the specified file system is in the cache.
1010 static struct fs_cache
*look_up_fs_cache( dev_t dev
)
1013 for (i
= 0; i
< ARRAY_SIZE( fs_cache
); i
++)
1014 if (fs_cache
[i
].dev
== dev
)
1019 /***********************************************************************
1022 * Adds the specified file system to the cache.
1024 static void add_fs_cache( dev_t dev
, fsid_t fsid
, BOOLEAN case_sensitive
)
1027 struct fs_cache
*entry
= look_up_fs_cache( dev
);
1028 static int once
= 0;
1031 /* Update the cache */
1033 entry
->case_sensitive
= case_sensitive
;
1037 /* Add a new entry */
1038 for (i
= 0; i
< ARRAY_SIZE( fs_cache
); i
++)
1039 if (fs_cache
[i
].dev
== 0)
1041 /* This entry is empty, use it */
1042 fs_cache
[i
].dev
= dev
;
1043 fs_cache
[i
].fsid
= fsid
;
1044 fs_cache
[i
].case_sensitive
= case_sensitive
;
1048 /* Cache is out of space, warn */
1050 WARN( "FS cache is out of space, expect performance problems\n" );
1053 /***********************************************************************
1054 * get_dir_case_sensitivity_attr
1056 * Checks if the volume containing the specified directory is case
1057 * sensitive or not. Uses getattrlist(2).
1059 static int get_dir_case_sensitivity_attr( const char *dir
)
1062 struct attrlist attr
;
1063 struct vol_caps caps
;
1064 struct get_fsid get_fsid
;
1065 struct fs_cache
*entry
;
1067 /* First get the FS ID of the volume */
1068 attr
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1070 attr
.commonattr
= ATTR_CMN_DEVID
|ATTR_CMN_FSID
;
1071 attr
.volattr
= attr
.dirattr
= attr
.fileattr
= attr
.forkattr
= 0;
1073 if (getattrlist( dir
, &attr
, &get_fsid
, sizeof(get_fsid
), 0 ) != 0 ||
1074 get_fsid
.size
!= sizeof(get_fsid
))
1076 /* Try to look it up in the cache */
1077 entry
= look_up_fs_cache( get_fsid
.dev
);
1078 if (entry
&& !memcmp( &entry
->fsid
, &get_fsid
.fsid
, sizeof(fsid_t
) ))
1079 /* Cache lookup succeeded */
1080 return entry
->case_sensitive
;
1081 /* Cache is stale at this point, we have to update it */
1083 mntpoint
= get_device_mount_point( get_fsid
.dev
);
1084 /* Now look up the case-sensitivity */
1085 attr
.commonattr
= 0;
1086 attr
.volattr
= ATTR_VOL_INFO
|ATTR_VOL_CAPABILITIES
;
1087 if (getattrlist( mntpoint
, &attr
, &caps
, sizeof(caps
), 0 ) < 0)
1090 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, TRUE
);
1094 if (caps
.size
== sizeof(caps
) &&
1095 (caps
.caps
.valid
[VOL_CAPABILITIES_FORMAT
] &
1096 (VOL_CAP_FMT_CASE_SENSITIVE
| VOL_CAP_FMT_CASE_PRESERVING
)) ==
1097 (VOL_CAP_FMT_CASE_SENSITIVE
| VOL_CAP_FMT_CASE_PRESERVING
))
1101 if ((caps
.caps
.capabilities
[VOL_CAPABILITIES_FORMAT
] &
1102 VOL_CAP_FMT_CASE_SENSITIVE
) != VOL_CAP_FMT_CASE_SENSITIVE
)
1106 /* Update the cache */
1107 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, ret
);
1114 /***********************************************************************
1115 * get_dir_case_sensitivity_stat
1117 * Checks if the volume containing the specified directory is case
1118 * sensitive or not. Uses (f)statfs(2), statvfs(2), fstatat(2), or ioctl(2).
1120 static BOOLEAN
get_dir_case_sensitivity_stat( const char *dir
)
1122 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1125 if (statfs( dir
, &stfs
) == -1) return TRUE
;
1126 /* Assume these file systems are always case insensitive.*/
1127 if (!strcmp( stfs
.f_fstypename
, "fusefs" ) &&
1128 !strncmp( stfs
.f_mntfromname
, "ciopfs", 5 ))
1130 /* msdosfs was case-insensitive since FreeBSD 8, if not earlier */
1131 if (!strcmp( stfs
.f_fstypename
, "msdosfs" ) ||
1132 /* older CIFS protocol versions uppercase filename on the client,
1133 * newer versions should be case-insensitive on the server anyway */
1134 !strcmp( stfs
.f_fstypename
, "smbfs" ))
1136 /* no ntfs-3g: modern fusefs has no way to report the filesystem on FreeBSD
1137 * no cd9660 or udf, they're case-sensitive on FreeBSD
1140 if (!strcmp( stfs
.f_fstypename
, "msdos" ) ||
1141 !strcmp( stfs
.f_fstypename
, "cd9660" ) ||
1142 !strcmp( stfs
.f_fstypename
, "udf" ) ||
1143 !strcmp( stfs
.f_fstypename
, "ntfs" ))
1145 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1146 if (!strcmp( stfs
.f_fstypename
, "hfs" ) && (stfs
.f_fssubtype
== 0 ||
1147 stfs
.f_fssubtype
== 1 ||
1148 stfs
.f_fssubtype
== 128))
1151 /* The field says "reserved", but a quick look at the kernel source
1152 * tells us that this "reserved" field is really the same as the
1153 * "fssubtype" field from the inode64 structure (see munge_statfs()
1154 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1156 if (!strcmp( stfs
.f_fstypename
, "hfs" ) && (stfs
.f_reserved1
== 0 ||
1157 stfs
.f_reserved1
== 1 ||
1158 stfs
.f_reserved1
== 128))
1164 #elif defined(__NetBSD__)
1165 struct statvfs stfs
;
1167 if (statvfs( dir
, &stfs
) == -1) return TRUE
;
1168 /* Only assume CIOPFS is case insensitive. */
1169 if (strcmp( stfs
.f_fstypename
, "fusefs" ) ||
1170 strncmp( stfs
.f_mntfromname
, "ciopfs", 5 ))
1174 #elif defined(__linux__)
1175 BOOLEAN sens
= TRUE
;
1180 if ((fd
= open( dir
, O_RDONLY
| O_NONBLOCK
| O_LARGEFILE
)) == -1)
1183 if (ioctl( fd
, EXT2_IOC_GETFLAGS
, &flags
) != -1 && (flags
& EXT4_CASEFOLD_FL
))
1187 else if (fstatfs( fd
, &stfs
) == 0 && /* CIOPFS is case insensitive. Instead of */
1188 stfs
.f_type
== 0x65735546 /* FUSE_SUPER_MAGIC */ && /* parsing mtab to discover if the FUSE FS */
1189 fstatat( fd
, ".ciopfs", &st
, AT_NO_AUTOMOUNT
) == 0) /* is CIOPFS, look for .ciopfs in the dir. */
1202 /***********************************************************************
1203 * get_dir_case_sensitivity
1205 * Checks if the volume containing the specified directory is case
1206 * sensitive or not. Uses multiple methods, depending on platform.
1208 static BOOLEAN
get_dir_case_sensitivity( const char *dir
)
1210 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1211 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1212 int case_sensitive
= get_dir_case_sensitivity_attr( dir
);
1213 if (case_sensitive
!= -1) return case_sensitive
;
1215 return get_dir_case_sensitivity_stat( dir
);
1219 /***********************************************************************
1222 * Check if the specified file should be hidden based on its name and the show dot files option.
1224 static BOOL
is_hidden_file( const UNICODE_STRING
*name
)
1228 if (show_dot_files
) return FALSE
;
1230 end
= p
= name
->Buffer
+ name
->Length
/sizeof(WCHAR
);
1231 while (p
> name
->Buffer
&& IS_SEPARATOR(p
[-1])) p
--;
1232 while (p
> name
->Buffer
&& !IS_SEPARATOR(p
[-1])) p
--;
1233 if (p
== end
|| *p
!= '.') return FALSE
;
1234 /* make sure it isn't '.' or '..' */
1235 if (p
+ 1 == end
) return FALSE
;
1236 if (p
[1] == '.' && p
+ 2 == end
) return FALSE
;
1241 /***********************************************************************
1242 * hash_short_file_name
1244 * Transform a Unix file name into a hashed DOS name. If the name is not a valid
1245 * DOS name, it is replaced by a hashed version that fits in 8.3 format.
1246 * 'buffer' must be at least 12 characters long.
1247 * Returns length of short name in bytes; short name is NOT null-terminated.
1249 static ULONG
hash_short_file_name( const WCHAR
*name
, int length
, LPWSTR buffer
)
1251 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1253 LPCWSTR p
, ext
, end
= name
+ length
;
1255 unsigned short hash
;
1258 /* Compute the hash code of the file name */
1259 /* If you know something about hash functions, feel free to */
1260 /* insert a better algorithm here... */
1261 if (!is_case_sensitive
)
1263 for (p
= name
, hash
= 0xbeef; p
< end
- 1; p
++)
1264 hash
= (hash
<<3) ^ (hash
>>5) ^ towlower(*p
) ^ (towlower(p
[1]) << 8);
1265 hash
= (hash
<<3) ^ (hash
>>5) ^ towlower(*p
); /* Last character */
1269 for (p
= name
, hash
= 0xbeef; p
< end
- 1; p
++)
1270 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
1271 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
1274 /* Find last dot for start of the extension */
1275 for (p
= name
+ 1, ext
= NULL
; p
< end
- 1; p
++) if (*p
== '.') ext
= p
;
1277 /* Copy first 4 chars, replacing invalid chars with '_' */
1278 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
1280 if (p
== end
|| p
== ext
) break;
1281 *dst
++ = is_invalid_dos_char(*p
) ? '_' : *p
;
1283 /* Pad to 5 chars with '~' */
1284 while (i
-- >= 0) *dst
++ = '~';
1286 /* Insert hash code converted to 3 ASCII chars */
1287 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
1288 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
1289 *dst
++ = hash_chars
[hash
& 0x1f];
1291 /* Copy the first 3 chars of the extension (if any) */
1295 for (i
= 3, ext
++; (i
> 0) && ext
< end
; i
--, ext
++)
1296 *dst
++ = is_invalid_dos_char(*ext
) ? '_' : *ext
;
1298 return dst
- buffer
;
1302 /***********************************************************************
1305 * Check a long file name against a mask.
1307 * Tests (done in W95 DOS shell - case insensitive):
1308 * *.txt test1.test.txt *
1310 * *.t??????.t* test1.ta.tornado.txt *
1311 * *tornado* test1.ta.tornado.txt *
1312 * t*t test1.ta.tornado.txt *
1314 * ?est??? test1.txt -
1315 * *test1.txt* test1.txt *
1316 * h?l?o*t.dat hellothisisatest.dat *
1318 static BOOLEAN
match_filename( const WCHAR
*name
, int length
, const UNICODE_STRING
*mask_str
)
1321 const WCHAR
*mask
= mask_str
->Buffer
;
1322 const WCHAR
*name_end
= name
+ length
;
1323 const WCHAR
*mask_end
= mask
+ mask_str
->Length
/ sizeof(WCHAR
);
1324 const WCHAR
*lastjoker
= NULL
;
1325 const WCHAR
*next_to_retry
= NULL
;
1327 while (name
< name_end
&& mask
< mask_end
)
1333 while (mask
< mask_end
&& *mask
== '*') mask
++; /* Skip consecutive '*' */
1334 if (mask
== mask_end
) return TRUE
; /* end of mask is all '*', so match */
1337 /* skip to the next match after the joker(s) */
1338 if (is_case_sensitive
)
1339 while (name
< name_end
&& (*name
!= *mask
)) name
++;
1341 while (name
< name_end
&& (towupper(*name
) != towupper(*mask
))) name
++;
1342 next_to_retry
= name
;
1350 if (is_case_sensitive
) mismatch
= (*mask
!= *name
);
1351 else mismatch
= (towupper(*mask
) != towupper(*name
));
1357 if (mask
== mask_end
)
1359 if (name
== name_end
) return TRUE
;
1360 if (lastjoker
) mask
= lastjoker
;
1363 else /* mismatch ! */
1365 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
1369 /* this scan sequence was a mismatch, so restart
1370 * 1 char after the first char we checked last time */
1372 name
= next_to_retry
;
1374 else return FALSE
; /* bad luck */
1379 while (mask
< mask_end
&& ((*mask
== '.') || (*mask
== '*')))
1380 mask
++; /* Ignore trailing '.' or '*' in mask */
1381 return (name
== name_end
&& mask
== mask_end
);
1385 /***********************************************************************
1386 * is_legal_8dot3_name
1388 * Simplified version of RtlIsNameLegalDOS8Dot3.
1390 static BOOLEAN
is_legal_8dot3_name( const WCHAR
*name
, int len
)
1392 static const WCHAR invalid_chars
[] = { INVALID_DOS_CHARS
,':','/','\\',0 };
1395 if (len
> 12) return FALSE
;
1397 /* a starting . is invalid, except for . and .. */
1398 if (len
> 0 && name
[0] == '.') return (len
== 1 || (len
== 2 && name
[1] == '.'));
1400 for (i
= 0; i
< len
; i
++)
1402 if (name
[i
] > 0x7f) return FALSE
;
1403 if (wcschr( invalid_chars
, name
[i
] )) return FALSE
;
1406 if (dot
!= -1) return FALSE
;
1411 if (dot
== -1) return (len
<= 8);
1412 if (dot
> 8) return FALSE
;
1413 return (len
- dot
> 1 && len
- dot
< 5);
1417 /***********************************************************************
1420 * Add a file to the directory data if it matches the mask.
1422 static BOOL
append_entry( struct dir_data
*data
, const char *long_name
,
1423 const char *short_name
, const UNICODE_STRING
*mask
)
1425 int long_len
, short_len
;
1426 WCHAR long_nameW
[MAX_DIR_ENTRY_LEN
+ 1];
1427 WCHAR short_nameW
[13];
1429 long_len
= ntdll_umbstowcs( long_name
, strlen(long_name
), long_nameW
, ARRAY_SIZE(long_nameW
) );
1430 if (long_len
== ARRAY_SIZE(long_nameW
)) return TRUE
;
1431 long_nameW
[long_len
] = 0;
1435 short_len
= ntdll_umbstowcs( short_name
, strlen(short_name
),
1436 short_nameW
, ARRAY_SIZE( short_nameW
) - 1 );
1438 else /* generate a short name if necessary */
1441 if (!is_legal_8dot3_name( long_nameW
, long_len
))
1442 short_len
= hash_short_file_name( long_nameW
, long_len
, short_nameW
);
1444 short_nameW
[short_len
] = 0;
1445 wcsupr( short_nameW
);
1447 TRACE( "long %s short %s mask %s\n",
1448 debugstr_w( long_nameW
), debugstr_w( short_nameW
), debugstr_us( mask
));
1450 if (mask
&& !match_filename( long_nameW
, long_len
, mask
))
1452 if (!short_len
) return TRUE
; /* no short name to match */
1453 if (!match_filename( short_nameW
, short_len
, mask
)) return TRUE
;
1456 return add_dir_data_names( data
, long_nameW
, short_nameW
, long_name
);
1460 /* fetch the attributes of a file */
1461 static inline ULONG
get_file_attributes( const struct stat
*st
)
1465 if (S_ISDIR(st
->st_mode
))
1466 attr
= FILE_ATTRIBUTE_DIRECTORY
;
1468 attr
= FILE_ATTRIBUTE_ARCHIVE
;
1469 if (!(st
->st_mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
)))
1470 attr
|= FILE_ATTRIBUTE_READONLY
;
1475 static BOOL
fd_is_mount_point( int fd
, const struct stat
*st
)
1478 return S_ISDIR( st
->st_mode
) && !fstatat( fd
, "..", &parent
, 0 )
1479 && (parent
.st_dev
!= st
->st_dev
|| parent
.st_ino
== st
->st_ino
);
1483 /* get the stat info and file attributes for a file (by file descriptor) */
1484 static int fd_get_file_info( int fd
, unsigned int options
, struct stat
*st
, ULONG
*attr
)
1489 ret
= fstat( fd
, st
);
1490 if (ret
== -1) return ret
;
1491 *attr
|= get_file_attributes( st
);
1492 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1493 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, st
))
1494 *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1499 /* get the stat info and file attributes for a file (by name) */
1500 static int get_file_info( const char *path
, struct stat
*st
, ULONG
*attr
)
1506 ret
= lstat( path
, st
);
1507 if (ret
== -1) return ret
;
1508 if (S_ISLNK( st
->st_mode
))
1510 ret
= stat( path
, st
);
1511 if (ret
== -1) return ret
;
1512 /* is a symbolic link and a directory, consider these "reparse points" */
1513 if (S_ISDIR( st
->st_mode
)) *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1515 else if (S_ISDIR( st
->st_mode
) && (parent_path
= malloc( strlen(path
) + 4 )))
1517 struct stat parent_st
;
1519 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1520 strcpy( parent_path
, path
);
1521 strcat( parent_path
, "/.." );
1522 if (!stat( parent_path
, &parent_st
)
1523 && (st
->st_dev
!= parent_st
.st_dev
|| st
->st_ino
== parent_st
.st_ino
))
1524 *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1526 free( parent_path
);
1528 *attr
|= get_file_attributes( st
);
1533 #if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
1534 static int futimens( int fd
, const struct timespec spec
[2] )
1536 return syscall( __NR_utimensat
, fd
, NULL
, spec
, 0 );
1538 #define HAVE_FUTIMENS
1539 #endif /* __ANDROID__ */
1542 #define UTIME_OMIT ((1 << 30) - 2)
1545 static BOOL
set_file_times_precise( int fd
, const LARGE_INTEGER
*mtime
,
1546 const LARGE_INTEGER
*atime
, NTSTATUS
*status
)
1548 #ifdef HAVE_FUTIMENS
1549 struct timespec tv
[2];
1551 tv
[0].tv_sec
= tv
[1].tv_sec
= 0;
1552 tv
[0].tv_nsec
= tv
[1].tv_nsec
= UTIME_OMIT
;
1553 if (atime
->QuadPart
)
1555 tv
[0].tv_sec
= atime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1556 tv
[0].tv_nsec
= (atime
->QuadPart
% 10000000) * 100;
1558 if (mtime
->QuadPart
)
1560 tv
[1].tv_sec
= mtime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1561 tv
[1].tv_nsec
= (mtime
->QuadPart
% 10000000) * 100;
1564 if (!&futimens
) return FALSE
;
1566 if (futimens( fd
, tv
) == -1) *status
= errno_to_status( errno
);
1567 else *status
= STATUS_SUCCESS
;
1575 static NTSTATUS
set_file_times( int fd
, const LARGE_INTEGER
*mtime
, const LARGE_INTEGER
*atime
)
1577 NTSTATUS status
= STATUS_SUCCESS
;
1578 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1579 struct timeval tv
[2];
1583 if (set_file_times_precise( fd
, mtime
, atime
, &status
))
1586 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1587 if (!atime
->QuadPart
|| !mtime
->QuadPart
)
1590 tv
[0].tv_sec
= tv
[0].tv_usec
= 0;
1591 tv
[1].tv_sec
= tv
[1].tv_usec
= 0;
1592 if (!fstat( fd
, &st
))
1594 tv
[0].tv_sec
= st
.st_atime
;
1595 tv
[1].tv_sec
= st
.st_mtime
;
1596 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1597 tv
[0].tv_usec
= st
.st_atim
.tv_nsec
/ 1000;
1598 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1599 tv
[0].tv_usec
= st
.st_atimespec
.tv_nsec
/ 1000;
1601 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1602 tv
[1].tv_usec
= st
.st_mtim
.tv_nsec
/ 1000;
1603 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1604 tv
[1].tv_usec
= st
.st_mtimespec
.tv_nsec
/ 1000;
1608 if (atime
->QuadPart
)
1610 tv
[0].tv_sec
= atime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1611 tv
[0].tv_usec
= (atime
->QuadPart
% 10000000) / 10;
1613 if (mtime
->QuadPart
)
1615 tv
[1].tv_sec
= mtime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1616 tv
[1].tv_usec
= (mtime
->QuadPart
% 10000000) / 10;
1619 if (futimes( fd
, tv
) == -1) status
= errno_to_status( errno
);
1620 #elif defined(HAVE_FUTIMESAT)
1621 if (futimesat( fd
, NULL
, tv
) == -1) status
= errno_to_status( errno
);
1624 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1625 FIXME( "setting file times not supported\n" );
1626 status
= STATUS_NOT_IMPLEMENTED
;
1632 static inline void get_file_times( const struct stat
*st
, LARGE_INTEGER
*mtime
, LARGE_INTEGER
*ctime
,
1633 LARGE_INTEGER
*atime
, LARGE_INTEGER
*creation
)
1635 mtime
->QuadPart
= ticks_from_time_t( st
->st_mtime
);
1636 ctime
->QuadPart
= ticks_from_time_t( st
->st_ctime
);
1637 atime
->QuadPart
= ticks_from_time_t( st
->st_atime
);
1638 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1639 mtime
->QuadPart
+= st
->st_mtim
.tv_nsec
/ 100;
1640 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1641 mtime
->QuadPart
+= st
->st_mtimespec
.tv_nsec
/ 100;
1643 #ifdef HAVE_STRUCT_STAT_ST_CTIM
1644 ctime
->QuadPart
+= st
->st_ctim
.tv_nsec
/ 100;
1645 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1646 ctime
->QuadPart
+= st
->st_ctimespec
.tv_nsec
/ 100;
1648 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1649 atime
->QuadPart
+= st
->st_atim
.tv_nsec
/ 100;
1650 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1651 atime
->QuadPart
+= st
->st_atimespec
.tv_nsec
/ 100;
1653 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1654 creation
->QuadPart
= ticks_from_time_t( st
->st_birthtime
);
1655 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
1656 creation
->QuadPart
+= st
->st_birthtim
.tv_nsec
/ 100;
1657 #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1658 creation
->QuadPart
+= st
->st_birthtimespec
.tv_nsec
/ 100;
1660 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
1661 creation
->QuadPart
= ticks_from_time_t( st
->__st_birthtime
);
1662 #ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
1663 creation
->QuadPart
+= st
->__st_birthtim
.tv_nsec
/ 100;
1671 /* fill in the file information that depends on the stat and attribute info */
1672 static NTSTATUS
fill_file_info( const struct stat
*st
, ULONG attr
, void *ptr
,
1673 FILE_INFORMATION_CLASS
class )
1677 case FileBasicInformation
:
1679 FILE_BASIC_INFORMATION
*info
= ptr
;
1681 get_file_times( st
, &info
->LastWriteTime
, &info
->ChangeTime
,
1682 &info
->LastAccessTime
, &info
->CreationTime
);
1683 info
->FileAttributes
= attr
;
1686 case FileStandardInformation
:
1688 FILE_STANDARD_INFORMATION
*info
= ptr
;
1690 if ((info
->Directory
= S_ISDIR(st
->st_mode
)))
1692 info
->AllocationSize
.QuadPart
= 0;
1693 info
->EndOfFile
.QuadPart
= 0;
1694 info
->NumberOfLinks
= 1;
1698 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1699 info
->EndOfFile
.QuadPart
= st
->st_size
;
1700 info
->NumberOfLinks
= st
->st_nlink
;
1704 case FileInternalInformation
:
1706 FILE_INTERNAL_INFORMATION
*info
= ptr
;
1707 info
->IndexNumber
.QuadPart
= st
->st_ino
;
1710 case FileEndOfFileInformation
:
1712 FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
1713 info
->EndOfFile
.QuadPart
= S_ISDIR(st
->st_mode
) ? 0 : st
->st_size
;
1716 case FileAllInformation
:
1718 FILE_ALL_INFORMATION
*info
= ptr
;
1719 fill_file_info( st
, attr
, &info
->BasicInformation
, FileBasicInformation
);
1720 fill_file_info( st
, attr
, &info
->StandardInformation
, FileStandardInformation
);
1721 fill_file_info( st
, attr
, &info
->InternalInformation
, FileInternalInformation
);
1724 /* all directory structures start with the FileDirectoryInformation layout */
1725 case FileBothDirectoryInformation
:
1726 case FileFullDirectoryInformation
:
1727 case FileDirectoryInformation
:
1729 FILE_DIRECTORY_INFORMATION
*info
= ptr
;
1731 get_file_times( st
, &info
->LastWriteTime
, &info
->ChangeTime
,
1732 &info
->LastAccessTime
, &info
->CreationTime
);
1733 if (S_ISDIR(st
->st_mode
))
1735 info
->AllocationSize
.QuadPart
= 0;
1736 info
->EndOfFile
.QuadPart
= 0;
1740 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1741 info
->EndOfFile
.QuadPart
= st
->st_size
;
1743 info
->FileAttributes
= attr
;
1746 case FileIdFullDirectoryInformation
:
1748 FILE_ID_FULL_DIRECTORY_INFORMATION
*info
= ptr
;
1749 info
->FileId
.QuadPart
= st
->st_ino
;
1750 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
1753 case FileIdBothDirectoryInformation
:
1755 FILE_ID_BOTH_DIRECTORY_INFORMATION
*info
= ptr
;
1756 info
->FileId
.QuadPart
= st
->st_ino
;
1757 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
1760 case FileIdGlobalTxDirectoryInformation
:
1762 FILE_ID_GLOBAL_TX_DIR_INFORMATION
*info
= ptr
;
1763 info
->FileId
.QuadPart
= st
->st_ino
;
1764 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
1769 return STATUS_INVALID_INFO_CLASS
;
1771 return STATUS_SUCCESS
;
1775 static NTSTATUS
server_get_unix_name( HANDLE handle
, char **unix_name
)
1777 data_size_t size
= 1024;
1783 if (!(name
= malloc( size
+ 1 ))) return STATUS_NO_MEMORY
;
1785 SERVER_START_REQ( get_handle_unix_name
)
1787 req
->handle
= wine_server_obj_handle( handle
);
1788 wine_server_set_reply( req
, name
, size
);
1789 ret
= wine_server_call( req
);
1790 size
= reply
->name_len
;
1801 if (ret
!= STATUS_BUFFER_OVERFLOW
) break;
1806 static NTSTATUS
fill_name_info( const char *unix_name
, FILE_NAME_INFORMATION
*info
, LONG
*name_len
)
1811 if (!(status
= unix_to_nt_file_name( unix_name
, &nt_name
)))
1813 const WCHAR
*ptr
= nt_name
;
1814 const WCHAR
*end
= ptr
+ wcslen( nt_name
);
1816 /* Skip the volume mount point. */
1817 while (ptr
!= end
&& *ptr
== '\\') ++ptr
;
1818 while (ptr
!= end
&& *ptr
!= '\\') ++ptr
;
1819 while (ptr
!= end
&& *ptr
== '\\') ++ptr
;
1820 while (ptr
!= end
&& *ptr
!= '\\') ++ptr
;
1822 info
->FileNameLength
= (end
- ptr
) * sizeof(WCHAR
);
1823 if (*name_len
< info
->FileNameLength
) status
= STATUS_BUFFER_OVERFLOW
;
1824 else *name_len
= info
->FileNameLength
;
1826 memcpy( info
->FileName
, ptr
, *name_len
);
1834 static NTSTATUS
server_get_file_info( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
,
1835 ULONG length
, FILE_INFORMATION_CLASS info_class
)
1837 SERVER_START_REQ( get_file_info
)
1839 req
->handle
= wine_server_obj_handle( handle
);
1840 req
->info_class
= info_class
;
1841 wine_server_set_reply( req
, buffer
, length
);
1842 io
->u
.Status
= wine_server_call( req
);
1843 io
->Information
= wine_server_reply_size( reply
);
1846 if (io
->u
.Status
== STATUS_NOT_IMPLEMENTED
)
1847 FIXME( "Unsupported info class %x\n", info_class
);
1848 return io
->u
.Status
;
1853 /* retrieve device/inode number for all the drives */
1854 static unsigned int get_drives_info( struct file_identity info
[MAX_DOS_DRIVES
] )
1856 static pthread_mutex_t cache_mutex
= PTHREAD_MUTEX_INITIALIZER
;
1857 static struct file_identity cache
[MAX_DOS_DRIVES
];
1858 static time_t last_update
;
1859 static unsigned int nb_drives
;
1861 time_t now
= time(NULL
);
1863 mutex_lock( &cache_mutex
);
1864 if (now
!= last_update
)
1870 if ((buffer
= malloc( strlen(config_dir
) + sizeof("/dosdevices/a:") )))
1872 strcpy( buffer
, config_dir
);
1873 strcat( buffer
, "/dosdevices/a:" );
1874 p
= buffer
+ strlen(buffer
) - 2;
1876 for (i
= nb_drives
= 0; i
< MAX_DOS_DRIVES
; i
++)
1879 if (!stat( buffer
, &st
))
1881 cache
[i
].dev
= st
.st_dev
;
1882 cache
[i
].ino
= st
.st_ino
;
1895 memcpy( info
, cache
, sizeof(cache
) );
1897 mutex_unlock( &cache_mutex
);
1902 /* Find a DOS device which can act as the root of "path".
1903 * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
1904 static int find_dos_device( const char *path
)
1906 int len
= strlen(path
);
1910 struct file_identity info
[MAX_DOS_DRIVES
];
1913 if (!get_drives_info( info
)) return -1;
1915 if (stat( path
, &st
) < 0) return -1;
1918 /* strip off trailing slashes */
1919 while (len
> 1 && path
[len
- 1] == '/') len
--;
1921 /* make a copy of the path */
1922 if (!(buffer
= malloc( len
+ 1 ))) return -1;
1923 memcpy( buffer
, path
, len
);
1928 if (!stat( buffer
, &st
) && S_ISDIR( st
.st_mode
))
1930 if (st
.st_dev
!= dev_id
) break;
1932 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1934 if ((info
[drive
].dev
== st
.st_dev
) && (info
[drive
].ino
== st
.st_ino
))
1936 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
1937 debugstr_a(path
), 'A' + drive
, debugstr_a(buffer
), debugstr_a(path
+ len
));
1943 if (len
<= 1) break; /* reached root */
1944 while (len
> 1 && path
[len
- 1] != '/') len
--;
1945 while (len
> 1 && path
[len
- 1] == '/') len
--;
1952 static NTSTATUS
get_mountmgr_fs_info( HANDLE handle
, int fd
, struct mountmgr_unix_drive
*drive
, ULONG size
)
1954 OBJECT_ATTRIBUTES attr
;
1955 UNICODE_STRING string
;
1962 if ((status
= server_get_unix_name( handle
, &unix_name
))) return status
;
1963 letter
= find_dos_device( unix_name
);
1966 memset( drive
, 0, sizeof(*drive
) );
1972 drive
->unix_dev
= st
.st_rdev
? st
.st_rdev
: st
.st_dev
;
1975 drive
->letter
= 'a' + letter
;
1977 init_unicode_string( &string
, MOUNTMGR_DEVICE_NAME
);
1978 InitializeObjectAttributes( &attr
, &string
, 0, NULL
, NULL
);
1979 status
= NtOpenFile( &mountmgr
, GENERIC_READ
| SYNCHRONIZE
, &attr
, &io
,
1980 FILE_SHARE_READ
| FILE_SHARE_WRITE
, FILE_SYNCHRONOUS_IO_NONALERT
);
1981 if (status
) return status
;
1983 status
= NtDeviceIoControlFile( mountmgr
, NULL
, NULL
, NULL
, &io
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
,
1984 drive
, sizeof(*drive
), drive
, size
);
1985 NtClose( mountmgr
);
1986 if (status
== STATUS_BUFFER_OVERFLOW
) status
= STATUS_SUCCESS
;
1987 else if (status
) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status
);
1992 /***********************************************************************
1993 * get_dir_data_entry
1995 * Return a directory entry from the cached data.
1997 static NTSTATUS
get_dir_data_entry( struct dir_data
*dir_data
, void *info_ptr
, IO_STATUS_BLOCK
*io
,
1998 ULONG max_length
, FILE_INFORMATION_CLASS
class,
1999 union file_directory_info
**last_info
)
2001 const struct dir_data_names
*names
= &dir_data
->names
[dir_data
->pos
];
2002 union file_directory_info
*info
;
2004 ULONG name_len
, start
, dir_size
, attributes
;
2006 if (get_file_info( names
->unix_name
, &st
, &attributes
) == -1)
2008 TRACE( "file no longer exists %s\n", names
->unix_name
);
2009 return STATUS_SUCCESS
;
2011 if (is_ignored_file( &st
))
2013 TRACE( "ignoring file %s\n", names
->unix_name
);
2014 return STATUS_SUCCESS
;
2016 start
= dir_info_align( io
->Information
);
2017 dir_size
= dir_info_size( class, 0 );
2018 if (start
+ dir_size
> max_length
) return STATUS_MORE_ENTRIES
;
2020 max_length
-= start
+ dir_size
;
2021 name_len
= wcslen( names
->long_name
) * sizeof(WCHAR
);
2022 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2023 if (*last_info
&& name_len
> max_length
) return STATUS_MORE_ENTRIES
;
2025 info
= (union file_directory_info
*)((char *)info_ptr
+ start
);
2026 info
->dir
.NextEntryOffset
= 0;
2027 info
->dir
.FileIndex
= 0; /* NTFS always has 0 here, so let's not bother with it */
2029 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2030 if (class != FileNamesInformation
)
2032 if (st
.st_dev
!= dir_data
->id
.dev
) st
.st_ino
= 0; /* ignore inode if on a different device */
2034 if (!show_dot_files
&& names
->long_name
[0] == '.' && names
->long_name
[1] &&
2035 (names
->long_name
[1] != '.' || names
->long_name
[2]))
2036 attributes
|= FILE_ATTRIBUTE_HIDDEN
;
2038 fill_file_info( &st
, attributes
, info
, class );
2043 case FileDirectoryInformation
:
2044 info
->dir
.FileNameLength
= name_len
;
2047 case FileFullDirectoryInformation
:
2048 info
->full
.EaSize
= 0; /* FIXME */
2049 info
->full
.FileNameLength
= name_len
;
2052 case FileIdFullDirectoryInformation
:
2053 info
->id_full
.EaSize
= 0; /* FIXME */
2054 info
->id_full
.FileNameLength
= name_len
;
2057 case FileBothDirectoryInformation
:
2058 info
->both
.EaSize
= 0; /* FIXME */
2059 info
->both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2060 memcpy( info
->both
.ShortName
, names
->short_name
, info
->both
.ShortNameLength
);
2061 info
->both
.FileNameLength
= name_len
;
2064 case FileIdBothDirectoryInformation
:
2065 info
->id_both
.EaSize
= 0; /* FIXME */
2066 info
->id_both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2067 memcpy( info
->id_both
.ShortName
, names
->short_name
, info
->id_both
.ShortNameLength
);
2068 info
->id_both
.FileNameLength
= name_len
;
2071 case FileIdGlobalTxDirectoryInformation
:
2072 info
->id_tx
.TxInfoFlags
= 0;
2073 info
->id_tx
.FileNameLength
= name_len
;
2076 case FileNamesInformation
:
2077 info
->names
.FileNameLength
= name_len
;
2085 memcpy( (char *)info
+ dir_size
, names
->long_name
, min( name_len
, max_length
) );
2086 io
->Information
= start
+ dir_size
+ min( name_len
, max_length
);
2087 if (*last_info
) (*last_info
)->next
= (char *)info
- (char *)*last_info
;
2089 return name_len
> max_length
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
2092 #ifdef VFAT_IOCTL_READDIR_BOTH
2094 /***********************************************************************
2095 * read_directory_vfat
2097 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2099 static NTSTATUS
read_directory_data_vfat( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2101 char *short_name
, *long_name
;
2102 KERNEL_DIRENT de
[2];
2103 NTSTATUS status
= STATUS_NO_MEMORY
;
2104 off_t old_pos
= lseek( fd
, 0, SEEK_CUR
);
2106 lseek( fd
, 0, SEEK_SET
);
2108 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1)
2110 if (errno
!= ENOENT
)
2112 status
= STATUS_NOT_SUPPORTED
;
2118 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2119 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2121 while (de
[0].d_reclen
)
2123 if (strcmp( de
[0].d_name
, "." ) && strcmp( de
[0].d_name
, ".." ))
2125 if (de
[1].d_name
[0])
2127 short_name
= de
[0].d_name
;
2128 long_name
= de
[1].d_name
;
2132 long_name
= de
[0].d_name
;
2135 if (!append_entry( data
, long_name
, short_name
, mask
)) goto done
;
2137 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1) break;
2139 status
= STATUS_SUCCESS
;
2141 lseek( fd
, old_pos
, SEEK_SET
);
2144 #endif /* VFAT_IOCTL_READDIR_BOTH */
2147 #ifdef HAVE_GETATTRLIST
2148 /***********************************************************************
2149 * read_directory_getattrlist
2151 * Read a single file from a directory by determining whether the file
2152 * identified by mask exists using getattrlist.
2154 static NTSTATUS
read_directory_data_getattrlist( struct dir_data
*data
, const char *unix_name
)
2156 struct attrlist attrlist
;
2157 #include "pshpack4.h"
2161 struct attrreference name_reference
;
2163 char name
[NAME_MAX
* 3 + 1];
2165 #include "poppack.h"
2167 memset( &attrlist
, 0, sizeof(attrlist
) );
2168 attrlist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
2169 attrlist
.commonattr
= ATTR_CMN_NAME
| ATTR_CMN_OBJTYPE
;
2170 if (getattrlist( unix_name
, &attrlist
, &buffer
, sizeof(buffer
), FSOPT_NOFOLLOW
) == -1)
2171 return STATUS_NO_SUCH_FILE
;
2172 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2173 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2174 if (buffer
.type
== VLNK
)
2177 attrlist
.commonattr
= 0;
2178 if (getattrlist( unix_name
, &attrlist
, &dummy
, sizeof(dummy
), 0 ) == -1)
2179 return STATUS_NO_SUCH_FILE
;
2182 TRACE( "found %s\n", buffer
.name
);
2184 if (!append_entry( data
, buffer
.name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2186 return STATUS_SUCCESS
;
2188 #endif /* HAVE_GETATTRLIST */
2191 /***********************************************************************
2192 * read_directory_stat
2194 * Read a single file from a directory by determining whether the file
2195 * identified by mask exists using stat.
2197 static NTSTATUS
read_directory_data_stat( struct dir_data
*data
, const char *unix_name
)
2201 /* if the file system is not case sensitive we can't find the actual name through stat() */
2202 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE
;
2203 if (stat( unix_name
, &st
) == -1) return STATUS_NO_SUCH_FILE
;
2205 TRACE( "found %s\n", unix_name
);
2207 if (!append_entry( data
, unix_name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2209 return STATUS_SUCCESS
;
2213 /***********************************************************************
2214 * read_directory_readdir
2216 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2218 static NTSTATUS
read_directory_data_readdir( struct dir_data
*data
, const UNICODE_STRING
*mask
)
2221 NTSTATUS status
= STATUS_NO_MEMORY
;
2222 DIR *dir
= opendir( "." );
2224 if (!dir
) return STATUS_NO_SUCH_FILE
;
2226 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2227 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2228 while ((de
= readdir( dir
)))
2230 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
2231 if (!append_entry( data
, de
->d_name
, NULL
, mask
)) goto done
;
2233 status
= STATUS_SUCCESS
;
2241 /***********************************************************************
2242 * read_directory_data
2244 * Read the full contents of a directory, using one of the above helper functions.
2246 static NTSTATUS
read_directory_data( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2250 #ifdef VFAT_IOCTL_READDIR_BOTH
2251 if (!(status
= read_directory_data_vfat( data
, fd
, mask
))) return status
;
2254 if (!has_wildcard( mask
))
2256 /* convert the mask to a Unix name and check for it */
2257 char unix_name
[MAX_DIR_ENTRY_LEN
* 3 + 1];
2258 int ret
= ntdll_wcstoumbs( mask
->Buffer
, mask
->Length
/ sizeof(WCHAR
),
2259 unix_name
, sizeof(unix_name
) - 1, TRUE
);
2263 #ifdef HAVE_GETATTRLIST
2264 if (!(status
= read_directory_data_getattrlist( data
, unix_name
))) return status
;
2266 if (!(status
= read_directory_data_stat( data
, unix_name
))) return status
;
2270 return read_directory_data_readdir( data
, mask
);
2274 /* compare file names for directory sorting */
2275 static int name_compare( const void *a
, const void *b
)
2277 const struct dir_data_names
*file_a
= (const struct dir_data_names
*)a
;
2278 const struct dir_data_names
*file_b
= (const struct dir_data_names
*)b
;
2279 int ret
= wcsicmp( file_a
->long_name
, file_b
->long_name
);
2280 if (!ret
) ret
= wcscmp( file_a
->long_name
, file_b
->long_name
);
2285 /***********************************************************************
2286 * init_cached_dir_data
2288 * Initialize the cached directory contents.
2290 static NTSTATUS
init_cached_dir_data( struct dir_data
**data_ret
, int fd
, const UNICODE_STRING
*mask
)
2292 struct dir_data
*data
;
2297 if (!(data
= calloc( 1, sizeof(*data
) ))) return STATUS_NO_MEMORY
;
2299 if ((status
= read_directory_data( data
, fd
, mask
)))
2301 free_dir_data( data
);
2305 /* sort filenames, but not "." and ".." */
2307 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, "." )) i
++;
2308 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, ".." )) i
++;
2309 if (i
< data
->count
) qsort( data
->names
+ i
, data
->count
- i
, sizeof(*data
->names
), name_compare
);
2314 data
->id
.dev
= st
.st_dev
;
2315 data
->id
.ino
= st
.st_ino
;
2318 TRACE( "mask %s found %u files\n", debugstr_us( mask
), data
->count
);
2319 for (i
= 0; i
< data
->count
; i
++)
2320 TRACE( "%s %s\n", debugstr_w(data
->names
[i
].long_name
), debugstr_w(data
->names
[i
].short_name
) );
2323 return data
->count
? STATUS_SUCCESS
: STATUS_NO_SUCH_FILE
;
2327 /***********************************************************************
2328 * get_cached_dir_data
2330 * Retrieve the cached directory data, or initialize it if necessary.
2332 static NTSTATUS
get_cached_dir_data( HANDLE handle
, struct dir_data
**data_ret
, int fd
,
2333 const UNICODE_STRING
*mask
)
2336 int entry
= -1, free_entries
[16];
2339 SERVER_START_REQ( get_directory_cache_entry
)
2341 req
->handle
= wine_server_obj_handle( handle
);
2342 wine_server_set_reply( req
, free_entries
, sizeof(free_entries
) );
2343 if (!(status
= wine_server_call( req
))) entry
= reply
->entry
;
2345 for (i
= 0; i
< wine_server_reply_size( reply
) / sizeof(*free_entries
); i
++)
2347 int free_idx
= free_entries
[i
];
2348 if (free_idx
< dir_data_cache_size
)
2350 free_dir_data( dir_data_cache
[free_idx
] );
2351 dir_data_cache
[free_idx
] = NULL
;
2359 if (status
== STATUS_SHARING_VIOLATION
) FIXME( "shared directory handle not supported yet\n" );
2363 if (entry
>= dir_data_cache_size
)
2365 unsigned int size
= max( dir_data_cache_initial_size
, max( dir_data_cache_size
* 2, entry
+ 1 ) );
2366 struct dir_data
**new_cache
= realloc( dir_data_cache
, size
* sizeof(*new_cache
) );
2368 if (!new_cache
) return STATUS_NO_MEMORY
;
2369 memset( new_cache
+ dir_data_cache_size
, 0, (size
- dir_data_cache_size
) * sizeof(*new_cache
) );
2370 dir_data_cache
= new_cache
;
2371 dir_data_cache_size
= size
;
2374 if (!dir_data_cache
[entry
]) status
= init_cached_dir_data( &dir_data_cache
[entry
], fd
, mask
);
2376 *data_ret
= dir_data_cache
[entry
];
2381 /******************************************************************************
2382 * NtQueryDirectoryFile (NTDLL.@)
2384 NTSTATUS WINAPI
NtQueryDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc_routine
,
2385 void *apc_context
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
2386 FILE_INFORMATION_CLASS info_class
, BOOLEAN single_entry
,
2387 UNICODE_STRING
*mask
, BOOLEAN restart_scan
)
2389 int cwd
, fd
, needs_close
;
2390 enum server_fd_type type
;
2391 struct dir_data
*data
;
2394 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2395 handle
, event
, apc_routine
, apc_context
, io
, buffer
,
2396 length
, info_class
, single_entry
, debugstr_us(mask
),
2399 if (event
|| apc_routine
)
2401 FIXME( "Unsupported yet option\n" );
2402 return STATUS_NOT_IMPLEMENTED
;
2406 case FileDirectoryInformation
:
2407 case FileBothDirectoryInformation
:
2408 case FileFullDirectoryInformation
:
2409 case FileIdBothDirectoryInformation
:
2410 case FileIdFullDirectoryInformation
:
2411 case FileIdGlobalTxDirectoryInformation
:
2412 case FileNamesInformation
:
2413 if (length
< dir_info_align( dir_info_size( info_class
, 1 ))) return STATUS_INFO_LENGTH_MISMATCH
;
2415 case FileObjectIdInformation
:
2416 if (length
!= sizeof(FILE_OBJECTID_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2417 return STATUS_INVALID_INFO_CLASS
;
2418 case FileQuotaInformation
:
2419 if (length
!= sizeof(FILE_QUOTA_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2420 return STATUS_INVALID_INFO_CLASS
;
2421 case FileReparsePointInformation
:
2422 if (length
!= sizeof(FILE_REPARSE_POINT_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2423 return STATUS_INVALID_INFO_CLASS
;
2425 return STATUS_INVALID_INFO_CLASS
;
2427 if (!buffer
) return STATUS_ACCESS_VIOLATION
;
2429 if ((status
= server_get_unix_fd( handle
, FILE_LIST_DIRECTORY
, &fd
, &needs_close
, &type
, NULL
)))
2432 if (type
!= FD_TYPE_DIR
)
2434 if (needs_close
) close( fd
);
2435 return STATUS_INVALID_PARAMETER
;
2438 io
->Information
= 0;
2440 mutex_lock( &dir_mutex
);
2442 cwd
= open( ".", O_RDONLY
);
2443 if (fchdir( fd
) != -1)
2445 if (!(status
= get_cached_dir_data( handle
, &data
, fd
, mask
)))
2447 union file_directory_info
*last_info
= NULL
;
2449 if (restart_scan
) data
->pos
= 0;
2451 while (!status
&& data
->pos
< data
->count
)
2453 status
= get_dir_data_entry( data
, buffer
, io
, length
, info_class
, &last_info
);
2454 if (!status
|| status
== STATUS_BUFFER_OVERFLOW
) data
->pos
++;
2455 if (single_entry
&& last_info
) break;
2458 if (!last_info
) status
= STATUS_NO_MORE_FILES
;
2459 else if (status
== STATUS_MORE_ENTRIES
) status
= STATUS_SUCCESS
;
2461 io
->u
.Status
= status
;
2463 if (cwd
== -1 || fchdir( cwd
) == -1) chdir( "/" );
2465 else status
= errno_to_status( errno
);
2467 mutex_unlock( &dir_mutex
);
2469 if (needs_close
) close( fd
);
2470 if (cwd
!= -1) close( cwd
);
2471 TRACE( "=> %x (%ld)\n", status
, io
->Information
);
2476 /***********************************************************************
2479 * Find a file in a directory the hard way, by doing a case-insensitive search.
2480 * The file found is appended to unix_name at pos.
2481 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2483 static NTSTATUS
find_file_in_dir( char *unix_name
, int pos
, const WCHAR
*name
, int length
,
2484 BOOLEAN check_case
, BOOLEAN
*is_win_dir
)
2486 WCHAR buffer
[MAX_DIR_ENTRY_LEN
];
2487 BOOLEAN is_name_8_dot_3
;
2493 /* try a shortcut for this directory */
2495 unix_name
[pos
++] = '/';
2496 ret
= ntdll_wcstoumbs( name
, length
, unix_name
+ pos
, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
2497 if (ret
>= 0 && ret
<= MAX_DIR_ENTRY_LEN
)
2499 unix_name
[pos
+ ret
] = 0;
2500 if (!stat( unix_name
, &st
))
2502 if (is_win_dir
) *is_win_dir
= is_same_file( &windir
, &st
);
2503 return STATUS_SUCCESS
;
2506 if (check_case
) goto not_found
; /* we want an exact match */
2508 if (pos
> 1) unix_name
[pos
- 1] = 0;
2509 else unix_name
[1] = 0; /* keep the initial slash */
2511 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2513 is_name_8_dot_3
= is_legal_8dot3_name( name
, length
);
2514 #ifndef VFAT_IOCTL_READDIR_BOTH
2515 is_name_8_dot_3
= is_name_8_dot_3
&& length
>= 8 && name
[4] == '~';
2518 if (!is_name_8_dot_3
&& !get_dir_case_sensitivity( unix_name
)) goto not_found
;
2520 /* now look for it through the directory */
2522 #ifdef VFAT_IOCTL_READDIR_BOTH
2523 if (is_name_8_dot_3
)
2525 int fd
= open( unix_name
, O_RDONLY
| O_DIRECTORY
);
2528 KERNEL_DIRENT kde
[2];
2530 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) != -1)
2532 unix_name
[pos
- 1] = '/';
2533 while (kde
[0].d_reclen
)
2535 if (kde
[1].d_name
[0])
2537 ret
= ntdll_umbstowcs( kde
[1].d_name
, strlen(kde
[1].d_name
),
2538 buffer
, MAX_DIR_ENTRY_LEN
);
2539 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2541 strcpy( unix_name
+ pos
, kde
[1].d_name
);
2546 ret
= ntdll_umbstowcs( kde
[0].d_name
, strlen(kde
[0].d_name
),
2547 buffer
, MAX_DIR_ENTRY_LEN
);
2548 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2550 strcpy( unix_name
+ pos
,
2551 kde
[1].d_name
[0] ? kde
[1].d_name
: kde
[0].d_name
);
2555 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) == -1)
2561 /* if that did not work, restore previous state of unix_name */
2562 unix_name
[pos
- 1] = 0;
2566 /* fall through to normal handling */
2568 #endif /* VFAT_IOCTL_READDIR_BOTH */
2570 if (!(dir
= opendir( unix_name
))) return errno_to_status( errno
);
2572 unix_name
[pos
- 1] = '/';
2573 while ((de
= readdir( dir
)))
2575 ret
= ntdll_umbstowcs( de
->d_name
, strlen(de
->d_name
), buffer
, MAX_DIR_ENTRY_LEN
);
2576 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2578 strcpy( unix_name
+ pos
, de
->d_name
);
2583 if (!is_name_8_dot_3
) continue;
2585 if (!is_legal_8dot3_name( buffer
, ret
))
2587 WCHAR short_nameW
[12];
2588 ret
= hash_short_file_name( buffer
, ret
, short_nameW
);
2589 if (ret
== length
&& !wcsnicmp( short_nameW
, name
, length
))
2591 strcpy( unix_name
+ pos
, de
->d_name
);
2600 unix_name
[pos
- 1] = 0;
2601 return STATUS_OBJECT_PATH_NOT_FOUND
;
2604 if (is_win_dir
&& !stat( unix_name
, &st
)) *is_win_dir
= is_same_file( &windir
, &st
);
2605 return STATUS_SUCCESS
;
2611 static const WCHAR catrootW
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2612 static const WCHAR catroot2W
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2613 static const WCHAR driversstoreW
[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','s','t','o','r','e',0};
2614 static const WCHAR driversetcW
[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2615 static const WCHAR logfilesW
[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2616 static const WCHAR spoolW
[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2617 static const WCHAR system32W
[] = {'s','y','s','t','e','m','3','2',0};
2618 static const WCHAR sysnativeW
[] = {'s','y','s','n','a','t','i','v','e',0};
2619 static const WCHAR regeditW
[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2623 const WCHAR
*source
;
2624 const char *unix_target
;
2628 { catroot2W
, NULL
},
2629 { driversstoreW
, NULL
},
2630 { driversetcW
, NULL
},
2631 { logfilesW
, NULL
},
2633 { system32W
, "syswow64" },
2634 { sysnativeW
, "system32" },
2635 { regeditW
, "syswow64/regedit.exe" }
2638 static unsigned int nb_redirects
;
2641 /***********************************************************************
2644 static void init_redirects(void)
2646 static const char windows_dir
[] = "/dosdevices/c:/windows";
2650 if (!(dir
= malloc( strlen(config_dir
) + sizeof(windows_dir
) ))) return;
2651 strcpy( dir
, config_dir
);
2652 strcat( dir
, windows_dir
);
2653 if (!stat( dir
, &st
))
2655 windir
.dev
= st
.st_dev
;
2656 windir
.ino
= st
.st_ino
;
2657 nb_redirects
= ARRAY_SIZE( redirects
);
2659 else ERR( "%s: %s\n", dir
, strerror(errno
) );
2665 /***********************************************************************
2668 * Check if path matches a redirect name. If yes, return matched length.
2670 static int match_redirect( const WCHAR
*path
, int len
, const WCHAR
*redir
, BOOLEAN check_case
)
2677 while (i
< len
&& !IS_SEPARATOR(path
[i
])) i
++;
2680 if (wcsncmp( path
+ start
, redir
, i
- start
)) return 0;
2684 if (wcsnicmp( path
+ start
, redir
, i
- start
)) return 0;
2687 while (i
< len
&& IS_SEPARATOR(path
[i
])) i
++;
2688 if (!*redir
) return i
;
2689 if (*redir
++ != '\\') return 0;
2695 /***********************************************************************
2698 * Retrieve the Unix path corresponding to a redirected path if any.
2700 static int get_redirect_path( char *unix_name
, int pos
, const WCHAR
*name
, int length
, BOOLEAN check_case
)
2705 for (i
= 0; i
< nb_redirects
; i
++)
2707 if ((len
= match_redirect( name
, length
, redirects
[i
].source
, check_case
)))
2709 if (!redirects
[i
].unix_target
) break;
2710 unix_name
[pos
++] = '/';
2711 strcpy( unix_name
+ pos
, redirects
[i
].unix_target
);
2720 /* there are no redirects on 64-bit */
2721 static int get_redirect_path( char *unix_name
, int pos
, const WCHAR
*name
, int length
, BOOLEAN check_case
)
2728 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
2730 /***********************************************************************
2733 void init_files(void)
2738 if (is_wow64
) init_redirects();
2740 /* a couple of directories that we don't want to return in directory searches */
2741 ignore_file( config_dir
);
2742 ignore_file( "/dev" );
2743 ignore_file( "/proc" );
2745 ignore_file( "/sys" );
2747 /* retrieve initial umask */
2748 start_umask
= umask( 0777 );
2749 umask( start_umask
);
2751 if (!open_hkcu_key( "Software\\Wine", &key
))
2753 static WCHAR showdotfilesW
[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
2756 UNICODE_STRING nameW
;
2758 init_unicode_string( &nameW
, showdotfilesW
);
2759 if (!NtQueryValueKey( key
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
2761 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
2762 show_dot_files
= IS_OPTION_TRUE( str
[0] );
2769 /******************************************************************************
2772 * Get the Unix path of a DOS device.
2774 static NTSTATUS
get_dos_device( const WCHAR
*name
, UINT name_len
, char **unix_name_ret
)
2777 char *unix_name
, *new_name
, *dev
;
2781 /* make sure the device name is ASCII */
2782 for (i
= 0; i
< name_len
; i
++)
2783 if (name
[i
] <= 32 || name
[i
] >= 127) return STATUS_BAD_DEVICE_TYPE
;
2785 unix_len
= strlen(config_dir
) + sizeof("/dosdevices/") + name_len
+ 1;
2787 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
2789 strcpy( unix_name
, config_dir
);
2790 strcat( unix_name
, "/dosdevices/" );
2791 dev
= unix_name
+ strlen(unix_name
);
2793 for (i
= 0; i
< name_len
; i
++) dev
[i
] = (name
[i
] >= 'A' && name
[i
] <= 'Z' ? name
[i
] + 32 : name
[i
]);
2796 /* special case for drive devices */
2797 if (name_len
== 2 && dev
[1] == ':')
2805 if (!stat( unix_name
, &st
))
2807 TRACE( "%s -> %s\n", debugstr_wn(name
,name_len
), debugstr_a(unix_name
) );
2808 *unix_name_ret
= unix_name
;
2809 return STATUS_SUCCESS
;
2813 /* now try some defaults for it */
2814 if (!strcmp( dev
, "aux" ))
2816 strcpy( dev
, "com1" );
2819 if (!strcmp( dev
, "prn" ))
2821 strcpy( dev
, "lpt1" );
2826 if (dev
[1] == ':' && dev
[2] == ':') /* drive device */
2828 dev
[2] = 0; /* remove last ':' to get the drive mount point symlink */
2829 new_name
= get_default_drive_device( unix_name
);
2832 if (!new_name
) break;
2834 unix_name
= new_name
;
2835 dev
= NULL
; /* last try */
2838 return STATUS_BAD_DEVICE_TYPE
;
2842 /* return the length of the DOS namespace prefix if any */
2843 static inline int get_dos_prefix_len( const UNICODE_STRING
*name
)
2845 static const WCHAR nt_prefixW
[] = {'\\','?','?','\\'};
2846 static const WCHAR dosdev_prefixW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2848 if (name
->Length
>= sizeof(nt_prefixW
) &&
2849 !memcmp( name
->Buffer
, nt_prefixW
, sizeof(nt_prefixW
) ))
2850 return ARRAY_SIZE( nt_prefixW
);
2852 if (name
->Length
>= sizeof(dosdev_prefixW
) &&
2853 !wcsnicmp( name
->Buffer
, dosdev_prefixW
, ARRAY_SIZE( dosdev_prefixW
)))
2854 return ARRAY_SIZE( dosdev_prefixW
);
2860 /***********************************************************************
2861 * remove_last_componentA
2863 * Remove the last component of the path. Helper for find_drive_rootA.
2865 static inline unsigned int remove_last_componentA( const char *path
, unsigned int len
)
2871 /* find start of the last path component */
2872 unsigned int prev
= len
;
2873 if (prev
<= 1) break; /* reached root */
2874 while (prev
> 1 && path
[prev
- 1] != '/') prev
--;
2875 /* does removing it take us up a level? */
2876 if (len
- prev
!= 1 || path
[prev
] != '.') /* not '.' */
2878 if (len
- prev
== 2 && path
[prev
] == '.' && path
[prev
+1] == '.') /* is it '..'? */
2883 /* strip off trailing slashes */
2884 while (prev
> 1 && path
[prev
- 1] == '/') prev
--;
2891 /***********************************************************************
2894 * Find a drive for which the root matches the beginning of the given path.
2895 * This can be used to translate a Unix path into a drive + DOS path.
2896 * Return value is the drive, or -1 on error. On success, ppath is modified
2897 * to point to the beginning of the DOS path.
2899 static NTSTATUS
find_drive_rootA( LPCSTR
*ppath
, unsigned int len
, int *drive_ret
)
2901 /* Starting with the full path, check if the device and inode match any of
2902 * the wine 'drives'. If not then remove the last path component and try
2903 * again. If the last component was a '..' then skip a normal component
2904 * since it's a directory that's ascended back out of.
2908 const char *path
= *ppath
;
2910 struct file_identity info
[MAX_DOS_DRIVES
];
2912 /* get device and inode of all drives */
2913 if (!get_drives_info( info
)) return STATUS_OBJECT_PATH_NOT_FOUND
;
2915 /* strip off trailing slashes */
2916 while (len
> 1 && path
[len
- 1] == '/') len
--;
2918 /* make a copy of the path */
2919 if (!(buffer
= malloc( len
+ 1 ))) return STATUS_NO_MEMORY
;
2920 memcpy( buffer
, path
, len
);
2925 if (!stat( buffer
, &st
) && S_ISDIR( st
.st_mode
))
2927 /* Find the drive */
2928 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
2930 if ((info
[drive
].dev
== st
.st_dev
) && (info
[drive
].ino
== st
.st_ino
))
2932 if (len
== 1) len
= 0; /* preserve root slash in returned path */
2933 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2934 debugstr_a(path
), 'A' + drive
, debugstr_a(buffer
), debugstr_a(path
+ len
));
2938 return STATUS_SUCCESS
;
2942 if (len
<= 1) break; /* reached root */
2943 len
= remove_last_componentA( buffer
, len
);
2947 return STATUS_OBJECT_PATH_NOT_FOUND
;
2951 /******************************************************************************
2954 static void rebuild_nt_name( const UNICODE_STRING
*nameW
, DWORD prefix_len
,
2955 const char *unix_name
, UNICODE_STRING
*nt_name
)
2960 while (*unix_name
== '/') unix_name
++;
2961 nt_name
->MaximumLength
= (prefix_len
+ strlen(unix_name
) + 2) * sizeof(WCHAR
);
2962 if (!(buf
= malloc( nt_name
->MaximumLength
))) return;
2963 nt_name
->Buffer
= buf
;
2964 memcpy( buf
, nameW
->Buffer
, prefix_len
* sizeof(WCHAR
) );
2965 if (prefix_len
&& buf
[prefix_len
- 1] != '\\') buf
[prefix_len
++] = '\\';
2967 len
= ntdll_umbstowcs( unix_name
, strlen(unix_name
), buf
, strlen(unix_name
) );
2968 for (; len
; len
--, buf
++) if (*buf
== '/') *buf
= '\\';
2970 nt_name
->Length
= (buf
- nt_name
->Buffer
) * sizeof(WCHAR
);
2974 /******************************************************************************
2977 * Recursively search directories from the dir queue for a given inode.
2979 static NTSTATUS
find_file_id( char **unix_name
, ULONG
*len
, ULONGLONG file_id
, dev_t dev
)
2986 char *name
= *unix_name
;
2988 while (!(status
= next_dir_in_queue( name
)))
2990 if (!(dir
= opendir( name
))) continue;
2991 TRACE( "searching %s for %s\n", debugstr_a(name
), wine_dbgstr_longlong(file_id
) );
2992 pos
= strlen( name
);
2993 if (pos
+ MAX_DIR_ENTRY_LEN
>= *len
/ sizeof(WCHAR
))
2995 if (!(name
= realloc( name
, *len
* 2 )))
2998 return STATUS_NO_MEMORY
;
3004 while ((de
= readdir( dir
)))
3006 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
3007 strcpy( name
+ pos
, de
->d_name
);
3008 if (lstat( name
, &st
) == -1) continue;
3009 if (st
.st_dev
!= dev
) continue;
3010 if (st
.st_ino
== file_id
)
3013 return STATUS_SUCCESS
;
3015 if (!S_ISDIR( st
.st_mode
)) continue;
3016 if ((status
= add_dir_to_queue( name
)) != STATUS_SUCCESS
)
3028 /******************************************************************************
3029 * file_id_to_unix_file_name
3031 * Lookup a file from its file id instead of its name.
3033 static NTSTATUS
file_id_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char **unix_name_ret
,
3034 UNICODE_STRING
*nt_name
)
3036 enum server_fd_type type
;
3037 int old_cwd
, root_fd
, needs_close
;
3042 struct stat st
, root_st
;
3044 if (attr
->ObjectName
->Length
!= sizeof(ULONGLONG
)) return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3045 if (!attr
->RootDirectory
) return STATUS_INVALID_PARAMETER
;
3046 memcpy( &file_id
, attr
->ObjectName
->Buffer
, sizeof(file_id
) );
3048 len
= 2 * MAX_DIR_ENTRY_LEN
+ 4;
3049 if (!(unix_name
= malloc( len
))) return STATUS_NO_MEMORY
;
3050 strcpy( unix_name
, "." );
3052 if ((status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3055 if (type
!= FD_TYPE_DIR
)
3057 status
= STATUS_OBJECT_TYPE_MISMATCH
;
3061 fstat( root_fd
, &root_st
);
3062 if (root_st
.st_ino
== file_id
) /* shortcut for "." */
3064 status
= STATUS_SUCCESS
;
3068 mutex_lock( &dir_mutex
);
3069 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3071 /* shortcut for ".." */
3072 if (!stat( "..", &st
) && st
.st_dev
== root_st
.st_dev
&& st
.st_ino
== file_id
)
3074 strcpy( unix_name
, ".." );
3075 status
= STATUS_SUCCESS
;
3079 status
= add_dir_to_queue( "." );
3081 status
= find_file_id( &unix_name
, &len
, file_id
, root_st
.st_dev
);
3082 if (!status
) /* get rid of "./" prefix */
3083 memmove( unix_name
, unix_name
+ 2, strlen(unix_name
) - 1 );
3086 if (fchdir( old_cwd
) == -1) chdir( "/" );
3088 else status
= errno_to_status( errno
);
3089 mutex_unlock( &dir_mutex
);
3090 if (old_cwd
!= -1) close( old_cwd
);
3093 if (status
== STATUS_SUCCESS
)
3095 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id
), debugstr_a(unix_name
) );
3096 *unix_name_ret
= unix_name
;
3097 if (nt_name
) rebuild_nt_name( attr
->ObjectName
, 0, unix_name
, nt_name
);
3101 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id
), attr
->RootDirectory
);
3104 if (needs_close
) close( root_fd
);
3109 /******************************************************************************
3112 * Helper for nt_to_unix_file_name
3114 static NTSTATUS
lookup_unix_name( const WCHAR
*name
, int name_len
, char **buffer
, int unix_len
, int pos
,
3115 UINT disposition
, BOOLEAN check_case
)
3120 char *unix_name
= *buffer
;
3121 const BOOL redirect
= NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
];
3123 /* try a shortcut first */
3125 while (name_len
&& IS_SEPARATOR(*name
))
3131 unix_name
[pos
] = '/';
3132 ret
= ntdll_wcstoumbs( name
, name_len
, unix_name
+ pos
+ 1, unix_len
- pos
- 1, TRUE
);
3133 if (ret
>= 0 && ret
< unix_len
- pos
- 1)
3136 unix_name
[pos
+ 1 + ret
] = 0;
3137 for (p
= unix_name
+ pos
; *p
; p
++) if (*p
== '\\') *p
= '/';
3138 if (!name_len
|| !redirect
|| (!strstr( unix_name
, "/windows/") && strncmp( unix_name
, "windows/", 8 )))
3140 if (!stat( unix_name
, &st
))
3142 if (disposition
== FILE_CREATE
)
3143 return STATUS_OBJECT_NAME_COLLISION
;
3144 return STATUS_SUCCESS
;
3149 if (!name_len
) /* empty name -> drive root doesn't exist */
3150 return STATUS_OBJECT_PATH_NOT_FOUND
;
3151 if (check_case
&& !redirect
&& (disposition
== FILE_OPEN
|| disposition
== FILE_OVERWRITE
))
3152 return STATUS_OBJECT_NAME_NOT_FOUND
;
3154 /* now do it component by component */
3158 const WCHAR
*end
, *next
;
3159 BOOLEAN is_win_dir
= FALSE
;
3162 while (end
< name
+ name_len
&& !IS_SEPARATOR(*end
)) end
++;
3164 while (next
< name
+ name_len
&& IS_SEPARATOR(*next
)) next
++;
3165 name_len
-= next
- name
;
3167 /* grow the buffer if needed */
3169 if (unix_len
- pos
< MAX_DIR_ENTRY_LEN
+ 2)
3172 unix_len
+= 2 * MAX_DIR_ENTRY_LEN
;
3173 if (!(new_name
= realloc( unix_name
, unix_len
))) return STATUS_NO_MEMORY
;
3174 unix_name
= *buffer
= new_name
;
3177 status
= find_file_in_dir( unix_name
, pos
, name
, end
- name
,
3178 check_case
, redirect
? &is_win_dir
: NULL
);
3180 /* if this is the last element, not finding it is not necessarily fatal */
3183 if (status
== STATUS_OBJECT_PATH_NOT_FOUND
)
3185 status
= STATUS_OBJECT_NAME_NOT_FOUND
;
3186 if (disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
3188 ret
= ntdll_wcstoumbs( name
, end
- name
, unix_name
+ pos
+ 1, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
3189 if (ret
> 0 && ret
<= MAX_DIR_ENTRY_LEN
)
3191 unix_name
[pos
] = '/';
3192 unix_name
[pos
+ 1 + ret
] = 0;
3193 status
= STATUS_NO_SUCH_FILE
;
3198 else if (status
== STATUS_SUCCESS
&& disposition
== FILE_CREATE
)
3200 status
= STATUS_OBJECT_NAME_COLLISION
;
3204 if (status
!= STATUS_SUCCESS
) break;
3206 pos
+= strlen( unix_name
+ pos
);
3209 if (is_win_dir
&& (len
= get_redirect_path( unix_name
, pos
, name
, name_len
, check_case
)))
3213 pos
+= strlen( unix_name
+ pos
);
3214 TRACE( "redirecting -> %s + %s\n", debugstr_a(unix_name
), debugstr_w(name
) );
3222 /******************************************************************************
3223 * nt_to_unix_file_name_attr
3225 static NTSTATUS
nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES
*attr
, char **name_ret
,
3226 UNICODE_STRING
*nt_name
, UINT disposition
)
3228 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, 0 };
3229 enum server_fd_type type
;
3230 int old_cwd
, root_fd
, needs_close
;
3231 const WCHAR
*name
, *p
;
3233 int name_len
, unix_len
;
3236 if (!attr
->RootDirectory
) /* without root dir fall back to normal lookup */
3237 return nt_to_unix_file_name( attr
->ObjectName
, name_ret
, nt_name
, disposition
);
3239 name
= attr
->ObjectName
->Buffer
;
3240 name_len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
3242 if (name_len
&& IS_SEPARATOR(name
[0])) return STATUS_INVALID_PARAMETER
;
3244 /* check for invalid characters */
3245 for (p
= name
; p
< name
+ name_len
; p
++)
3246 if (*p
< 32 || wcschr( invalid_charsW
, *p
)) return STATUS_OBJECT_NAME_INVALID
;
3248 unix_len
= name_len
* 3 + MAX_DIR_ENTRY_LEN
+ 3;
3249 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3252 if (!(status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3254 if (type
!= FD_TYPE_DIR
)
3256 if (needs_close
) close( root_fd
);
3257 status
= STATUS_BAD_DEVICE_TYPE
;
3261 mutex_lock( &dir_mutex
);
3262 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3264 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, 1,
3265 disposition
, FALSE
);
3266 if (fchdir( old_cwd
) == -1) chdir( "/" );
3268 else status
= errno_to_status( errno
);
3269 mutex_unlock( &dir_mutex
);
3270 if (old_cwd
!= -1) close( old_cwd
);
3271 if (needs_close
) close( root_fd
);
3274 else if (status
== STATUS_OBJECT_TYPE_MISMATCH
) status
= STATUS_BAD_DEVICE_TYPE
;
3276 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3278 TRACE( "%s -> %s\n", debugstr_us(attr
->ObjectName
), debugstr_a(unix_name
) );
3279 *name_ret
= unix_name
;
3280 if (nt_name
) rebuild_nt_name( attr
->ObjectName
, 0, unix_name
, nt_name
);
3284 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
3291 /******************************************************************************
3292 * nt_to_unix_file_name
3294 * Convert a file name from NT namespace to Unix namespace.
3296 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3297 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3298 * returned, but the unix name is still filled in properly.
3300 NTSTATUS
nt_to_unix_file_name( const UNICODE_STRING
*nameW
, char **unix_name_ret
,
3301 UNICODE_STRING
*nt_name
, UINT disposition
)
3303 static const WCHAR unixW
[] = {'u','n','i','x'};
3304 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, 0 };
3306 NTSTATUS status
= STATUS_SUCCESS
;
3307 const WCHAR
*name
, *p
;
3310 int pos
, ret
, name_len
, unix_len
, prefix_len
;
3311 WCHAR prefix
[MAX_DIR_ENTRY_LEN
+ 1];
3312 BOOLEAN check_case
= FALSE
;
3313 BOOLEAN is_unix
= FALSE
;
3315 name
= nameW
->Buffer
;
3316 name_len
= nameW
->Length
/ sizeof(WCHAR
);
3318 if (!name_len
|| !IS_SEPARATOR(name
[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3320 if (!(pos
= get_dos_prefix_len( nameW
)))
3321 return STATUS_BAD_DEVICE_TYPE
; /* no DOS prefix, assume NT native name */
3326 if (!name_len
) return STATUS_OBJECT_NAME_INVALID
;
3328 /* check for sub-directory */
3329 for (pos
= 0; pos
< name_len
&& pos
<= MAX_DIR_ENTRY_LEN
; pos
++)
3331 if (IS_SEPARATOR(name
[pos
])) break;
3332 if (name
[pos
] < 32 || wcschr( invalid_charsW
, name
[pos
] ))
3333 return STATUS_OBJECT_NAME_INVALID
;
3334 prefix
[pos
] = (name
[pos
] >= 'A' && name
[pos
] <= 'Z') ? name
[pos
] + 'a' - 'A' : name
[pos
];
3336 if (pos
> MAX_DIR_ENTRY_LEN
) return STATUS_OBJECT_NAME_INVALID
;
3338 if (pos
== name_len
) /* no subdir, plain DOS device */
3339 return get_dos_device( name
, name_len
, unix_name_ret
);
3342 prefix
[prefix_len
] = 0;
3345 name_len
-= prefix_len
;
3347 /* check for invalid characters (all chars except 0 are valid for unix) */
3348 is_unix
= (prefix_len
== 4 && !memcmp( prefix
, unixW
, sizeof(unixW
) ));
3351 for (p
= name
; p
< name
+ name_len
; p
++)
3352 if (!*p
) return STATUS_OBJECT_NAME_INVALID
;
3357 for (p
= name
; p
< name
+ name_len
; p
++)
3358 if (*p
< 32 || wcschr( invalid_charsW
, *p
)) return STATUS_OBJECT_NAME_INVALID
;
3361 unix_len
= (prefix_len
+ name_len
) * 3 + MAX_DIR_ENTRY_LEN
+ 3;
3362 unix_len
+= strlen(config_dir
) + sizeof("/dosdevices/");
3363 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3364 strcpy( unix_name
, config_dir
);
3365 strcat( unix_name
, "/dosdevices/" );
3366 pos
= strlen(unix_name
);
3368 ret
= ntdll_wcstoumbs( prefix
, prefix_len
, unix_name
+ pos
, unix_len
- pos
- 1, TRUE
);
3372 return STATUS_OBJECT_NAME_INVALID
;
3376 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3378 if (prefix_len
!= 2 || prefix
[1] != ':')
3381 if (lstat( unix_name
, &st
) == -1 && errno
== ENOENT
)
3386 return STATUS_BAD_DEVICE_TYPE
;
3388 pos
= 0; /* fall back to unix root */
3392 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, pos
, disposition
, check_case
);
3393 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3395 TRACE( "%s -> %s\n", debugstr_us(nameW
), debugstr_a(unix_name
) );
3396 *unix_name_ret
= unix_name
;
3397 if (nt_name
) rebuild_nt_name( nameW
, name
- nameW
->Buffer
, unix_name
+ pos
, nt_name
);
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
;
3421 NTSTATUS status
= nt_to_unix_file_name( nameW
, &buffer
, NULL
, disposition
);
3425 if (*size
> strlen(buffer
)) strcpy( nameA
, buffer
);
3426 else status
= STATUS_BUFFER_TOO_SMALL
;
3427 *size
= strlen(buffer
) + 1;
3434 /******************************************************************
3437 * Get rid of . and .. components in the path.
3439 static void collapse_path( WCHAR
*path
)
3441 WCHAR
*p
, *start
, *next
;
3443 /* convert every / into a \ */
3444 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
3447 while (*p
&& *p
!= '\\') p
++;
3450 /* collapse duplicate backslashes */
3452 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
3462 case '\\': /* .\ component */
3464 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3466 case 0: /* final . */
3471 if (p
[2] == '\\') /* ..\ component */
3477 while (p
> start
&& p
[-1] != '\\') p
--;
3479 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3482 else if (!p
[2]) /* final .. */
3487 while (p
> start
&& p
[-1] != '\\') p
--;
3496 /* skip to the next component */
3497 while (*p
&& *p
!= '\\') p
++;
3500 /* remove last dot in previous dir name */
3501 if (p
> start
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
3506 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3507 while (p
> start
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
3512 /******************************************************************
3513 * unix_to_nt_file_name
3515 NTSTATUS
unix_to_nt_file_name( const char *name
, WCHAR
**nt
)
3517 static const WCHAR unix_prefixW
[] = {'\\','?','?','\\','u','n','i','x',0};
3518 WCHAR dos_prefixW
[] = {'\\','?','?','\\','A',':','\\',0};
3519 const WCHAR
*prefix
= unix_prefixW
;
3520 unsigned int lenW
, lenA
= strlen(name
);
3521 const char *path
= name
;
3526 status
= find_drive_rootA( &path
, lenA
, &drive
);
3527 lenA
-= path
- name
;
3529 if (status
== STATUS_SUCCESS
)
3531 while (lenA
&& path
[0] == '/') { lenA
--; path
++; }
3532 dos_prefixW
[4] += drive
;
3533 prefix
= dos_prefixW
;
3535 else if (status
!= STATUS_OBJECT_PATH_NOT_FOUND
) return status
;
3537 lenW
= wcslen( prefix
);
3538 if (!(buffer
= malloc( (lenA
+ lenW
+ 1) * sizeof(WCHAR
) ))) return STATUS_NO_MEMORY
;
3539 memcpy( buffer
, prefix
, lenW
* sizeof(WCHAR
) );
3540 lenW
+= ntdll_umbstowcs( path
, lenA
, buffer
+ lenW
, lenA
);
3542 collapse_path( buffer
);
3544 return STATUS_SUCCESS
;
3548 /******************************************************************
3549 * wine_unix_to_nt_file_name
3551 NTSTATUS CDECL
wine_unix_to_nt_file_name( const char *name
, WCHAR
*buffer
, SIZE_T
*size
)
3553 WCHAR
*nt_name
= NULL
;
3556 if (name
[0] != '/') return STATUS_INVALID_PARAMETER
; /* relative paths are not supported */
3558 status
= unix_to_nt_file_name( name
, &nt_name
);
3561 if (*size
> wcslen(nt_name
)) wcscpy( buffer
, nt_name
);
3562 else status
= STATUS_BUFFER_TOO_SMALL
;
3563 *size
= wcslen(nt_name
) + 1;
3570 /***********************************************************************
3573 * Simplified version of RtlGetFullPathName_U.
3575 NTSTATUS
get_full_path( const WCHAR
*name
, const WCHAR
*curdir
, WCHAR
**path
)
3577 static const WCHAR uncW
[] = {'\\','?','?','\\','U','N','C','\\',0};
3578 static const WCHAR devW
[] = {'\\','?','?','\\',0};
3579 static const WCHAR unixW
[] = {'u','n','i','x'};
3580 WCHAR
*ret
, root
[] = {'\\','?','?','\\','C',':','\\',0};
3581 NTSTATUS status
= STATUS_SUCCESS
;
3582 const WCHAR
*prefix
;
3584 if (IS_SEPARATOR(name
[0]) && IS_SEPARATOR(name
[1])) /* \\ prefix */
3586 if ((name
[2] == '.' || name
[2] == '?') && IS_SEPARATOR(name
[3])) /* \\?\ device */
3589 if (!wcsnicmp( name
, unixW
, 4 ) && IS_SEPARATOR(name
[4])) /* \\?\unix special name */
3593 unix_name
= malloc( wcslen(name
) * 3 + 1 );
3594 ntdll_wcstoumbs( name
, wcslen(name
) + 1, unix_name
, wcslen(name
) * 3 + 1, FALSE
);
3595 status
= unix_to_nt_file_name( unix_name
, path
);
3601 else prefix
= uncW
; /* UNC path */
3603 else if (IS_SEPARATOR(name
[0])) /* absolute path */
3605 root
[4] = curdir
[0];
3608 else if (name
[0] && name
[1] == ':') /* drive letter */
3610 root
[4] = towupper(name
[0]);
3614 else prefix
= curdir
; /* relative path */
3616 ret
= malloc( (wcslen(prefix
) + wcslen(name
) + 1) * sizeof(WCHAR
) );
3617 wcscpy( ret
, prefix
);
3618 wcscat( ret
, name
);
3619 collapse_path( ret
);
3621 return STATUS_SUCCESS
;
3625 /***********************************************************************
3628 * Unmount the specified device.
3630 static NTSTATUS
unmount_device( HANDLE handle
)
3633 int unix_fd
, needs_close
;
3635 if (!(status
= server_get_unix_fd( handle
, 0, &unix_fd
, &needs_close
, NULL
, NULL
)))
3638 char *mount_point
= NULL
;
3640 if (fstat( unix_fd
, &st
) == -1 || !is_valid_mounted_device( &st
))
3641 status
= STATUS_INVALID_PARAMETER
;
3644 if ((mount_point
= get_device_mount_point( st
.st_rdev
)))
3647 static const char umount
[] = "diskutil unmount >/dev/null 2>&1 ";
3649 static const char umount
[] = "umount >/dev/null 2>&1 ";
3651 char *cmd
= malloc( strlen(mount_point
)+sizeof(umount
));
3654 strcpy( cmd
, umount
);
3655 strcat( cmd
, mount_point
);
3659 /* umount will fail to release the loop device since we still have
3660 a handle to it, so we release it here */
3661 if (major(st
.st_rdev
) == LOOP_MAJOR
) ioctl( unix_fd
, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3664 free( mount_point
);
3667 if (needs_close
) close( unix_fd
);
3673 /******************************************************************************
3676 * Helper for NtCreateFile that takes a Unix path.
3678 NTSTATUS
open_unix_file( HANDLE
*handle
, const char *unix_name
, ACCESS_MASK access
,
3679 OBJECT_ATTRIBUTES
*attr
, ULONG attributes
, ULONG sharing
, ULONG disposition
,
3680 ULONG options
, void *ea_buffer
, ULONG ea_length
)
3682 struct object_attributes
*objattr
;
3686 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
3688 SERVER_START_REQ( create_file
)
3690 req
->access
= access
;
3691 req
->sharing
= sharing
;
3692 req
->create
= disposition
;
3693 req
->options
= options
;
3694 req
->attrs
= attributes
;
3695 wine_server_add_data( req
, objattr
, len
);
3696 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
3697 status
= wine_server_call( req
);
3698 *handle
= wine_server_ptr_handle( reply
->handle
);
3706 /******************************************************************************
3707 * NtCreateFile (NTDLL.@)
3709 NTSTATUS WINAPI
NtCreateFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
3710 IO_STATUS_BLOCK
*io
, LARGE_INTEGER
*alloc_size
,
3711 ULONG attributes
, ULONG sharing
, ULONG disposition
,
3712 ULONG options
, void *ea_buffer
, ULONG ea_length
)
3714 UNICODE_STRING nt_name
= { 0 };
3716 BOOL created
= FALSE
;
3718 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
3719 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
3720 handle
, access
, debugstr_us(attr
->ObjectName
), attr
->Attributes
,
3721 attr
->RootDirectory
, attr
->SecurityDescriptor
, io
, alloc_size
,
3722 attributes
, sharing
, disposition
, options
, ea_buffer
, ea_length
);
3724 if (!attr
|| !attr
->ObjectName
) return STATUS_INVALID_PARAMETER
;
3726 if (alloc_size
) FIXME( "alloc_size not supported\n" );
3728 if (options
& FILE_OPEN_BY_FILE_ID
)
3729 io
->u
.Status
= file_id_to_unix_file_name( attr
, &unix_name
, &nt_name
);
3731 io
->u
.Status
= nt_to_unix_file_name_attr( attr
, &unix_name
, &nt_name
, disposition
);
3733 if (io
->u
.Status
== STATUS_BAD_DEVICE_TYPE
)
3735 SERVER_START_REQ( open_file_object
)
3737 req
->access
= access
;
3738 req
->attributes
= attr
->Attributes
;
3739 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
3740 req
->sharing
= sharing
;
3741 req
->options
= options
;
3742 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
3743 io
->u
.Status
= wine_server_call( req
);
3744 *handle
= wine_server_ptr_handle( reply
->handle
);
3747 if (io
->u
.Status
== STATUS_SUCCESS
) io
->Information
= FILE_OPENED
;
3748 return io
->u
.Status
;
3751 if (io
->u
.Status
== STATUS_NO_SUCH_FILE
&& disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
3754 io
->u
.Status
= STATUS_SUCCESS
;
3757 if (io
->u
.Status
== STATUS_SUCCESS
)
3759 OBJECT_ATTRIBUTES nt_attr
= *attr
;
3761 if (nt_name
.Buffer
) nt_attr
.ObjectName
= &nt_name
;
3762 io
->u
.Status
= open_unix_file( handle
, unix_name
, access
, &nt_attr
, attributes
,
3763 sharing
, disposition
, options
, ea_buffer
, ea_length
);
3764 free( nt_name
.Buffer
);
3767 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), io
->u
.Status
);
3769 if (io
->u
.Status
== STATUS_SUCCESS
)
3771 if (created
) io
->Information
= FILE_CREATED
;
3772 else switch(disposition
)
3774 case FILE_SUPERSEDE
:
3775 io
->Information
= FILE_SUPERSEDED
;
3778 io
->Information
= FILE_CREATED
;
3782 io
->Information
= FILE_OPENED
;
3784 case FILE_OVERWRITE
:
3785 case FILE_OVERWRITE_IF
:
3786 io
->Information
= FILE_OVERWRITTEN
;
3790 else if (io
->u
.Status
== STATUS_TOO_MANY_OPENED_FILES
)
3793 if (!once
++) ERR_(winediag
)( "Too many open files, ulimit -n probably needs to be increased\n" );
3796 return io
->u
.Status
;
3800 /******************************************************************************
3801 * NtOpenFile (NTDLL.@)
3803 NTSTATUS WINAPI
NtOpenFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
3804 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG options
)
3806 return NtCreateFile( handle
, access
, attr
, io
, NULL
, 0, sharing
, FILE_OPEN
, options
, NULL
, 0 );
3810 /******************************************************************************
3811 * NtCreateMailslotFile (NTDLL.@)
3813 NTSTATUS WINAPI
NtCreateMailslotFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
3814 IO_STATUS_BLOCK
*io
, ULONG options
, ULONG quota
, ULONG msg_size
,
3815 LARGE_INTEGER
*timeout
)
3819 struct object_attributes
*objattr
;
3821 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
3822 handle
, access
, attr
, io
, options
, quota
, msg_size
, timeout
);
3824 if (!handle
) return STATUS_ACCESS_VIOLATION
;
3825 if (!attr
) return STATUS_INVALID_PARAMETER
;
3827 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
3829 SERVER_START_REQ( create_mailslot
)
3831 req
->access
= access
;
3832 req
->max_msgsize
= msg_size
;
3833 req
->read_timeout
= timeout
? timeout
->QuadPart
: -1;
3834 wine_server_add_data( req
, objattr
, len
);
3835 if (!(status
= wine_server_call( req
))) *handle
= wine_server_ptr_handle( reply
->handle
);
3844 /******************************************************************
3845 * NtCreateNamedPipeFile (NTDLL.@)
3847 NTSTATUS WINAPI
NtCreateNamedPipeFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
3848 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG dispo
, ULONG options
,
3849 ULONG pipe_type
, ULONG read_mode
, ULONG completion_mode
,
3850 ULONG max_inst
, ULONG inbound_quota
, ULONG outbound_quota
,
3851 LARGE_INTEGER
*timeout
)
3855 struct object_attributes
*objattr
;
3857 if (!attr
) return STATUS_INVALID_PARAMETER
;
3859 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
3860 handle
, access
, debugstr_us(attr
->ObjectName
), io
, sharing
, dispo
,
3861 options
, pipe_type
, read_mode
, completion_mode
, max_inst
, inbound_quota
,
3862 outbound_quota
, timeout
);
3864 /* assume we only get relative timeout */
3865 if (timeout
->QuadPart
> 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout
->QuadPart
) );
3867 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
3869 SERVER_START_REQ( create_named_pipe
)
3871 req
->access
= access
;
3872 req
->options
= options
;
3873 req
->sharing
= sharing
;
3875 (pipe_type
? NAMED_PIPE_MESSAGE_STREAM_WRITE
: 0) |
3876 (read_mode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0) |
3877 (completion_mode
? NAMED_PIPE_NONBLOCKING_MODE
: 0);
3878 req
->maxinstances
= max_inst
;
3879 req
->outsize
= outbound_quota
;
3880 req
->insize
= inbound_quota
;
3881 req
->timeout
= timeout
->QuadPart
;
3882 wine_server_add_data( req
, objattr
, len
);
3883 if (!(status
= wine_server_call( req
))) *handle
= wine_server_ptr_handle( reply
->handle
);
3892 /******************************************************************
3893 * NtDeleteFile (NTDLL.@)
3895 NTSTATUS WINAPI
NtDeleteFile( OBJECT_ATTRIBUTES
*attr
)
3901 status
= NtCreateFile( &handle
, GENERIC_READ
| GENERIC_WRITE
| DELETE
, attr
, &io
, NULL
, 0,
3902 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, FILE_OPEN
,
3903 FILE_DELETE_ON_CLOSE
, NULL
, 0 );
3904 if (status
== STATUS_SUCCESS
) NtClose( handle
);
3909 /******************************************************************************
3910 * NtQueryFullAttributesFile (NTDLL.@)
3912 NTSTATUS WINAPI
NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES
*attr
,
3913 FILE_NETWORK_OPEN_INFORMATION
*info
)
3918 if (!(status
= nt_to_unix_file_name_attr( attr
, &unix_name
, NULL
, FILE_OPEN
)))
3923 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
3924 status
= errno_to_status( errno
);
3925 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
3926 status
= STATUS_INVALID_INFO_CLASS
;
3929 FILE_BASIC_INFORMATION basic
;
3930 FILE_STANDARD_INFORMATION std
;
3932 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
3933 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
3935 info
->CreationTime
= basic
.CreationTime
;
3936 info
->LastAccessTime
= basic
.LastAccessTime
;
3937 info
->LastWriteTime
= basic
.LastWriteTime
;
3938 info
->ChangeTime
= basic
.ChangeTime
;
3939 info
->AllocationSize
= std
.AllocationSize
;
3940 info
->EndOfFile
= std
.EndOfFile
;
3941 info
->FileAttributes
= basic
.FileAttributes
;
3942 if (is_hidden_file( attr
->ObjectName
)) info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
3946 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
3951 /******************************************************************************
3952 * NtQueryAttributesFile (NTDLL.@)
3954 NTSTATUS WINAPI
NtQueryAttributesFile( const OBJECT_ATTRIBUTES
*attr
, FILE_BASIC_INFORMATION
*info
)
3959 if (!(status
= nt_to_unix_file_name_attr( attr
, &unix_name
, NULL
, FILE_OPEN
)))
3964 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
3965 status
= errno_to_status( errno
);
3966 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
3967 status
= STATUS_INVALID_INFO_CLASS
;
3970 status
= fill_file_info( &st
, attributes
, info
, FileBasicInformation
);
3971 if (is_hidden_file( attr
->ObjectName
)) info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
3975 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
3980 /******************************************************************************
3981 * NtQueryInformationFile (NTDLL.@)
3983 NTSTATUS WINAPI
NtQueryInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
3984 void *ptr
, LONG len
, FILE_INFORMATION_CLASS
class )
3986 static const size_t info_sizes
[] =
3989 sizeof(FILE_DIRECTORY_INFORMATION
), /* FileDirectoryInformation */
3990 sizeof(FILE_FULL_DIRECTORY_INFORMATION
), /* FileFullDirectoryInformation */
3991 sizeof(FILE_BOTH_DIRECTORY_INFORMATION
), /* FileBothDirectoryInformation */
3992 sizeof(FILE_BASIC_INFORMATION
), /* FileBasicInformation */
3993 sizeof(FILE_STANDARD_INFORMATION
), /* FileStandardInformation */
3994 sizeof(FILE_INTERNAL_INFORMATION
), /* FileInternalInformation */
3995 sizeof(FILE_EA_INFORMATION
), /* FileEaInformation */
3996 0, /* FileAccessInformation */
3997 sizeof(FILE_NAME_INFORMATION
), /* FileNameInformation */
3998 sizeof(FILE_RENAME_INFORMATION
)-sizeof(WCHAR
), /* FileRenameInformation */
3999 0, /* FileLinkInformation */
4000 sizeof(FILE_NAMES_INFORMATION
)-sizeof(WCHAR
), /* FileNamesInformation */
4001 sizeof(FILE_DISPOSITION_INFORMATION
), /* FileDispositionInformation */
4002 sizeof(FILE_POSITION_INFORMATION
), /* FilePositionInformation */
4003 sizeof(FILE_FULL_EA_INFORMATION
), /* FileFullEaInformation */
4004 0, /* FileModeInformation */
4005 sizeof(FILE_ALIGNMENT_INFORMATION
), /* FileAlignmentInformation */
4006 sizeof(FILE_ALL_INFORMATION
), /* FileAllInformation */
4007 sizeof(FILE_ALLOCATION_INFORMATION
), /* FileAllocationInformation */
4008 sizeof(FILE_END_OF_FILE_INFORMATION
), /* FileEndOfFileInformation */
4009 0, /* FileAlternateNameInformation */
4010 sizeof(FILE_STREAM_INFORMATION
)-sizeof(WCHAR
), /* FileStreamInformation */
4011 sizeof(FILE_PIPE_INFORMATION
), /* FilePipeInformation */
4012 sizeof(FILE_PIPE_LOCAL_INFORMATION
), /* FilePipeLocalInformation */
4013 0, /* FilePipeRemoteInformation */
4014 sizeof(FILE_MAILSLOT_QUERY_INFORMATION
), /* FileMailslotQueryInformation */
4015 0, /* FileMailslotSetInformation */
4016 0, /* FileCompressionInformation */
4017 0, /* FileObjectIdInformation */
4018 0, /* FileCompletionInformation */
4019 0, /* FileMoveClusterInformation */
4020 0, /* FileQuotaInformation */
4021 0, /* FileReparsePointInformation */
4022 sizeof(FILE_NETWORK_OPEN_INFORMATION
), /* FileNetworkOpenInformation */
4023 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
), /* FileAttributeTagInformation */
4024 0, /* FileTrackingInformation */
4025 0, /* FileIdBothDirectoryInformation */
4026 0, /* FileIdFullDirectoryInformation */
4027 0, /* FileValidDataLengthInformation */
4028 0, /* FileShortNameInformation */
4029 0, /* FileIoCompletionNotificationInformation, */
4030 0, /* FileIoStatusBlockRangeInformation */
4031 0, /* FileIoPriorityHintInformation */
4032 0, /* FileSfioReserveInformation */
4033 0, /* FileSfioVolumeInformation */
4034 0, /* FileHardLinkInformation */
4035 0, /* FileProcessIdsUsingFileInformation */
4036 0, /* FileNormalizedNameInformation */
4037 0, /* FileNetworkPhysicalNameInformation */
4038 0, /* FileIdGlobalTxDirectoryInformation */
4039 0, /* FileIsRemoteDeviceInformation */
4040 0, /* FileAttributeCacheInformation */
4041 0, /* FileNumaNodeInformation */
4042 0, /* FileStandardLinkInformation */
4043 0, /* FileRemoteProtocolInformation */
4044 0, /* FileRenameInformationBypassAccessCheck */
4045 0, /* FileLinkInformationBypassAccessCheck */
4046 0, /* FileVolumeNameInformation */
4047 sizeof(FILE_ID_INFORMATION
), /* FileIdInformation */
4048 0, /* FileIdExtdDirectoryInformation */
4049 0, /* FileReplaceCompletionInformation */
4050 0, /* FileHardLinkFullIdInformation */
4051 0, /* FileIdExtdBothDirectoryInformation */
4055 int fd
, needs_close
= FALSE
;
4057 unsigned int options
;
4059 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, len
, class);
4061 io
->Information
= 0;
4063 if (class <= 0 || class >= FileMaximumInformation
)
4064 return io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4065 if (!info_sizes
[class])
4066 return server_get_file_info( handle
, io
, ptr
, len
, class );
4067 if (len
< info_sizes
[class])
4068 return io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4070 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, &options
)))
4072 if (io
->u
.Status
!= STATUS_BAD_DEVICE_TYPE
) return io
->u
.Status
;
4073 return server_get_file_info( handle
, io
, ptr
, len
, class );
4078 case FileBasicInformation
:
4079 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1)
4080 io
->u
.Status
= errno_to_status( errno
);
4081 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4082 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4084 fill_file_info( &st
, attr
, ptr
, class );
4086 case FileStandardInformation
:
4088 FILE_STANDARD_INFORMATION
*info
= ptr
;
4090 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4093 fill_file_info( &st
, attr
, info
, class );
4094 info
->DeletePending
= FALSE
; /* FIXME */
4098 case FilePositionInformation
:
4100 FILE_POSITION_INFORMATION
*info
= ptr
;
4101 off_t res
= lseek( fd
, 0, SEEK_CUR
);
4102 if (res
== (off_t
)-1) io
->u
.Status
= errno_to_status( errno
);
4103 else info
->CurrentByteOffset
.QuadPart
= res
;
4106 case FileInternalInformation
:
4107 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4108 else fill_file_info( &st
, attr
, ptr
, class );
4110 case FileEaInformation
:
4112 FILE_EA_INFORMATION
*info
= ptr
;
4116 case FileEndOfFileInformation
:
4117 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4118 else fill_file_info( &st
, attr
, ptr
, class );
4120 case FileAllInformation
:
4122 FILE_ALL_INFORMATION
*info
= ptr
;
4125 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4126 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4127 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4128 else if (!(io
->u
.Status
= server_get_unix_name( handle
, &unix_name
)))
4130 LONG name_len
= len
- FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
);
4132 fill_file_info( &st
, attr
, info
, FileAllInformation
);
4133 info
->StandardInformation
.DeletePending
= FALSE
; /* FIXME */
4134 info
->EaInformation
.EaSize
= 0;
4135 info
->AccessInformation
.AccessFlags
= 0; /* FIXME */
4136 info
->PositionInformation
.CurrentByteOffset
.QuadPart
= lseek( fd
, 0, SEEK_CUR
);
4137 info
->ModeInformation
.Mode
= 0; /* FIXME */
4138 info
->AlignmentInformation
.AlignmentRequirement
= 1; /* FIXME */
4140 io
->u
.Status
= fill_name_info( unix_name
, &info
->NameInformation
, &name_len
);
4142 io
->Information
= FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
) + name_len
;
4146 case FileMailslotQueryInformation
:
4148 FILE_MAILSLOT_QUERY_INFORMATION
*info
= ptr
;
4150 SERVER_START_REQ( set_mailslot_info
)
4152 req
->handle
= wine_server_obj_handle( handle
);
4154 io
->u
.Status
= wine_server_call( req
);
4155 if( io
->u
.Status
== STATUS_SUCCESS
)
4157 info
->MaximumMessageSize
= reply
->max_msgsize
;
4158 info
->MailslotQuota
= 0;
4159 info
->NextMessageSize
= 0;
4160 info
->MessagesAvailable
= 0;
4161 info
->ReadTimeout
.QuadPart
= reply
->read_timeout
;
4168 ULONG size
= info
->MaximumMessageSize
? info
->MaximumMessageSize
: 0x10000;
4169 if (size
> 0x10000) size
= 0x10000;
4170 if ((tmpbuf
= malloc( size
)))
4172 if (!server_get_unix_fd( handle
, FILE_READ_DATA
, &fd
, &needs_close
, NULL
, NULL
))
4174 int res
= recv( fd
, tmpbuf
, size
, MSG_PEEK
);
4175 info
->MessagesAvailable
= (res
> 0);
4176 info
->NextMessageSize
= (res
>= 0) ? res
: MAILSLOT_NO_MESSAGE
;
4177 if (needs_close
) close( fd
);
4184 case FileNameInformation
:
4186 FILE_NAME_INFORMATION
*info
= ptr
;
4189 if (!(io
->u
.Status
= server_get_unix_name( handle
, &unix_name
)))
4191 LONG name_len
= len
- FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
);
4192 io
->u
.Status
= fill_name_info( unix_name
, info
, &name_len
);
4194 io
->Information
= FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
) + name_len
;
4198 case FileNetworkOpenInformation
:
4200 FILE_NETWORK_OPEN_INFORMATION
*info
= ptr
;
4203 if (!(io
->u
.Status
= server_get_unix_name( handle
, &unix_name
)))
4208 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4209 io
->u
.Status
= errno_to_status( errno
);
4210 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4211 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4214 FILE_BASIC_INFORMATION basic
;
4215 FILE_STANDARD_INFORMATION std
;
4217 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
4218 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
4220 info
->CreationTime
= basic
.CreationTime
;
4221 info
->LastAccessTime
= basic
.LastAccessTime
;
4222 info
->LastWriteTime
= basic
.LastWriteTime
;
4223 info
->ChangeTime
= basic
.ChangeTime
;
4224 info
->AllocationSize
= std
.AllocationSize
;
4225 info
->EndOfFile
= std
.EndOfFile
;
4226 info
->FileAttributes
= basic
.FileAttributes
;
4232 case FileIdInformation
:
4233 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4236 struct mountmgr_unix_drive drive
;
4237 FILE_ID_INFORMATION
*info
= ptr
;
4239 info
->VolumeSerialNumber
= 0;
4240 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) ))
4241 info
->VolumeSerialNumber
= drive
.serial
;
4242 memset( &info
->FileId
, 0, sizeof(info
->FileId
) );
4243 *(ULONGLONG
*)&info
->FileId
= st
.st_ino
;
4246 case FileAttributeTagInformation
:
4247 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4250 FILE_ATTRIBUTE_TAG_INFORMATION
*info
= ptr
;
4251 info
->FileAttributes
= attr
;
4252 info
->ReparseTag
= 0; /* FIXME */
4253 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, &st
))
4254 info
->ReparseTag
= IO_REPARSE_TAG_MOUNT_POINT
;
4258 FIXME("Unsupported class (%d)\n", class);
4259 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
4262 if (needs_close
) close( fd
);
4263 if (io
->u
.Status
== STATUS_SUCCESS
&& !io
->Information
) io
->Information
= info_sizes
[class];
4264 return io
->u
.Status
;
4268 /******************************************************************************
4269 * NtSetInformationFile (NTDLL.@)
4271 NTSTATUS WINAPI
NtSetInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
4272 void *ptr
, ULONG len
, FILE_INFORMATION_CLASS
class )
4274 int fd
, needs_close
;
4276 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, len
, class );
4278 io
->u
.Status
= STATUS_SUCCESS
;
4281 case FileBasicInformation
:
4282 if (len
>= sizeof(FILE_BASIC_INFORMATION
))
4285 const FILE_BASIC_INFORMATION
*info
= ptr
;
4286 LARGE_INTEGER mtime
, atime
;
4288 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4289 return io
->u
.Status
;
4291 mtime
.QuadPart
= info
->LastWriteTime
.QuadPart
== -1 ? 0 : info
->LastWriteTime
.QuadPart
;
4292 atime
.QuadPart
= info
->LastAccessTime
.QuadPart
== -1 ? 0 : info
->LastAccessTime
.QuadPart
;
4294 if (atime
.QuadPart
|| mtime
.QuadPart
)
4295 io
->u
.Status
= set_file_times( fd
, &mtime
, &atime
);
4297 if (io
->u
.Status
== STATUS_SUCCESS
&& info
->FileAttributes
)
4299 if (fstat( fd
, &st
) == -1) io
->u
.Status
= errno_to_status( errno
);
4302 if (info
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)
4304 if (S_ISDIR( st
.st_mode
))
4305 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
4307 st
.st_mode
&= ~0222; /* clear write permission bits */
4311 /* add write permission only where we already have read permission */
4312 st
.st_mode
|= (0600 | ((st
.st_mode
& 044) >> 1)) & (~start_umask
);
4314 if (fchmod( fd
, st
.st_mode
) == -1) io
->u
.Status
= errno_to_status( errno
);
4318 if (needs_close
) close( fd
);
4320 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4323 case FilePositionInformation
:
4324 if (len
>= sizeof(FILE_POSITION_INFORMATION
))
4326 const FILE_POSITION_INFORMATION
*info
= ptr
;
4328 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4329 return io
->u
.Status
;
4331 if (lseek( fd
, info
->CurrentByteOffset
.QuadPart
, SEEK_SET
) == (off_t
)-1)
4332 io
->u
.Status
= errno_to_status( errno
);
4334 if (needs_close
) close( fd
);
4336 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4339 case FileEndOfFileInformation
:
4340 if (len
>= sizeof(FILE_END_OF_FILE_INFORMATION
))
4343 const FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
4345 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4346 return io
->u
.Status
;
4348 /* first try normal truncate */
4349 if (ftruncate( fd
, (off_t
)info
->EndOfFile
.QuadPart
) != -1) break;
4351 /* now check for the need to extend the file */
4352 if (fstat( fd
, &st
) != -1 && (off_t
)info
->EndOfFile
.QuadPart
> st
.st_size
)
4354 static const char zero
;
4356 /* extend the file one byte beyond the requested size and then truncate it */
4357 /* this should work around ftruncate implementations that can't extend files */
4358 if (pwrite( fd
, &zero
, 1, (off_t
)info
->EndOfFile
.QuadPart
) != -1 &&
4359 ftruncate( fd
, (off_t
)info
->EndOfFile
.QuadPart
) != -1) break;
4361 io
->u
.Status
= errno_to_status( errno
);
4363 if (needs_close
) close( fd
);
4365 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4368 case FilePipeInformation
:
4369 if (len
>= sizeof(FILE_PIPE_INFORMATION
))
4371 FILE_PIPE_INFORMATION
*info
= ptr
;
4373 if ((info
->CompletionMode
| info
->ReadMode
) & ~1)
4375 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
4379 SERVER_START_REQ( set_named_pipe_info
)
4381 req
->handle
= wine_server_obj_handle( handle
);
4382 req
->flags
= (info
->CompletionMode
? NAMED_PIPE_NONBLOCKING_MODE
: 0) |
4383 (info
->ReadMode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0);
4384 io
->u
.Status
= wine_server_call( req
);
4388 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4391 case FileMailslotSetInformation
:
4393 FILE_MAILSLOT_SET_INFORMATION
*info
= ptr
;
4395 SERVER_START_REQ( set_mailslot_info
)
4397 req
->handle
= wine_server_obj_handle( handle
);
4398 req
->flags
= MAILSLOT_SET_READ_TIMEOUT
;
4399 req
->read_timeout
= info
->ReadTimeout
.QuadPart
;
4400 io
->u
.Status
= wine_server_call( req
);
4406 case FileCompletionInformation
:
4407 if (len
>= sizeof(FILE_COMPLETION_INFORMATION
))
4409 FILE_COMPLETION_INFORMATION
*info
= ptr
;
4411 SERVER_START_REQ( set_completion_info
)
4413 req
->handle
= wine_server_obj_handle( handle
);
4414 req
->chandle
= wine_server_obj_handle( info
->CompletionPort
);
4415 req
->ckey
= info
->CompletionKey
;
4416 io
->u
.Status
= wine_server_call( req
);
4420 io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4423 case FileIoCompletionNotificationInformation
:
4424 if (len
>= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
))
4426 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
*info
= ptr
;
4428 if (info
->Flags
& FILE_SKIP_SET_USER_EVENT_ON_FAST_IO
)
4429 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4431 SERVER_START_REQ( set_fd_completion_mode
)
4433 req
->handle
= wine_server_obj_handle( handle
);
4434 req
->flags
= info
->Flags
;
4435 io
->u
.Status
= wine_server_call( req
);
4439 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4442 case FileIoPriorityHintInformation
:
4443 if (len
>= sizeof(FILE_IO_PRIORITY_HINT_INFO
))
4445 FILE_IO_PRIORITY_HINT_INFO
*info
= ptr
;
4446 if (info
->PriorityHint
< MaximumIoPriorityHintType
)
4447 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info
->PriorityHint
);
4449 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
4451 else io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4454 case FileAllInformation
:
4455 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4458 case FileValidDataLengthInformation
:
4459 if (len
>= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
))
4462 const FILE_VALID_DATA_LENGTH_INFORMATION
*info
= ptr
;
4464 if ((io
->u
.Status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, NULL
, NULL
)))
4465 return io
->u
.Status
;
4467 if (fstat( fd
, &st
) == -1) io
->u
.Status
= errno_to_status( errno
);
4468 else if (info
->ValidDataLength
.QuadPart
<= 0 || (off_t
)info
->ValidDataLength
.QuadPart
> st
.st_size
)
4469 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
4472 #ifdef HAVE_FALLOCATE
4473 if (fallocate( fd
, 0, 0, (off_t
)info
->ValidDataLength
.QuadPart
) == -1)
4475 NTSTATUS status
= errno_to_status( errno
);
4476 if (status
== STATUS_NOT_SUPPORTED
) WARN( "fallocate not supported on this filesystem\n" );
4477 else io
->u
.Status
= status
;
4480 FIXME( "setting valid data length not supported\n" );
4483 if (needs_close
) close( fd
);
4485 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4488 case FileDispositionInformation
:
4489 if (len
>= sizeof(FILE_DISPOSITION_INFORMATION
))
4491 FILE_DISPOSITION_INFORMATION
*info
= ptr
;
4493 SERVER_START_REQ( set_fd_disp_info
)
4495 req
->handle
= wine_server_obj_handle( handle
);
4496 req
->unlink
= info
->DoDeleteFile
;
4497 io
->u
.Status
= wine_server_call( req
);
4501 io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4504 case FileRenameInformation
:
4505 if (len
>= sizeof(FILE_RENAME_INFORMATION
))
4507 FILE_RENAME_INFORMATION
*info
= ptr
;
4508 UNICODE_STRING name_str
, nt_name
= { 0 };
4509 OBJECT_ATTRIBUTES attr
;
4512 name_str
.Buffer
= info
->FileName
;
4513 name_str
.Length
= info
->FileNameLength
;
4514 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4516 attr
.Length
= sizeof(attr
);
4517 attr
.ObjectName
= &name_str
;
4518 attr
.RootDirectory
= info
->RootDirectory
;
4519 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
4521 io
->u
.Status
= nt_to_unix_file_name_attr( &attr
, &unix_name
, &nt_name
, FILE_OPEN_IF
);
4522 if (io
->u
.Status
!= STATUS_SUCCESS
&& io
->u
.Status
!= STATUS_NO_SUCH_FILE
)
4525 SERVER_START_REQ( set_fd_name_info
)
4527 req
->handle
= wine_server_obj_handle( handle
);
4528 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4529 req
->namelen
= nt_name
.Length
;
4531 req
->replace
= info
->ReplaceIfExists
;
4532 wine_server_add_data( req
, nt_name
.Buffer
, nt_name
.Length
);
4533 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4534 io
->u
.Status
= wine_server_call( req
);
4539 free( nt_name
.Buffer
);
4541 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4544 case FileLinkInformation
:
4545 if (len
>= sizeof(FILE_LINK_INFORMATION
))
4547 FILE_LINK_INFORMATION
*info
= ptr
;
4548 UNICODE_STRING name_str
, nt_name
= { 0 };
4549 OBJECT_ATTRIBUTES attr
;
4552 name_str
.Buffer
= info
->FileName
;
4553 name_str
.Length
= info
->FileNameLength
;
4554 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4556 attr
.Length
= sizeof(attr
);
4557 attr
.ObjectName
= &name_str
;
4558 attr
.RootDirectory
= info
->RootDirectory
;
4559 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
4561 io
->u
.Status
= nt_to_unix_file_name_attr( &attr
, &unix_name
, &nt_name
, FILE_OPEN_IF
);
4562 if (io
->u
.Status
!= STATUS_SUCCESS
&& io
->u
.Status
!= STATUS_NO_SUCH_FILE
)
4565 SERVER_START_REQ( set_fd_name_info
)
4567 req
->handle
= wine_server_obj_handle( handle
);
4568 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4569 req
->namelen
= nt_name
.Length
;
4571 req
->replace
= info
->ReplaceIfExists
;
4572 wine_server_add_data( req
, nt_name
.Buffer
, nt_name
.Length
);
4573 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4574 io
->u
.Status
= wine_server_call( req
);
4579 free( nt_name
.Buffer
);
4581 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4585 FIXME("Unsupported class (%d)\n", class);
4586 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
4589 io
->Information
= 0;
4590 return io
->u
.Status
;
4594 /***********************************************************************
4595 * Asynchronous file I/O *
4598 typedef NTSTATUS
async_callback_t( void *user
, IO_STATUS_BLOCK
*io
, NTSTATUS status
);
4602 async_callback_t
*callback
; /* must be the first field */
4603 struct async_fileio
*next
;
4607 struct async_fileio_read
4609 struct async_fileio io
;
4611 unsigned int already
;
4616 struct async_fileio_write
4618 struct async_fileio io
;
4620 unsigned int already
;
4624 struct async_fileio_read_changes
4626 struct async_fileio io
;
4635 struct async_fileio io
;
4636 void *buffer
; /* buffer for output */
4637 ULONG size
; /* size of buffer */
4640 static struct async_fileio
*fileio_freelist
;
4642 static void release_fileio( struct async_fileio
*io
)
4646 struct async_fileio
*next
= fileio_freelist
;
4648 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist
, io
, next
) == next
) return;
4652 static struct async_fileio
*alloc_fileio( DWORD size
, async_callback_t callback
, HANDLE handle
)
4654 /* first free remaining previous fileinfos */
4655 struct async_fileio
*io
= InterlockedExchangePointer( (void **)&fileio_freelist
, NULL
);
4659 struct async_fileio
*next
= io
->next
;
4664 if ((io
= malloc( size
)))
4666 io
->callback
= callback
;
4667 io
->handle
= handle
;
4672 static async_data_t
server_async( HANDLE handle
, struct async_fileio
*user
, HANDLE event
,
4673 PIO_APC_ROUTINE apc
, void *apc_context
, IO_STATUS_BLOCK
*io
)
4676 async
.handle
= wine_server_obj_handle( handle
);
4677 async
.user
= wine_server_client_ptr( user
);
4678 async
.iosb
= wine_server_client_ptr( io
);
4679 async
.event
= wine_server_obj_handle( event
);
4680 async
.apc
= wine_server_client_ptr( apc
);
4681 async
.apc_context
= wine_server_client_ptr( apc_context
);
4685 static NTSTATUS
wait_async( HANDLE handle
, BOOL alertable
, IO_STATUS_BLOCK
*io
)
4687 if (NtWaitForSingleObject( handle
, alertable
, NULL
)) return STATUS_PENDING
;
4688 return io
->u
.Status
;
4691 /* callback for irp async I/O completion */
4692 static NTSTATUS
irp_completion( void *user
, IO_STATUS_BLOCK
*io
, NTSTATUS status
)
4694 struct async_irp
*async
= user
;
4695 ULONG information
= 0;
4697 if (status
== STATUS_ALERTED
)
4699 SERVER_START_REQ( get_async_result
)
4701 req
->user_arg
= wine_server_client_ptr( async
);
4702 wine_server_set_reply( req
, async
->buffer
, async
->size
);
4703 status
= virtual_locked_server_call( req
);
4704 information
= reply
->size
;
4708 if (status
!= STATUS_PENDING
)
4710 io
->u
.Status
= status
;
4711 io
->Information
= information
;
4712 release_fileio( &async
->io
);
4717 static NTSTATUS
async_read_proc( void *user
, IO_STATUS_BLOCK
*iosb
, NTSTATUS status
)
4719 struct async_fileio_read
*fileio
= user
;
4720 int fd
, needs_close
, result
;
4724 case STATUS_ALERTED
: /* got some new data */
4725 /* check to see if the data is ready (non-blocking) */
4726 if ((status
= server_get_unix_fd( fileio
->io
.handle
, FILE_READ_DATA
, &fd
,
4727 &needs_close
, NULL
, NULL
)))
4730 result
= virtual_locked_read(fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
-fileio
->already
);
4731 if (needs_close
) close( fd
);
4735 if (errno
== EAGAIN
|| errno
== EINTR
)
4736 status
= STATUS_PENDING
;
4737 else /* check to see if the transfer is complete */
4738 status
= errno_to_status( errno
);
4740 else if (result
== 0)
4742 status
= fileio
->already
? STATUS_SUCCESS
: STATUS_PIPE_BROKEN
;
4746 fileio
->already
+= result
;
4747 if (fileio
->already
>= fileio
->count
|| fileio
->avail_mode
)
4748 status
= STATUS_SUCCESS
;
4750 status
= STATUS_PENDING
;
4754 case STATUS_TIMEOUT
:
4755 case STATUS_IO_TIMEOUT
:
4756 if (fileio
->already
) status
= STATUS_SUCCESS
;
4759 if (status
!= STATUS_PENDING
)
4761 iosb
->u
.Status
= status
;
4762 iosb
->Information
= fileio
->already
;
4763 release_fileio( &fileio
->io
);
4768 static NTSTATUS
async_write_proc( void *user
, IO_STATUS_BLOCK
*iosb
, NTSTATUS status
)
4770 struct async_fileio_write
*fileio
= user
;
4771 int result
, fd
, needs_close
;
4772 enum server_fd_type type
;
4776 case STATUS_ALERTED
:
4777 /* write some data (non-blocking) */
4778 if ((status
= server_get_unix_fd( fileio
->io
.handle
, FILE_WRITE_DATA
, &fd
,
4779 &needs_close
, &type
, NULL
)))
4782 if (!fileio
->count
&& (type
== FD_TYPE_MAILSLOT
|| type
== FD_TYPE_SOCKET
))
4783 result
= send( fd
, fileio
->buffer
, 0, 0 );
4785 result
= write( fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
- fileio
->already
);
4787 if (needs_close
) close( fd
);
4791 if (errno
== EAGAIN
|| errno
== EINTR
) status
= STATUS_PENDING
;
4792 else status
= errno_to_status( errno
);
4796 fileio
->already
+= result
;
4797 status
= (fileio
->already
< fileio
->count
) ? STATUS_PENDING
: STATUS_SUCCESS
;
4801 case STATUS_TIMEOUT
:
4802 case STATUS_IO_TIMEOUT
:
4803 if (fileio
->already
) status
= STATUS_SUCCESS
;
4806 if (status
!= STATUS_PENDING
)
4808 iosb
->u
.Status
= status
;
4809 iosb
->Information
= fileio
->already
;
4810 release_fileio( &fileio
->io
);
4815 /* do a read call through the server */
4816 static NTSTATUS
server_read_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
4817 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG size
,
4818 LARGE_INTEGER
*offset
, ULONG
*key
)
4820 struct async_irp
*async
;
4825 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4826 return STATUS_NO_MEMORY
;
4828 async
->buffer
= buffer
;
4831 SERVER_START_REQ( read
)
4833 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, io
);
4834 req
->pos
= offset
? offset
->QuadPart
: 0;
4835 wine_server_set_reply( req
, buffer
, size
);
4836 status
= virtual_locked_server_call( req
);
4837 wait_handle
= wine_server_ptr_handle( reply
->wait
);
4838 options
= reply
->options
;
4839 if (wait_handle
&& status
!= STATUS_PENDING
)
4841 io
->u
.Status
= status
;
4842 io
->Information
= wine_server_reply_size( reply
);
4847 if (status
!= STATUS_PENDING
) free( async
);
4849 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
), io
);
4853 /* do a write call through the server */
4854 static NTSTATUS
server_write_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
4855 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG size
,
4856 LARGE_INTEGER
*offset
, ULONG
*key
)
4858 struct async_irp
*async
;
4863 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4864 return STATUS_NO_MEMORY
;
4866 async
->buffer
= NULL
;
4869 SERVER_START_REQ( write
)
4871 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, io
);
4872 req
->pos
= offset
? offset
->QuadPart
: 0;
4873 wine_server_add_data( req
, buffer
, size
);
4874 status
= wine_server_call( req
);
4875 wait_handle
= wine_server_ptr_handle( reply
->wait
);
4876 options
= reply
->options
;
4877 if (wait_handle
&& status
!= STATUS_PENDING
)
4879 io
->u
.Status
= status
;
4880 io
->Information
= reply
->size
;
4885 if (status
!= STATUS_PENDING
) free( async
);
4887 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
), io
);
4891 /* do an ioctl call through the server */
4892 static NTSTATUS
server_ioctl_file( HANDLE handle
, HANDLE event
,
4893 PIO_APC_ROUTINE apc
, PVOID apc_context
,
4894 IO_STATUS_BLOCK
*io
, ULONG code
,
4895 const void *in_buffer
, ULONG in_size
,
4896 PVOID out_buffer
, ULONG out_size
)
4898 struct async_irp
*async
;
4903 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4904 return STATUS_NO_MEMORY
;
4905 async
->buffer
= out_buffer
;
4906 async
->size
= out_size
;
4908 SERVER_START_REQ( ioctl
)
4911 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, io
);
4912 wine_server_add_data( req
, in_buffer
, in_size
);
4913 if ((code
& 3) != METHOD_BUFFERED
) wine_server_add_data( req
, out_buffer
, out_size
);
4914 wine_server_set_reply( req
, out_buffer
, out_size
);
4915 status
= virtual_locked_server_call( req
);
4916 wait_handle
= wine_server_ptr_handle( reply
->wait
);
4917 options
= reply
->options
;
4918 if (wait_handle
&& status
!= STATUS_PENDING
)
4920 io
->u
.Status
= status
;
4921 io
->Information
= wine_server_reply_size( reply
);
4926 if (status
== STATUS_NOT_SUPPORTED
)
4927 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
4928 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
4930 if (status
!= STATUS_PENDING
) free( async
);
4932 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
), io
);
4939 int interval
; /* max interval between two bytes */
4940 int total
; /* total timeout for the whole operation */
4941 int end_time
; /* absolute time of end of operation */
4944 /* retrieve the I/O timeouts to use for a given handle */
4945 static NTSTATUS
get_io_timeouts( HANDLE handle
, enum server_fd_type type
, ULONG count
, BOOL is_read
,
4946 struct io_timeouts
*timeouts
)
4948 NTSTATUS status
= STATUS_SUCCESS
;
4950 timeouts
->interval
= timeouts
->total
= -1;
4954 case FD_TYPE_SERIAL
:
4956 /* GetCommTimeouts */
4960 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
4961 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
4966 if (st
.ReadIntervalTimeout
)
4967 timeouts
->interval
= st
.ReadIntervalTimeout
;
4969 if (st
.ReadTotalTimeoutMultiplier
|| st
.ReadTotalTimeoutConstant
)
4971 timeouts
->total
= st
.ReadTotalTimeoutConstant
;
4972 if (st
.ReadTotalTimeoutMultiplier
!= MAXDWORD
)
4973 timeouts
->total
+= count
* st
.ReadTotalTimeoutMultiplier
;
4975 else if (st
.ReadIntervalTimeout
== MAXDWORD
)
4976 timeouts
->interval
= timeouts
->total
= 0;
4980 if (st
.WriteTotalTimeoutMultiplier
|| st
.WriteTotalTimeoutConstant
)
4982 timeouts
->total
= st
.WriteTotalTimeoutConstant
;
4983 if (st
.WriteTotalTimeoutMultiplier
!= MAXDWORD
)
4984 timeouts
->total
+= count
* st
.WriteTotalTimeoutMultiplier
;
4989 case FD_TYPE_MAILSLOT
:
4992 timeouts
->interval
= 0; /* return as soon as we got something */
4993 SERVER_START_REQ( set_mailslot_info
)
4995 req
->handle
= wine_server_obj_handle( handle
);
4997 if (!(status
= wine_server_call( req
)) &&
4998 reply
->read_timeout
!= TIMEOUT_INFINITE
)
4999 timeouts
->total
= reply
->read_timeout
/ -10000;
5004 case FD_TYPE_SOCKET
:
5006 if (is_read
) timeouts
->interval
= 0; /* return as soon as we got something */
5011 if (timeouts
->total
!= -1) timeouts
->end_time
= NtGetTickCount() + timeouts
->total
;
5012 return STATUS_SUCCESS
;
5016 /* retrieve the timeout for the next wait, in milliseconds */
5017 static inline int get_next_io_timeout( const struct io_timeouts
*timeouts
, ULONG already
)
5021 if (timeouts
->total
!= -1)
5023 ret
= timeouts
->end_time
- NtGetTickCount();
5024 if (ret
< 0) ret
= 0;
5026 if (already
&& timeouts
->interval
!= -1)
5028 if (ret
== -1 || ret
> timeouts
->interval
) ret
= timeouts
->interval
;
5034 /* retrieve the avail_mode flag for async reads */
5035 static NTSTATUS
get_io_avail_mode( HANDLE handle
, enum server_fd_type type
, BOOL
*avail_mode
)
5037 NTSTATUS status
= STATUS_SUCCESS
;
5041 case FD_TYPE_SERIAL
:
5043 /* GetCommTimeouts */
5047 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
5048 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
5050 *avail_mode
= (!st
.ReadTotalTimeoutMultiplier
&&
5051 !st
.ReadTotalTimeoutConstant
&&
5052 st
.ReadIntervalTimeout
== MAXDWORD
);
5055 case FD_TYPE_MAILSLOT
:
5056 case FD_TYPE_SOCKET
:
5061 *avail_mode
= FALSE
;
5067 /* register an async I/O for a file read; helper for NtReadFile */
5068 static NTSTATUS
register_async_file_read( HANDLE handle
, HANDLE event
,
5069 PIO_APC_ROUTINE apc
, void *apc_user
,
5070 IO_STATUS_BLOCK
*iosb
, void *buffer
,
5071 ULONG already
, ULONG length
, BOOL avail_mode
)
5073 struct async_fileio_read
*fileio
;
5076 if (!(fileio
= (struct async_fileio_read
*)alloc_fileio( sizeof(*fileio
), async_read_proc
, handle
)))
5077 return STATUS_NO_MEMORY
;
5079 fileio
->already
= already
;
5080 fileio
->count
= length
;
5081 fileio
->buffer
= buffer
;
5082 fileio
->avail_mode
= avail_mode
;
5084 SERVER_START_REQ( register_async
)
5086 req
->type
= ASYNC_TYPE_READ
;
5087 req
->count
= length
;
5088 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, iosb
);
5089 status
= wine_server_call( req
);
5093 if (status
!= STATUS_PENDING
) free( fileio
);
5097 static void add_completion( HANDLE handle
, ULONG_PTR value
, NTSTATUS status
, ULONG info
, BOOL async
)
5099 SERVER_START_REQ( add_fd_completion
)
5101 req
->handle
= wine_server_obj_handle( handle
);
5102 req
->cvalue
= value
;
5103 req
->status
= status
;
5104 req
->information
= info
;
5106 wine_server_call( req
);
5111 static NTSTATUS
set_pending_write( HANDLE device
)
5115 SERVER_START_REQ( set_serial_info
)
5117 req
->handle
= wine_server_obj_handle( device
);
5118 req
->flags
= SERIALINFO_PENDING_WRITE
;
5119 status
= wine_server_call( req
);
5126 /******************************************************************************
5127 * NtReadFile (NTDLL.@)
5129 NTSTATUS WINAPI
NtReadFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5130 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
5131 LARGE_INTEGER
*offset
, ULONG
*key
)
5133 int result
, unix_handle
, needs_close
;
5134 unsigned int options
;
5135 struct io_timeouts timeouts
;
5136 NTSTATUS status
, ret_status
;
5138 enum server_fd_type type
;
5139 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5140 BOOL send_completion
= FALSE
, async_read
, timeout_init_done
= FALSE
;
5142 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5143 handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5145 if (!io
) return STATUS_ACCESS_VIOLATION
;
5147 status
= server_get_unix_fd( handle
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5148 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5150 if (!virtual_check_buffer_for_write( buffer
, length
)) return STATUS_ACCESS_VIOLATION
;
5152 if (status
== STATUS_BAD_DEVICE_TYPE
)
5153 return server_read_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5155 async_read
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5157 if (type
== FD_TYPE_FILE
)
5159 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5161 status
= STATUS_INVALID_PARAMETER
;
5165 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5167 /* async I/O doesn't make sense on regular files */
5168 while ((result
= virtual_locked_pread( unix_handle
, buffer
, length
, offset
->QuadPart
)) == -1)
5172 status
= errno_to_status( errno
);
5176 if (!async_read
) /* update file pointer position */
5177 lseek( unix_handle
, offset
->QuadPart
+ result
, SEEK_SET
);
5180 status
= (total
|| !length
) ? STATUS_SUCCESS
: STATUS_END_OF_FILE
;
5184 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5186 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5188 status
= STATUS_INVALID_PARAMETER
;
5193 if (type
== FD_TYPE_SERIAL
&& async_read
&& length
)
5195 /* an asynchronous serial port read with a read interval timeout needs to
5196 skip the synchronous read to make sure that the server starts the read
5197 interval timer after the first read */
5198 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5199 if (timeouts
.interval
)
5201 status
= register_async_file_read( handle
, event
, apc
, apc_user
, io
,
5202 buffer
, total
, length
, FALSE
);
5209 if ((result
= virtual_locked_read( unix_handle
, (char *)buffer
+ total
, length
- total
)) >= 0)
5212 if (!result
|| total
== length
)
5216 status
= STATUS_SUCCESS
;
5223 case FD_TYPE_DEVICE
:
5224 status
= length
? STATUS_END_OF_FILE
: STATUS_SUCCESS
;
5226 case FD_TYPE_SERIAL
:
5229 status
= STATUS_SUCCESS
;
5234 status
= STATUS_PIPE_BROKEN
;
5238 else if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5240 else if (errno
!= EAGAIN
)
5242 if (errno
== EINTR
) continue;
5243 if (!total
) status
= errno_to_status( errno
);
5251 if ((status
= get_io_avail_mode( handle
, type
, &avail_mode
))) goto err
;
5252 if (total
&& avail_mode
)
5254 status
= STATUS_SUCCESS
;
5257 status
= register_async_file_read( handle
, event
, apc
, apc_user
, io
,
5258 buffer
, total
, length
, avail_mode
);
5261 else /* synchronous read, wait for the fd to become ready */
5266 if (!timeout_init_done
)
5268 timeout_init_done
= TRUE
;
5269 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5270 if (event
) NtResetEvent( event
, NULL
);
5272 timeout
= get_next_io_timeout( &timeouts
, total
);
5274 pfd
.fd
= unix_handle
;
5275 pfd
.events
= POLLIN
;
5277 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
5279 if (total
) /* return with what we got so far */
5280 status
= STATUS_SUCCESS
;
5282 status
= (type
== FD_TYPE_MAILSLOT
) ? STATUS_IO_TIMEOUT
: STATUS_TIMEOUT
;
5285 if (ret
== -1 && errno
!= EINTR
)
5287 status
= errno_to_status( errno
);
5290 /* will now restart the read */
5295 send_completion
= cvalue
!= 0;
5298 if (needs_close
) close( unix_handle
);
5299 if (status
== STATUS_SUCCESS
|| (status
== STATUS_END_OF_FILE
&& (!async_read
|| type
== FD_TYPE_FILE
)))
5301 io
->u
.Status
= status
;
5302 io
->Information
= total
;
5303 TRACE("= SUCCESS (%u)\n", total
);
5304 if (event
) NtSetEvent( event
, NULL
);
5305 if (apc
&& (!status
|| async_read
)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5306 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5310 TRACE("= 0x%08x\n", status
);
5311 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5314 ret_status
= async_read
&& type
== FD_TYPE_FILE
&& (status
== STATUS_SUCCESS
|| status
== STATUS_END_OF_FILE
)
5315 ? STATUS_PENDING
: status
;
5317 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
5322 /******************************************************************************
5323 * NtReadFileScatter (NTDLL.@)
5325 NTSTATUS WINAPI
NtReadFileScatter( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5326 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
5327 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
5329 int result
, unix_handle
, needs_close
;
5330 unsigned int options
;
5332 ULONG pos
= 0, total
= 0;
5333 enum server_fd_type type
;
5334 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5335 BOOL send_completion
= FALSE
;
5337 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5338 file
, event
, apc
, apc_user
, io
, segments
, length
, offset
, key
);
5340 if (!io
) return STATUS_ACCESS_VIOLATION
;
5342 status
= server_get_unix_fd( file
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5343 if (status
) return status
;
5345 if ((type
!= FD_TYPE_FILE
) ||
5346 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
5347 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
5349 status
= STATUS_INVALID_PARAMETER
;
5355 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5356 result
= pread( unix_handle
, (char *)segments
->Buffer
+ pos
,
5357 min( length
- pos
, page_size
- pos
), offset
->QuadPart
+ total
);
5359 result
= read( unix_handle
, (char *)segments
->Buffer
+ pos
, min( length
- pos
, page_size
- pos
) );
5363 if (errno
== EINTR
) continue;
5364 status
= errno_to_status( errno
);
5370 if ((pos
+= result
) == page_size
)
5377 if (total
== 0) status
= STATUS_END_OF_FILE
;
5379 send_completion
= cvalue
!= 0;
5381 if (needs_close
) close( unix_handle
);
5382 io
->u
.Status
= status
;
5383 io
->Information
= total
;
5384 TRACE("= 0x%08x (%u)\n", status
, total
);
5385 if (event
) NtSetEvent( event
, NULL
);
5386 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5387 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5388 if (send_completion
) add_completion( file
, cvalue
, status
, total
, TRUE
);
5390 return STATUS_PENDING
;
5393 if (needs_close
) close( unix_handle
);
5394 if (event
) NtResetEvent( event
, NULL
);
5395 TRACE("= 0x%08x\n", status
);
5400 /******************************************************************************
5401 * NtWriteFile (NTDLL.@)
5403 NTSTATUS WINAPI
NtWriteFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5404 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG length
,
5405 LARGE_INTEGER
*offset
, ULONG
*key
)
5407 int result
, unix_handle
, needs_close
;
5408 unsigned int options
;
5409 struct io_timeouts timeouts
;
5410 NTSTATUS status
, ret_status
;
5412 enum server_fd_type type
;
5413 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5414 BOOL send_completion
= FALSE
, async_write
, append_write
= FALSE
, timeout_init_done
= FALSE
;
5415 LARGE_INTEGER offset_eof
;
5417 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5418 handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5420 if (!io
) return STATUS_ACCESS_VIOLATION
;
5422 status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5423 if (status
== STATUS_ACCESS_DENIED
)
5425 status
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &unix_handle
,
5426 &needs_close
, &type
, &options
);
5427 append_write
= TRUE
;
5429 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5431 async_write
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5433 if (!virtual_check_buffer_for_read( buffer
, length
))
5435 status
= STATUS_INVALID_USER_BUFFER
;
5439 if (status
== STATUS_BAD_DEVICE_TYPE
)
5440 return server_write_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5442 if (type
== FD_TYPE_FILE
)
5445 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5447 status
= STATUS_INVALID_PARAMETER
;
5453 offset_eof
.QuadPart
= FILE_WRITE_TO_END_OF_FILE
;
5454 offset
= &offset_eof
;
5457 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5459 off_t off
= offset
->QuadPart
;
5461 if (offset
->QuadPart
== FILE_WRITE_TO_END_OF_FILE
)
5465 if (fstat( unix_handle
, &st
) == -1)
5467 status
= errno_to_status( errno
);
5472 else if (offset
->QuadPart
< 0)
5474 status
= STATUS_INVALID_PARAMETER
;
5478 /* async I/O doesn't make sense on regular files */
5479 while ((result
= pwrite( unix_handle
, buffer
, length
, off
)) == -1)
5483 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5484 else status
= errno_to_status( errno
);
5489 if (!async_write
) /* update file pointer position */
5490 lseek( unix_handle
, off
+ result
, SEEK_SET
);
5493 status
= STATUS_SUCCESS
;
5497 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5500 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5502 status
= STATUS_INVALID_PARAMETER
;
5509 /* zero-length writes on sockets may not work with plain write(2) */
5510 if (!length
&& (type
== FD_TYPE_MAILSLOT
|| type
== FD_TYPE_SOCKET
))
5511 result
= send( unix_handle
, buffer
, 0, 0 );
5513 result
= write( unix_handle
, (const char *)buffer
+ total
, length
- total
);
5518 if (total
== length
)
5520 status
= STATUS_SUCCESS
;
5523 if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5525 else if (errno
!= EAGAIN
)
5527 if (errno
== EINTR
) continue;
5530 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5531 else status
= errno_to_status( errno
);
5538 struct async_fileio_write
*fileio
;
5540 fileio
= (struct async_fileio_write
*)alloc_fileio( sizeof(*fileio
), async_write_proc
, handle
);
5543 status
= STATUS_NO_MEMORY
;
5546 fileio
->already
= total
;
5547 fileio
->count
= length
;
5548 fileio
->buffer
= buffer
;
5550 SERVER_START_REQ( register_async
)
5552 req
->type
= ASYNC_TYPE_WRITE
;
5553 req
->count
= length
;
5554 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, io
);
5555 status
= wine_server_call( req
);
5559 if (status
!= STATUS_PENDING
) free( fileio
);
5562 else /* synchronous write, wait for the fd to become ready */
5567 if (!timeout_init_done
)
5569 timeout_init_done
= TRUE
;
5570 if ((status
= get_io_timeouts( handle
, type
, length
, FALSE
, &timeouts
)))
5572 if (event
) NtResetEvent( event
, NULL
);
5574 timeout
= get_next_io_timeout( &timeouts
, total
);
5576 pfd
.fd
= unix_handle
;
5577 pfd
.events
= POLLOUT
;
5579 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
5581 /* return with what we got so far */
5582 status
= total
? STATUS_SUCCESS
: STATUS_TIMEOUT
;
5585 if (ret
== -1 && errno
!= EINTR
)
5587 status
= errno_to_status( errno
);
5590 /* will now restart the write */
5595 send_completion
= cvalue
!= 0;
5598 if (needs_close
) close( unix_handle
);
5600 if (type
== FD_TYPE_SERIAL
&& (status
== STATUS_SUCCESS
|| status
== STATUS_PENDING
))
5601 set_pending_write( handle
);
5603 if (status
== STATUS_SUCCESS
)
5605 io
->u
.Status
= status
;
5606 io
->Information
= total
;
5607 TRACE("= SUCCESS (%u)\n", total
);
5608 if (event
) NtSetEvent( event
, NULL
);
5609 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5610 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5614 TRACE("= 0x%08x\n", status
);
5615 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5618 ret_status
= async_write
&& type
== FD_TYPE_FILE
&& status
== STATUS_SUCCESS
? STATUS_PENDING
: status
;
5619 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
5624 /******************************************************************************
5625 * NtWriteFileGather (NTDLL.@)
5627 NTSTATUS WINAPI
NtWriteFileGather( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5628 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
5629 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
5631 int result
, unix_handle
, needs_close
;
5632 unsigned int options
;
5634 ULONG pos
= 0, total
= 0;
5635 enum server_fd_type type
;
5636 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5637 BOOL send_completion
= FALSE
;
5639 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5640 file
, event
, apc
, apc_user
, io
, segments
, length
, offset
, key
);
5642 if (length
% page_size
) return STATUS_INVALID_PARAMETER
;
5643 if (!io
) return STATUS_ACCESS_VIOLATION
;
5645 status
= server_get_unix_fd( file
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5646 if (status
) return status
;
5648 if ((type
!= FD_TYPE_FILE
) ||
5649 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
5650 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
5652 status
= STATUS_INVALID_PARAMETER
;
5658 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5659 result
= pwrite( unix_handle
, (char *)segments
->Buffer
+ pos
,
5660 page_size
- pos
, offset
->QuadPart
+ total
);
5662 result
= write( unix_handle
, (char *)segments
->Buffer
+ pos
, page_size
- pos
);
5666 if (errno
== EINTR
) continue;
5667 if (errno
== EFAULT
)
5669 status
= STATUS_INVALID_USER_BUFFER
;
5672 status
= errno_to_status( errno
);
5677 status
= STATUS_DISK_FULL
;
5682 if ((pos
+= result
) == page_size
)
5689 send_completion
= cvalue
!= 0;
5692 if (needs_close
) close( unix_handle
);
5693 if (status
== STATUS_SUCCESS
)
5695 io
->u
.Status
= status
;
5696 io
->Information
= total
;
5697 TRACE("= SUCCESS (%u)\n", total
);
5698 if (event
) NtSetEvent( event
, NULL
);
5699 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5700 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5704 TRACE("= 0x%08x\n", status
);
5705 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5707 if (send_completion
) add_completion( file
, cvalue
, status
, total
, FALSE
);
5712 /******************************************************************************
5713 * NtDeviceIoControlFile (NTDLL.@)
5715 NTSTATUS WINAPI
NtDeviceIoControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5716 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
5717 void *out_buffer
, ULONG out_size
)
5719 ULONG device
= (code
>> 16);
5720 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
5722 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5723 handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
5727 case FILE_DEVICE_DISK
:
5728 case FILE_DEVICE_CD_ROM
:
5729 case FILE_DEVICE_DVD
:
5730 case FILE_DEVICE_CONTROLLER
:
5731 case FILE_DEVICE_MASS_STORAGE
:
5732 status
= cdrom_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5733 in_buffer
, in_size
, out_buffer
, out_size
);
5735 case FILE_DEVICE_SERIAL_PORT
:
5736 status
= serial_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5737 in_buffer
, in_size
, out_buffer
, out_size
);
5739 case FILE_DEVICE_TAPE
:
5740 status
= tape_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5741 in_buffer
, in_size
, out_buffer
, out_size
);
5745 if (status
== STATUS_NOT_SUPPORTED
|| status
== STATUS_BAD_DEVICE_TYPE
)
5746 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5747 in_buffer
, in_size
, out_buffer
, out_size
);
5749 if (status
!= STATUS_PENDING
) io
->u
.Status
= status
;
5754 /* Tell Valgrind to ignore any holes in structs we will be passing to the
5756 static void ignore_server_ioctl_struct_holes( ULONG code
, const void *in_buffer
, ULONG in_size
)
5758 #ifdef VALGRIND_MAKE_MEM_DEFINED
5759 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
5761 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
5762 if ((size) >= FIELD_OFFSET(t, f2)) \
5763 VALGRIND_MAKE_MEM_DEFINED( \
5764 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
5765 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
5770 case FSCTL_PIPE_WAIT
:
5771 IGNORE_STRUCT_HOLE(in_buffer
, in_size
, FILE_PIPE_WAIT_FOR_BUFFER
, TimeoutSpecified
, Name
);
5778 /******************************************************************************
5779 * NtFsControlFile (NTDLL.@)
5781 NTSTATUS WINAPI
NtFsControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5782 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
5783 void *out_buffer
, ULONG out_size
)
5787 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5788 handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
5790 if (!io
) return STATUS_INVALID_PARAMETER
;
5792 ignore_server_ioctl_struct_holes( code
, in_buffer
, in_size
);
5796 case FSCTL_DISMOUNT_VOLUME
:
5797 status
= server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5798 in_buffer
, in_size
, out_buffer
, out_size
);
5799 if (!status
) status
= unmount_device( handle
);
5802 case FSCTL_PIPE_IMPERSONATE
:
5803 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
5804 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5805 in_buffer
, in_size
, out_buffer
, out_size
);
5807 case FSCTL_IS_VOLUME_MOUNTED
:
5808 case FSCTL_LOCK_VOLUME
:
5809 case FSCTL_UNLOCK_VOLUME
:
5810 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
5811 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
5812 status
= STATUS_SUCCESS
;
5815 case FSCTL_GET_RETRIEVAL_POINTERS
:
5817 RETRIEVAL_POINTERS_BUFFER
*buffer
= (RETRIEVAL_POINTERS_BUFFER
*)out_buffer
;
5819 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
5821 if (out_size
>= sizeof(RETRIEVAL_POINTERS_BUFFER
))
5823 buffer
->ExtentCount
= 1;
5824 buffer
->StartingVcn
.QuadPart
= 1;
5825 buffer
->Extents
[0].NextVcn
.QuadPart
= 0;
5826 buffer
->Extents
[0].Lcn
.QuadPart
= 0;
5827 io
->Information
= sizeof(RETRIEVAL_POINTERS_BUFFER
);
5828 status
= STATUS_SUCCESS
;
5832 io
->Information
= 0;
5833 status
= STATUS_BUFFER_TOO_SMALL
;
5838 case FSCTL_GET_OBJECT_ID
:
5840 FILE_OBJECTID_BUFFER
*info
= out_buffer
;
5841 int fd
, needs_close
;
5844 io
->Information
= 0;
5845 if (out_size
>= sizeof(*info
))
5847 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
5850 if (needs_close
) close( fd
);
5851 memset( info
, 0, sizeof(*info
) );
5852 memcpy( info
->ObjectId
, &st
.st_dev
, sizeof(st
.st_dev
) );
5853 memcpy( info
->ObjectId
+ 8, &st
.st_ino
, sizeof(st
.st_ino
) );
5854 io
->Information
= sizeof(*info
);
5856 else status
= STATUS_BUFFER_TOO_SMALL
;
5860 case FSCTL_SET_SPARSE
:
5861 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
5862 io
->Information
= 0;
5863 status
= STATUS_SUCCESS
;
5866 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5867 in_buffer
, in_size
, out_buffer
, out_size
);
5870 if (status
!= STATUS_PENDING
) io
->u
.Status
= status
;
5875 /******************************************************************************
5876 * NtFlushBuffersFile (NTDLL.@)
5878 NTSTATUS WINAPI
NtFlushBuffersFile( HANDLE handle
, IO_STATUS_BLOCK
*io
)
5882 enum server_fd_type type
;
5883 int fd
, needs_close
;
5885 if (!io
|| !virtual_check_buffer_for_write( io
, sizeof(*io
) )) return STATUS_ACCESS_VIOLATION
;
5887 ret
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, &type
, NULL
);
5888 if (ret
== STATUS_ACCESS_DENIED
)
5889 ret
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &fd
, &needs_close
, &type
, NULL
);
5891 if (!ret
&& (type
== FD_TYPE_FILE
|| type
== FD_TYPE_DIR
|| type
== FD_TYPE_CHAR
))
5893 if (fsync(fd
)) ret
= errno_to_status( errno
);
5895 io
->Information
= 0;
5897 else if (!ret
&& type
== FD_TYPE_SERIAL
)
5899 ret
= serial_FlushBuffersFile( fd
);
5901 else if (ret
!= STATUS_ACCESS_DENIED
)
5903 struct async_irp
*async
;
5905 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
5906 return STATUS_NO_MEMORY
;
5907 async
->buffer
= NULL
;
5910 SERVER_START_REQ( flush
)
5912 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, io
);
5913 ret
= wine_server_call( req
);
5914 wait_handle
= wine_server_ptr_handle( reply
->event
);
5915 if (wait_handle
&& ret
!= STATUS_PENDING
)
5918 io
->Information
= 0;
5923 if (ret
!= STATUS_PENDING
) free( async
);
5925 if (wait_handle
) ret
= wait_async( wait_handle
, FALSE
, io
);
5928 if (needs_close
) close( fd
);
5933 /**************************************************************************
5934 * NtCancelIoFile (NTDLL.@)
5936 NTSTATUS WINAPI
NtCancelIoFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
)
5938 TRACE( "%p %p\n", handle
, io_status
);
5940 SERVER_START_REQ( cancel_async
)
5942 req
->handle
= wine_server_obj_handle( handle
);
5943 req
->only_thread
= TRUE
;
5944 io_status
->u
.Status
= wine_server_call( req
);
5947 return io_status
->u
.Status
;
5951 /**************************************************************************
5952 * NtCancelIoFileEx (NTDLL.@)
5954 NTSTATUS WINAPI
NtCancelIoFileEx( HANDLE handle
, IO_STATUS_BLOCK
*io
, IO_STATUS_BLOCK
*io_status
)
5956 TRACE( "%p %p %p\n", handle
, io
, io_status
);
5958 SERVER_START_REQ( cancel_async
)
5960 req
->handle
= wine_server_obj_handle( handle
);
5961 req
->iosb
= wine_server_client_ptr( io
);
5962 io_status
->u
.Status
= wine_server_call( req
);
5965 return io_status
->u
.Status
;
5969 /******************************************************************
5970 * NtLockFile (NTDLL.@)
5972 NTSTATUS WINAPI
NtLockFile( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void* apc_user
,
5973 IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
5974 LARGE_INTEGER
*count
, ULONG
*key
, BOOLEAN dont_wait
, BOOLEAN exclusive
)
5981 if (apc
|| io_status
|| key
)
5983 FIXME("Unimplemented yet parameter\n");
5984 return STATUS_NOT_IMPLEMENTED
;
5986 if (apc_user
&& !warn
++) FIXME("I/O completion on lock not implemented yet\n");
5990 SERVER_START_REQ( lock_file
)
5992 req
->handle
= wine_server_obj_handle( file
);
5993 req
->offset
= offset
->QuadPart
;
5994 req
->count
= count
->QuadPart
;
5995 req
->shared
= !exclusive
;
5996 req
->wait
= !dont_wait
;
5997 ret
= wine_server_call( req
);
5998 handle
= wine_server_ptr_handle( reply
->handle
);
5999 async
= reply
->overlapped
;
6002 if (ret
!= STATUS_PENDING
)
6004 if (!ret
&& event
) NtSetEvent( event
, NULL
);
6009 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6010 if (handle
) NtClose( handle
);
6011 return STATUS_PENDING
;
6015 NtWaitForSingleObject( handle
, FALSE
, NULL
);
6018 else /* Unix lock conflict, sleep a bit and retry */
6021 time
.QuadPart
= -100 * (ULONGLONG
)10000;
6022 NtDelayExecution( FALSE
, &time
);
6028 /******************************************************************
6029 * NtUnlockFile (NTDLL.@)
6031 NTSTATUS WINAPI
NtUnlockFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
6032 LARGE_INTEGER
*count
, ULONG
*key
)
6036 TRACE( "%p %x%08x %x%08x\n",
6037 handle
, offset
->u
.HighPart
, offset
->u
.LowPart
, count
->u
.HighPart
, count
->u
.LowPart
);
6039 if (io_status
|| key
)
6041 FIXME("Unimplemented yet parameter\n");
6042 return STATUS_NOT_IMPLEMENTED
;
6045 SERVER_START_REQ( unlock_file
)
6047 req
->handle
= wine_server_obj_handle( handle
);
6048 req
->offset
= offset
->QuadPart
;
6049 req
->count
= count
->QuadPart
;
6050 status
= wine_server_call( req
);
6057 static NTSTATUS
read_changes_apc( void *user
, IO_STATUS_BLOCK
*iosb
, NTSTATUS status
)
6059 struct async_fileio_read_changes
*fileio
= user
;
6062 if (status
== STATUS_ALERTED
)
6064 SERVER_START_REQ( read_change
)
6066 req
->handle
= wine_server_obj_handle( fileio
->io
.handle
);
6067 wine_server_set_reply( req
, fileio
->data
, fileio
->data_size
);
6068 status
= wine_server_call( req
);
6069 size
= wine_server_reply_size( reply
);
6073 if (status
== STATUS_SUCCESS
&& fileio
->buffer
)
6075 FILE_NOTIFY_INFORMATION
*pfni
= fileio
->buffer
;
6076 int i
, left
= fileio
->buffer_size
;
6077 DWORD
*last_entry_offset
= NULL
;
6078 struct filesystem_event
*event
= (struct filesystem_event
*)fileio
->data
;
6080 while (size
&& left
>= sizeof(*pfni
))
6082 DWORD len
= (left
- offsetof(FILE_NOTIFY_INFORMATION
, FileName
)) / sizeof(WCHAR
);
6084 /* convert to an NT style path */
6085 for (i
= 0; i
< event
->len
; i
++)
6086 if (event
->name
[i
] == '/') event
->name
[i
] = '\\';
6088 pfni
->Action
= event
->action
;
6089 pfni
->FileNameLength
= ntdll_umbstowcs( event
->name
, event
->len
, pfni
->FileName
, len
);
6090 last_entry_offset
= &pfni
->NextEntryOffset
;
6092 if (pfni
->FileNameLength
== len
) break;
6094 i
= offsetof(FILE_NOTIFY_INFORMATION
, FileName
[pfni
->FileNameLength
]);
6095 pfni
->FileNameLength
*= sizeof(WCHAR
);
6096 pfni
->NextEntryOffset
= i
;
6097 pfni
= (FILE_NOTIFY_INFORMATION
*)((char*)pfni
+ i
);
6100 i
= (offsetof(struct filesystem_event
, name
[event
->len
])
6101 + sizeof(int)-1) / sizeof(int) * sizeof(int);
6102 event
= (struct filesystem_event
*)((char*)event
+ i
);
6108 status
= STATUS_NOTIFY_ENUM_DIR
;
6113 if (last_entry_offset
) *last_entry_offset
= 0;
6114 size
= fileio
->buffer_size
- left
;
6119 status
= STATUS_NOTIFY_ENUM_DIR
;
6124 if (status
!= STATUS_PENDING
)
6126 iosb
->u
.Status
= status
;
6127 iosb
->Information
= size
;
6128 release_fileio( &fileio
->io
);
6133 #define FILE_NOTIFY_ALL ( \
6134 FILE_NOTIFY_CHANGE_FILE_NAME | \
6135 FILE_NOTIFY_CHANGE_DIR_NAME | \
6136 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
6137 FILE_NOTIFY_CHANGE_SIZE | \
6138 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6139 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6140 FILE_NOTIFY_CHANGE_CREATION | \
6141 FILE_NOTIFY_CHANGE_SECURITY )
6143 /******************************************************************************
6144 * NtNotifyChangeDirectoryFile (NTDLL.@)
6146 NTSTATUS WINAPI
NtNotifyChangeDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
,
6147 void *apc_context
, IO_STATUS_BLOCK
*iosb
, void *buffer
,
6148 ULONG buffer_size
, ULONG filter
, BOOLEAN subtree
)
6150 struct async_fileio_read_changes
*fileio
;
6152 ULONG size
= max( 4096, buffer_size
);
6154 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6155 handle
, event
, apc
, apc_context
, iosb
, buffer
, buffer_size
, filter
, subtree
);
6157 if (!iosb
) return STATUS_ACCESS_VIOLATION
;
6158 if (filter
== 0 || (filter
& ~FILE_NOTIFY_ALL
)) return STATUS_INVALID_PARAMETER
;
6160 fileio
= (struct async_fileio_read_changes
*)alloc_fileio(
6161 offsetof(struct async_fileio_read_changes
, data
[size
]), read_changes_apc
, handle
);
6162 if (!fileio
) return STATUS_NO_MEMORY
;
6164 fileio
->buffer
= buffer
;
6165 fileio
->buffer_size
= buffer_size
;
6166 fileio
->data_size
= size
;
6168 SERVER_START_REQ( read_directory_changes
)
6170 req
->filter
= filter
;
6171 req
->want_data
= (buffer
!= NULL
);
6172 req
->subtree
= subtree
;
6173 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_context
, iosb
);
6174 status
= wine_server_call( req
);
6178 if (status
!= STATUS_PENDING
) free( fileio
);
6183 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6184 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6185 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION
*info
, const char *fstypename
,
6186 unsigned int flags
)
6188 if (!strcmp("cd9660", fstypename
) || !strcmp("udf", fstypename
))
6190 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6191 /* Don't assume read-only, let the mount options set it below */
6192 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6194 else if (!strcmp("nfs", fstypename
) || !strcmp("nwfs", fstypename
) ||
6195 !strcmp("smbfs", fstypename
) || !strcmp("afpfs", fstypename
))
6197 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6198 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6200 else if (!strcmp("procfs", fstypename
))
6201 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6203 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6205 if (flags
& MNT_RDONLY
)
6206 info
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
6208 if (!(flags
& MNT_LOCAL
))
6210 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6211 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6216 static inline BOOL
is_device_placeholder( int fd
)
6218 static const char wine_placeholder
[] = "Wine device placeholder";
6219 char buffer
[sizeof(wine_placeholder
)-1];
6221 if (pread( fd
, buffer
, sizeof(wine_placeholder
) - 1, 0 ) != sizeof(wine_placeholder
) - 1)
6223 return !memcmp( buffer
, wine_placeholder
, sizeof(wine_placeholder
) - 1 );
6226 static NTSTATUS
get_device_info( int fd
, FILE_FS_DEVICE_INFORMATION
*info
)
6230 info
->Characteristics
= 0;
6231 if (fstat( fd
, &st
) < 0) return errno_to_status( errno
);
6232 if (S_ISCHR( st
.st_mode
))
6234 info
->DeviceType
= FILE_DEVICE_UNKNOWN
;
6236 switch(major(st
.st_rdev
))
6239 info
->DeviceType
= FILE_DEVICE_NULL
;
6242 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6245 info
->DeviceType
= FILE_DEVICE_PARALLEL_PORT
;
6247 case SCSI_TAPE_MAJOR
:
6248 info
->DeviceType
= FILE_DEVICE_TAPE
;
6251 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6254 if (ioctl(fd
, FIODTYPE
, &d_type
) == 0)
6259 info
->DeviceType
= FILE_DEVICE_TAPE
;
6262 info
->DeviceType
= FILE_DEVICE_DISK
;
6265 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6267 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6269 info
->DeviceType
= FILE_DEVICE_NULL
;
6273 /* no special d_type for parallel ports */
6278 else if (S_ISBLK( st
.st_mode
))
6280 info
->DeviceType
= FILE_DEVICE_DISK
;
6282 else if (S_ISFIFO( st
.st_mode
) || S_ISSOCK( st
.st_mode
))
6284 info
->DeviceType
= FILE_DEVICE_NAMED_PIPE
;
6286 else if (is_device_placeholder( fd
))
6288 info
->DeviceType
= FILE_DEVICE_DISK
;
6290 else /* regular file or directory */
6292 #if defined(linux) && defined(HAVE_FSTATFS)
6295 /* check for floppy disk */
6296 if (major(st
.st_dev
) == FLOPPY_MAJOR
)
6297 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6299 if (fstatfs( fd
, &stfs
) < 0) stfs
.f_type
= 0;
6300 switch (stfs
.f_type
)
6302 case 0x9660: /* iso9660 */
6303 case 0x9fa1: /* supermount */
6304 case 0x15013346: /* udf */
6305 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6306 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6308 case 0x6969: /* nfs */
6309 case 0xff534d42: /* cifs */
6310 case 0xfe534d42: /* smb2 */
6311 case 0x517b: /* smbfs */
6312 case 0x564c: /* ncpfs */
6313 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6314 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6316 case 0x1373: /* devfs */
6317 case 0x9fa0: /* procfs */
6318 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6320 case 0x01021994: /* tmpfs */
6321 case 0x28cd3d45: /* cramfs */
6322 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6323 * filesystems are rare on Windows, and some programs refuse to
6324 * recognize them as valid. */
6326 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6329 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6332 if (fstatfs( fd
, &stfs
) < 0)
6333 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6335 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flags
);
6336 #elif defined(__NetBSD__)
6337 struct statvfs stfs
;
6339 if (fstatvfs( fd
, &stfs
) < 0)
6340 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6342 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flag
);
6344 /* Use dkio to work out device types */
6346 # include <sys/dkio.h>
6347 # include <sys/vtoc.h>
6348 struct dk_cinfo dkinf
;
6349 int retval
= ioctl(fd
, DKIOCINFO
, &dkinf
);
6351 WARN("Unable to get disk device type information - assuming a disk like device\n");
6352 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6354 switch (dkinf
.dki_ctype
)
6357 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6358 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6362 case DKC_INTEL82072
:
6363 case DKC_INTEL82077
:
6364 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6365 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6368 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6369 * filesystems are rare on Windows, and some programs refuse to
6370 * recognize them as valid. */
6372 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6377 if (!warned
++) FIXME( "device info not properly supported on this platform\n" );
6378 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6380 info
->Characteristics
|= FILE_DEVICE_IS_MOUNTED
;
6382 return STATUS_SUCCESS
;
6386 /******************************************************************************
6387 * NtQueryVolumeInformationFile (NTDLL.@)
6389 NTSTATUS WINAPI
NtQueryVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
6390 void *buffer
, ULONG length
,
6391 FS_INFORMATION_CLASS info_class
)
6393 int fd
, needs_close
;
6396 io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
6397 if (io
->u
.Status
== STATUS_BAD_DEVICE_TYPE
)
6399 struct async_irp
*async
;
6403 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
6404 return STATUS_NO_MEMORY
;
6405 async
->buffer
= buffer
;
6406 async
->size
= length
;
6408 SERVER_START_REQ( get_volume_info
)
6410 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, io
);
6411 req
->handle
= wine_server_obj_handle( handle
);
6412 req
->info_class
= info_class
;
6413 wine_server_set_reply( req
, buffer
, length
);
6414 status
= wine_server_call( req
);
6415 if (status
!= STATUS_PENDING
)
6417 io
->u
.Status
= status
;
6418 io
->Information
= wine_server_reply_size( reply
);
6420 wait_handle
= wine_server_ptr_handle( reply
->wait
);
6423 if (status
!= STATUS_PENDING
) free( async
);
6424 if (wait_handle
) status
= wait_async( wait_handle
, FALSE
, io
);
6427 else if (io
->u
.Status
) return io
->u
.Status
;
6429 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
6430 io
->Information
= 0;
6432 switch( info_class
)
6434 case FileFsLabelInformation
:
6435 FIXME( "%p: label info not supported\n", handle
);
6438 case FileFsSizeInformation
:
6439 if (length
< sizeof(FILE_FS_SIZE_INFORMATION
))
6440 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
6443 FILE_FS_SIZE_INFORMATION
*info
= buffer
;
6445 if (fstat( fd
, &st
) < 0)
6447 io
->u
.Status
= errno_to_status( errno
);
6450 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
6452 io
->u
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
6457 /* Linux's fstatvfs is buggy */
6458 #if !defined(linux) || !defined(HAVE_FSTATFS)
6459 struct statvfs stfs
;
6461 if (fstatvfs( fd
, &stfs
) < 0)
6463 io
->u
.Status
= errno_to_status( errno
);
6466 bsize
= stfs
.f_frsize
;
6469 if (fstatfs( fd
, &stfs
) < 0)
6471 io
->u
.Status
= errno_to_status( errno
);
6474 bsize
= stfs
.f_bsize
;
6476 if (bsize
== 2048) /* assume CD-ROM */
6478 info
->BytesPerSector
= 2048;
6479 info
->SectorsPerAllocationUnit
= 1;
6483 info
->BytesPerSector
= 512;
6484 info
->SectorsPerAllocationUnit
= 8;
6486 info
->TotalAllocationUnits
.QuadPart
= bsize
* stfs
.f_blocks
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
6487 info
->AvailableAllocationUnits
.QuadPart
= bsize
* stfs
.f_bavail
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
6488 io
->Information
= sizeof(*info
);
6489 io
->u
.Status
= STATUS_SUCCESS
;
6494 case FileFsDeviceInformation
:
6495 if (length
< sizeof(FILE_FS_DEVICE_INFORMATION
))
6496 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
6499 FILE_FS_DEVICE_INFORMATION
*info
= buffer
;
6501 if ((io
->u
.Status
= get_device_info( fd
, info
)) == STATUS_SUCCESS
)
6502 io
->Information
= sizeof(*info
);
6506 case FileFsAttributeInformation
:
6508 static const WCHAR fatW
[] = {'F','A','T'};
6509 static const WCHAR fat32W
[] = {'F','A','T','3','2'};
6510 static const WCHAR ntfsW
[] = {'N','T','F','S'};
6511 static const WCHAR cdfsW
[] = {'C','D','F','S'};
6512 static const WCHAR udfW
[] = {'U','D','F'};
6514 FILE_FS_ATTRIBUTE_INFORMATION
*info
= buffer
;
6515 struct mountmgr_unix_drive drive
;
6516 enum mountmgr_fs_type fs_type
= MOUNTMGR_FS_TYPE_NTFS
;
6518 if (length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
))
6520 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
6524 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) )) fs_type
= drive
.fs_type
;
6529 if (!fstatfs( fd
, &stfs
))
6531 #if defined(linux) && defined(HAVE_FSTATFS)
6532 switch (stfs
.f_type
)
6535 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6538 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6541 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6544 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6545 if (!strcmp( stfs
.f_fstypename
, "cd9660" ))
6546 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6547 else if (!strcmp( stfs
.f_fstypename
, "udf" ))
6548 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6549 else if (!strcmp( stfs
.f_fstypename
, "msdos" )) /* FreeBSD < 5, Apple */
6550 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6551 else if (!strcmp( stfs
.f_fstypename
, "msdosfs" )) /* FreeBSD >= 5 */
6552 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6559 case MOUNTMGR_FS_TYPE_ISO9660
:
6560 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
;
6561 info
->MaximumComponentNameLength
= 221;
6562 info
->FileSystemNameLength
= min( sizeof(cdfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6563 memcpy(info
->FileSystemName
, cdfsW
, info
->FileSystemNameLength
);
6565 case MOUNTMGR_FS_TYPE_UDF
:
6566 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
| FILE_UNICODE_ON_DISK
| FILE_CASE_SENSITIVE_SEARCH
;
6567 info
->MaximumComponentNameLength
= 255;
6568 info
->FileSystemNameLength
= min( sizeof(udfW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6569 memcpy(info
->FileSystemName
, udfW
, info
->FileSystemNameLength
);
6571 case MOUNTMGR_FS_TYPE_FAT
:
6572 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
6573 info
->MaximumComponentNameLength
= 255;
6574 info
->FileSystemNameLength
= min( sizeof(fatW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6575 memcpy(info
->FileSystemName
, fatW
, info
->FileSystemNameLength
);
6577 case MOUNTMGR_FS_TYPE_FAT32
:
6578 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
6579 info
->MaximumComponentNameLength
= 255;
6580 info
->FileSystemNameLength
= min( sizeof(fat32W
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6581 memcpy(info
->FileSystemName
, fat32W
, info
->FileSystemNameLength
);
6584 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_PERSISTENT_ACLS
;
6585 info
->MaximumComponentNameLength
= 255;
6586 info
->FileSystemNameLength
= min( sizeof(ntfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6587 memcpy(info
->FileSystemName
, ntfsW
, info
->FileSystemNameLength
);
6591 io
->Information
= offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) + info
->FileSystemNameLength
;
6592 io
->u
.Status
= STATUS_SUCCESS
;
6596 case FileFsVolumeInformation
:
6598 FILE_FS_VOLUME_INFORMATION
*info
= buffer
;
6600 struct mountmgr_unix_drive
*drive
= (struct mountmgr_unix_drive
*)data
;
6603 if (length
< sizeof(FILE_FS_VOLUME_INFORMATION
))
6605 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
6609 if (get_mountmgr_fs_info( handle
, fd
, drive
, sizeof(data
) ))
6611 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
6615 label
= (WCHAR
*)((char *)drive
+ drive
->label_offset
);
6616 info
->VolumeCreationTime
.QuadPart
= 0; /* FIXME */
6617 info
->VolumeSerialNumber
= drive
->serial
;
6618 info
->VolumeLabelLength
= min( wcslen( label
) * sizeof(WCHAR
),
6619 length
- offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) );
6620 info
->SupportsObjects
= (drive
->fs_type
== MOUNTMGR_FS_TYPE_NTFS
);
6621 memcpy( info
->VolumeLabel
, label
, info
->VolumeLabelLength
);
6622 io
->Information
= offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) + info
->VolumeLabelLength
;
6623 io
->u
.Status
= STATUS_SUCCESS
;
6627 case FileFsControlInformation
:
6628 FIXME( "%p: control info not supported\n", handle
);
6631 case FileFsFullSizeInformation
:
6632 FIXME( "%p: full size info not supported\n", handle
);
6635 case FileFsObjectIdInformation
:
6636 FIXME( "%p: object id info not supported\n", handle
);
6639 case FileFsMaximumInformation
:
6640 FIXME( "%p: maximum info not supported\n", handle
);
6644 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
6647 if (needs_close
) close( fd
);
6648 return io
->u
.Status
;
6652 /******************************************************************************
6653 * NtSetVolumeInformationFile (NTDLL.@)
6655 NTSTATUS WINAPI
NtSetVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *info
,
6656 ULONG length
, FS_INFORMATION_CLASS
class )
6658 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle
, io
, info
, length
, class );
6659 return STATUS_SUCCESS
;
6663 /******************************************************************
6664 * NtQueryEaFile (NTDLL.@)
6666 NTSTATUS WINAPI
NtQueryEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
6667 BOOLEAN single_entry
, void *list
, ULONG list_len
,
6668 ULONG
*index
, BOOLEAN restart
)
6670 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) stub\n",
6671 handle
, io
, buffer
, length
, single_entry
, list
, list_len
, index
, restart
);
6672 return STATUS_ACCESS_DENIED
;
6676 /******************************************************************
6677 * NtSetEaFile (NTDLL.@)
6679 NTSTATUS WINAPI
NtSetEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
)
6681 FIXME( "(%p,%p,%p,%d) stub\n", handle
, io
, buffer
, length
);
6682 return STATUS_ACCESS_DENIED
;
6686 /* convert type information from server format; helper for NtQueryObject */
6687 static void *put_object_type_info( OBJECT_TYPE_INFORMATION
*p
, struct object_type_info
*info
)
6689 const ULONG align
= sizeof(DWORD_PTR
) - 1;
6691 memset( p
, 0, sizeof(*p
) );
6692 p
->TypeName
.Buffer
= (WCHAR
*)(p
+ 1);
6693 p
->TypeName
.Length
= info
->name_len
;
6694 p
->TypeName
.MaximumLength
= info
->name_len
+ sizeof(WCHAR
);
6695 p
->TotalNumberOfObjects
= info
->obj_count
;
6696 p
->TotalNumberOfHandles
= info
->handle_count
;
6697 p
->HighWaterNumberOfObjects
= info
->obj_max
;
6698 p
->HighWaterNumberOfHandles
= info
->handle_max
;
6699 p
->TypeIndex
= info
->index
+ 2;
6700 p
->GenericMapping
.GenericRead
= info
->mapping
.read
;
6701 p
->GenericMapping
.GenericWrite
= info
->mapping
.write
;
6702 p
->GenericMapping
.GenericExecute
= info
->mapping
.exec
;
6703 p
->GenericMapping
.GenericAll
= info
->mapping
.all
;
6704 p
->ValidAccessMask
= info
->valid_access
;
6705 memcpy( p
->TypeName
.Buffer
, info
+ 1, info
->name_len
);
6706 p
->TypeName
.Buffer
[info
->name_len
/ sizeof(WCHAR
)] = 0;
6707 return (char *)(p
+ 1) + ((p
->TypeName
.MaximumLength
+ align
) & ~align
);
6710 /**************************************************************************
6711 * NtQueryObject (NTDLL.@)
6713 NTSTATUS WINAPI
NtQueryObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
6714 void *ptr
, ULONG len
, ULONG
*used_len
)
6718 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle
, info_class
, ptr
, len
, used_len
);
6720 if (used_len
) *used_len
= 0;
6724 case ObjectBasicInformation
:
6726 OBJECT_BASIC_INFORMATION
*p
= ptr
;
6728 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
6730 SERVER_START_REQ( get_object_info
)
6732 req
->handle
= wine_server_obj_handle( handle
);
6733 status
= wine_server_call( req
);
6734 if (status
== STATUS_SUCCESS
)
6736 memset( p
, 0, sizeof(*p
) );
6737 p
->GrantedAccess
= reply
->access
;
6738 p
->PointerCount
= reply
->ref_count
;
6739 p
->HandleCount
= reply
->handle_count
;
6740 if (used_len
) *used_len
= sizeof(*p
);
6747 case ObjectNameInformation
:
6749 OBJECT_NAME_INFORMATION
*p
= ptr
;
6753 /* first try as a file object */
6755 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
6757 if (!(status
= unix_to_nt_file_name( unix_name
, &nt_name
)))
6759 ULONG size
= (wcslen(nt_name
) + 1) * sizeof(WCHAR
);
6760 if (len
< sizeof(*p
)) status
= STATUS_INFO_LENGTH_MISMATCH
;
6761 else if (len
< sizeof(*p
) + size
) status
= STATUS_BUFFER_OVERFLOW
;
6764 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
6765 p
->Name
.Length
= size
- sizeof(WCHAR
);
6766 p
->Name
.MaximumLength
= size
;
6767 wcscpy( p
->Name
.Buffer
, nt_name
);
6769 if (used_len
) *used_len
= sizeof(*p
) + size
;
6775 else if (status
!= STATUS_OBJECT_TYPE_MISMATCH
) break;
6777 /* not a file, treat as a generic object */
6779 SERVER_START_REQ( get_object_info
)
6781 req
->handle
= wine_server_obj_handle( handle
);
6782 if (len
> sizeof(*p
)) wine_server_set_reply( req
, p
+ 1, len
- sizeof(*p
) );
6783 status
= wine_server_call( req
);
6784 if (status
== STATUS_SUCCESS
)
6786 if (!reply
->total
) /* no name */
6788 if (sizeof(*p
) > len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
6789 else memset( p
, 0, sizeof(*p
) );
6790 if (used_len
) *used_len
= sizeof(*p
);
6792 else if (sizeof(*p
) + reply
->total
+ sizeof(WCHAR
) > len
)
6794 if (used_len
) *used_len
= sizeof(*p
) + reply
->total
+ sizeof(WCHAR
);
6795 status
= STATUS_INFO_LENGTH_MISMATCH
;
6799 ULONG res
= wine_server_reply_size( reply
);
6800 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
6801 p
->Name
.Length
= res
;
6802 p
->Name
.MaximumLength
= res
+ sizeof(WCHAR
);
6803 p
->Name
.Buffer
[res
/ sizeof(WCHAR
)] = 0;
6804 if (used_len
) *used_len
= sizeof(*p
) + p
->Name
.MaximumLength
;
6812 case ObjectTypeInformation
:
6814 OBJECT_TYPE_INFORMATION
*p
= ptr
;
6815 char buffer
[sizeof(struct object_type_info
) + 64];
6816 struct object_type_info
*info
= (struct object_type_info
*)buffer
;
6818 SERVER_START_REQ( get_object_type
)
6820 req
->handle
= wine_server_obj_handle( handle
);
6821 wine_server_set_reply( req
, buffer
, sizeof(buffer
) );
6822 status
= wine_server_call( req
);
6826 if (sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
) <= len
)
6828 put_object_type_info( p
, info
);
6829 if (used_len
) *used_len
= sizeof(*p
) + p
->TypeName
.MaximumLength
;
6833 if (used_len
) *used_len
= sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
);
6834 status
= STATUS_INFO_LENGTH_MISMATCH
;
6839 case ObjectTypesInformation
:
6841 OBJECT_TYPES_INFORMATION
*types
= ptr
;
6842 OBJECT_TYPE_INFORMATION
*p
;
6843 struct object_type_info
*buffer
;
6844 /* assume at most 32 types, with an average 16-char name */
6845 ULONG size
= 32 * (sizeof(struct object_type_info
) + 16 * sizeof(WCHAR
));
6846 ULONG i
, count
, pos
, total
, align
= sizeof(DWORD_PTR
) - 1;
6848 buffer
= malloc( size
);
6849 SERVER_START_REQ( get_object_types
)
6851 wine_server_set_reply( req
, buffer
, size
);
6852 status
= wine_server_call( req
);
6853 count
= reply
->count
;
6858 if (len
>= sizeof(*types
)) types
->NumberOfTypes
= count
;
6859 total
= (sizeof(*types
) + align
) & ~align
;
6860 p
= (OBJECT_TYPE_INFORMATION
*)((char *)ptr
+ total
);
6861 for (i
= pos
= 0; i
< count
; i
++)
6863 struct object_type_info
*info
= (struct object_type_info
*)((char *)buffer
+ pos
);
6864 pos
+= sizeof(*info
) + ((info
->name_len
+ 3) & ~3);
6865 total
+= sizeof(*p
) + ((info
->name_len
+ sizeof(WCHAR
) + align
) & ~align
);
6866 if (total
<= len
) p
= put_object_type_info( p
, info
);
6868 if (used_len
) *used_len
= total
;
6869 if (total
> len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
6871 else if (status
== STATUS_BUFFER_OVERFLOW
) FIXME( "size %u too small\n", size
);
6877 case ObjectDataInformation
:
6879 OBJECT_DATA_INFORMATION
* p
= ptr
;
6881 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
6883 SERVER_START_REQ( set_handle_info
)
6885 req
->handle
= wine_server_obj_handle( handle
);
6888 status
= wine_server_call( req
);
6889 if (status
== STATUS_SUCCESS
)
6891 p
->InheritHandle
= (reply
->old_flags
& HANDLE_FLAG_INHERIT
) != 0;
6892 p
->ProtectFromClose
= (reply
->old_flags
& HANDLE_FLAG_PROTECT_FROM_CLOSE
) != 0;
6893 if (used_len
) *used_len
= sizeof(*p
);
6901 FIXME("Unsupported information class %u\n", info_class
);
6902 status
= STATUS_NOT_IMPLEMENTED
;
6909 /**************************************************************************
6910 * NtSetInformationObject (NTDLL.@)
6912 NTSTATUS WINAPI
NtSetInformationObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
6913 void *ptr
, ULONG len
)
6917 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle
, info_class
, ptr
, len
);
6921 case ObjectDataInformation
:
6923 OBJECT_DATA_INFORMATION
* p
= ptr
;
6925 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
6927 SERVER_START_REQ( set_handle_info
)
6929 req
->handle
= wine_server_obj_handle( handle
);
6930 req
->mask
= HANDLE_FLAG_INHERIT
| HANDLE_FLAG_PROTECT_FROM_CLOSE
;
6931 if (p
->InheritHandle
) req
->flags
|= HANDLE_FLAG_INHERIT
;
6932 if (p
->ProtectFromClose
) req
->flags
|= HANDLE_FLAG_PROTECT_FROM_CLOSE
;
6933 status
= wine_server_call( req
);
6940 FIXME("Unsupported information class %u\n", info_class
);
6941 status
= STATUS_NOT_IMPLEMENTED
;