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
);
1971 drive
->unix_dev
= st
.st_rdev
? st
.st_rdev
: st
.st_dev
;
1975 drive
->letter
= 'a' + letter
;
1977 string
.Buffer
= (WCHAR
*)MOUNTMGR_DEVICE_NAME
;
1978 string
.Length
= sizeof(MOUNTMGR_DEVICE_NAME
) - sizeof(WCHAR
);
1979 InitializeObjectAttributes( &attr
, &string
, 0, NULL
, NULL
);
1980 status
= NtOpenFile( &mountmgr
, GENERIC_READ
| SYNCHRONIZE
, &attr
, &io
,
1981 FILE_SHARE_READ
| FILE_SHARE_WRITE
, FILE_SYNCHRONOUS_IO_NONALERT
);
1982 if (status
) return status
;
1984 status
= NtDeviceIoControlFile( mountmgr
, NULL
, NULL
, NULL
, &io
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
,
1985 drive
, sizeof(*drive
), drive
, size
);
1986 NtClose( mountmgr
);
1987 if (status
== STATUS_BUFFER_OVERFLOW
) status
= STATUS_SUCCESS
;
1988 else if (status
) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status
);
1993 /***********************************************************************
1994 * get_dir_data_entry
1996 * Return a directory entry from the cached data.
1998 static NTSTATUS
get_dir_data_entry( struct dir_data
*dir_data
, void *info_ptr
, IO_STATUS_BLOCK
*io
,
1999 ULONG max_length
, FILE_INFORMATION_CLASS
class,
2000 union file_directory_info
**last_info
)
2002 const struct dir_data_names
*names
= &dir_data
->names
[dir_data
->pos
];
2003 union file_directory_info
*info
;
2005 ULONG name_len
, start
, dir_size
, attributes
;
2007 if (get_file_info( names
->unix_name
, &st
, &attributes
) == -1)
2009 TRACE( "file no longer exists %s\n", names
->unix_name
);
2010 return STATUS_SUCCESS
;
2012 if (is_ignored_file( &st
))
2014 TRACE( "ignoring file %s\n", names
->unix_name
);
2015 return STATUS_SUCCESS
;
2017 start
= dir_info_align( io
->Information
);
2018 dir_size
= dir_info_size( class, 0 );
2019 if (start
+ dir_size
> max_length
) return STATUS_MORE_ENTRIES
;
2021 max_length
-= start
+ dir_size
;
2022 name_len
= wcslen( names
->long_name
) * sizeof(WCHAR
);
2023 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2024 if (*last_info
&& name_len
> max_length
) return STATUS_MORE_ENTRIES
;
2026 info
= (union file_directory_info
*)((char *)info_ptr
+ start
);
2027 info
->dir
.NextEntryOffset
= 0;
2028 info
->dir
.FileIndex
= 0; /* NTFS always has 0 here, so let's not bother with it */
2030 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2031 if (class != FileNamesInformation
)
2033 if (st
.st_dev
!= dir_data
->id
.dev
) st
.st_ino
= 0; /* ignore inode if on a different device */
2035 if (!show_dot_files
&& names
->long_name
[0] == '.' && names
->long_name
[1] &&
2036 (names
->long_name
[1] != '.' || names
->long_name
[2]))
2037 attributes
|= FILE_ATTRIBUTE_HIDDEN
;
2039 fill_file_info( &st
, attributes
, info
, class );
2044 case FileDirectoryInformation
:
2045 info
->dir
.FileNameLength
= name_len
;
2048 case FileFullDirectoryInformation
:
2049 info
->full
.EaSize
= 0; /* FIXME */
2050 info
->full
.FileNameLength
= name_len
;
2053 case FileIdFullDirectoryInformation
:
2054 info
->id_full
.EaSize
= 0; /* FIXME */
2055 info
->id_full
.FileNameLength
= name_len
;
2058 case FileBothDirectoryInformation
:
2059 info
->both
.EaSize
= 0; /* FIXME */
2060 info
->both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2061 memcpy( info
->both
.ShortName
, names
->short_name
, info
->both
.ShortNameLength
);
2062 info
->both
.FileNameLength
= name_len
;
2065 case FileIdBothDirectoryInformation
:
2066 info
->id_both
.EaSize
= 0; /* FIXME */
2067 info
->id_both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2068 memcpy( info
->id_both
.ShortName
, names
->short_name
, info
->id_both
.ShortNameLength
);
2069 info
->id_both
.FileNameLength
= name_len
;
2072 case FileIdGlobalTxDirectoryInformation
:
2073 info
->id_tx
.TxInfoFlags
= 0;
2074 info
->id_tx
.FileNameLength
= name_len
;
2077 case FileNamesInformation
:
2078 info
->names
.FileNameLength
= name_len
;
2086 memcpy( (char *)info
+ dir_size
, names
->long_name
, min( name_len
, max_length
) );
2087 io
->Information
= start
+ dir_size
+ min( name_len
, max_length
);
2088 if (*last_info
) (*last_info
)->next
= (char *)info
- (char *)*last_info
;
2090 return name_len
> max_length
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
2093 #ifdef VFAT_IOCTL_READDIR_BOTH
2095 /***********************************************************************
2096 * read_directory_vfat
2098 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2100 static NTSTATUS
read_directory_data_vfat( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2102 char *short_name
, *long_name
;
2103 KERNEL_DIRENT de
[2];
2104 NTSTATUS status
= STATUS_NO_MEMORY
;
2105 off_t old_pos
= lseek( fd
, 0, SEEK_CUR
);
2107 lseek( fd
, 0, SEEK_SET
);
2109 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1)
2111 if (errno
!= ENOENT
)
2113 status
= STATUS_NOT_SUPPORTED
;
2119 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2120 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2122 while (de
[0].d_reclen
)
2124 if (strcmp( de
[0].d_name
, "." ) && strcmp( de
[0].d_name
, ".." ))
2126 if (de
[1].d_name
[0])
2128 short_name
= de
[0].d_name
;
2129 long_name
= de
[1].d_name
;
2133 long_name
= de
[0].d_name
;
2136 if (!append_entry( data
, long_name
, short_name
, mask
)) goto done
;
2138 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1) break;
2140 status
= STATUS_SUCCESS
;
2142 lseek( fd
, old_pos
, SEEK_SET
);
2145 #endif /* VFAT_IOCTL_READDIR_BOTH */
2148 #ifdef HAVE_GETATTRLIST
2149 /***********************************************************************
2150 * read_directory_getattrlist
2152 * Read a single file from a directory by determining whether the file
2153 * identified by mask exists using getattrlist.
2155 static NTSTATUS
read_directory_data_getattrlist( struct dir_data
*data
, const char *unix_name
)
2157 struct attrlist attrlist
;
2158 #include "pshpack4.h"
2162 struct attrreference name_reference
;
2164 char name
[NAME_MAX
* 3 + 1];
2166 #include "poppack.h"
2168 memset( &attrlist
, 0, sizeof(attrlist
) );
2169 attrlist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
2170 attrlist
.commonattr
= ATTR_CMN_NAME
| ATTR_CMN_OBJTYPE
;
2171 if (getattrlist( unix_name
, &attrlist
, &buffer
, sizeof(buffer
), FSOPT_NOFOLLOW
) == -1)
2172 return STATUS_NO_SUCH_FILE
;
2173 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2174 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2175 if (buffer
.type
== VLNK
)
2178 attrlist
.commonattr
= 0;
2179 if (getattrlist( unix_name
, &attrlist
, &dummy
, sizeof(dummy
), 0 ) == -1)
2180 return STATUS_NO_SUCH_FILE
;
2183 TRACE( "found %s\n", buffer
.name
);
2185 if (!append_entry( data
, buffer
.name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2187 return STATUS_SUCCESS
;
2189 #endif /* HAVE_GETATTRLIST */
2192 /***********************************************************************
2193 * read_directory_stat
2195 * Read a single file from a directory by determining whether the file
2196 * identified by mask exists using stat.
2198 static NTSTATUS
read_directory_data_stat( struct dir_data
*data
, const char *unix_name
)
2202 /* if the file system is not case sensitive we can't find the actual name through stat() */
2203 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE
;
2204 if (stat( unix_name
, &st
) == -1) return STATUS_NO_SUCH_FILE
;
2206 TRACE( "found %s\n", unix_name
);
2208 if (!append_entry( data
, unix_name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2210 return STATUS_SUCCESS
;
2214 /***********************************************************************
2215 * read_directory_readdir
2217 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2219 static NTSTATUS
read_directory_data_readdir( struct dir_data
*data
, const UNICODE_STRING
*mask
)
2222 NTSTATUS status
= STATUS_NO_MEMORY
;
2223 DIR *dir
= opendir( "." );
2225 if (!dir
) return STATUS_NO_SUCH_FILE
;
2227 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2228 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2229 while ((de
= readdir( dir
)))
2231 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
2232 if (!append_entry( data
, de
->d_name
, NULL
, mask
)) goto done
;
2234 status
= STATUS_SUCCESS
;
2242 /***********************************************************************
2243 * read_directory_data
2245 * Read the full contents of a directory, using one of the above helper functions.
2247 static NTSTATUS
read_directory_data( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2251 #ifdef VFAT_IOCTL_READDIR_BOTH
2252 if (!(status
= read_directory_data_vfat( data
, fd
, mask
))) return status
;
2255 if (!has_wildcard( mask
))
2257 /* convert the mask to a Unix name and check for it */
2258 char unix_name
[MAX_DIR_ENTRY_LEN
* 3 + 1];
2259 int ret
= ntdll_wcstoumbs( mask
->Buffer
, mask
->Length
/ sizeof(WCHAR
),
2260 unix_name
, sizeof(unix_name
) - 1, TRUE
);
2264 #ifdef HAVE_GETATTRLIST
2265 if (!(status
= read_directory_data_getattrlist( data
, unix_name
))) return status
;
2267 if (!(status
= read_directory_data_stat( data
, unix_name
))) return status
;
2271 return read_directory_data_readdir( data
, mask
);
2275 /* compare file names for directory sorting */
2276 static int name_compare( const void *a
, const void *b
)
2278 const struct dir_data_names
*file_a
= (const struct dir_data_names
*)a
;
2279 const struct dir_data_names
*file_b
= (const struct dir_data_names
*)b
;
2280 int ret
= wcsicmp( file_a
->long_name
, file_b
->long_name
);
2281 if (!ret
) ret
= wcscmp( file_a
->long_name
, file_b
->long_name
);
2286 /***********************************************************************
2287 * init_cached_dir_data
2289 * Initialize the cached directory contents.
2291 static NTSTATUS
init_cached_dir_data( struct dir_data
**data_ret
, int fd
, const UNICODE_STRING
*mask
)
2293 struct dir_data
*data
;
2298 if (!(data
= calloc( 1, sizeof(*data
) ))) return STATUS_NO_MEMORY
;
2300 if ((status
= read_directory_data( data
, fd
, mask
)))
2302 free_dir_data( data
);
2306 /* sort filenames, but not "." and ".." */
2308 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, "." )) i
++;
2309 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, ".." )) i
++;
2310 if (i
< data
->count
) qsort( data
->names
+ i
, data
->count
- i
, sizeof(*data
->names
), name_compare
);
2315 data
->id
.dev
= st
.st_dev
;
2316 data
->id
.ino
= st
.st_ino
;
2319 TRACE( "mask %s found %u files\n", debugstr_us( mask
), data
->count
);
2320 for (i
= 0; i
< data
->count
; i
++)
2321 TRACE( "%s %s\n", debugstr_w(data
->names
[i
].long_name
), debugstr_w(data
->names
[i
].short_name
) );
2324 return data
->count
? STATUS_SUCCESS
: STATUS_NO_SUCH_FILE
;
2328 /***********************************************************************
2329 * get_cached_dir_data
2331 * Retrieve the cached directory data, or initialize it if necessary.
2333 static NTSTATUS
get_cached_dir_data( HANDLE handle
, struct dir_data
**data_ret
, int fd
,
2334 const UNICODE_STRING
*mask
)
2337 int entry
= -1, free_entries
[16];
2340 SERVER_START_REQ( get_directory_cache_entry
)
2342 req
->handle
= wine_server_obj_handle( handle
);
2343 wine_server_set_reply( req
, free_entries
, sizeof(free_entries
) );
2344 if (!(status
= wine_server_call( req
))) entry
= reply
->entry
;
2346 for (i
= 0; i
< wine_server_reply_size( reply
) / sizeof(*free_entries
); i
++)
2348 int free_idx
= free_entries
[i
];
2349 if (free_idx
< dir_data_cache_size
)
2351 free_dir_data( dir_data_cache
[free_idx
] );
2352 dir_data_cache
[free_idx
] = NULL
;
2360 if (status
== STATUS_SHARING_VIOLATION
) FIXME( "shared directory handle not supported yet\n" );
2364 if (entry
>= dir_data_cache_size
)
2366 unsigned int size
= max( dir_data_cache_initial_size
, max( dir_data_cache_size
* 2, entry
+ 1 ) );
2367 struct dir_data
**new_cache
= realloc( dir_data_cache
, size
* sizeof(*new_cache
) );
2369 if (!new_cache
) return STATUS_NO_MEMORY
;
2370 memset( new_cache
+ dir_data_cache_size
, 0, (size
- dir_data_cache_size
) * sizeof(*new_cache
) );
2371 dir_data_cache
= new_cache
;
2372 dir_data_cache_size
= size
;
2375 if (!dir_data_cache
[entry
]) status
= init_cached_dir_data( &dir_data_cache
[entry
], fd
, mask
);
2377 *data_ret
= dir_data_cache
[entry
];
2382 /******************************************************************************
2383 * NtQueryDirectoryFile (NTDLL.@)
2385 NTSTATUS WINAPI
NtQueryDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc_routine
,
2386 void *apc_context
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
2387 FILE_INFORMATION_CLASS info_class
, BOOLEAN single_entry
,
2388 UNICODE_STRING
*mask
, BOOLEAN restart_scan
)
2390 int cwd
, fd
, needs_close
;
2391 enum server_fd_type type
;
2392 struct dir_data
*data
;
2395 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2396 handle
, event
, apc_routine
, apc_context
, io
, buffer
,
2397 length
, info_class
, single_entry
, debugstr_us(mask
),
2400 if (event
|| apc_routine
)
2402 FIXME( "Unsupported yet option\n" );
2403 return STATUS_NOT_IMPLEMENTED
;
2407 case FileDirectoryInformation
:
2408 case FileBothDirectoryInformation
:
2409 case FileFullDirectoryInformation
:
2410 case FileIdBothDirectoryInformation
:
2411 case FileIdFullDirectoryInformation
:
2412 case FileIdGlobalTxDirectoryInformation
:
2413 case FileNamesInformation
:
2414 if (length
< dir_info_align( dir_info_size( info_class
, 1 ))) return STATUS_INFO_LENGTH_MISMATCH
;
2416 case FileObjectIdInformation
:
2417 if (length
!= sizeof(FILE_OBJECTID_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2418 return STATUS_INVALID_INFO_CLASS
;
2419 case FileQuotaInformation
:
2420 if (length
!= sizeof(FILE_QUOTA_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2421 return STATUS_INVALID_INFO_CLASS
;
2422 case FileReparsePointInformation
:
2423 if (length
!= sizeof(FILE_REPARSE_POINT_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2424 return STATUS_INVALID_INFO_CLASS
;
2426 return STATUS_INVALID_INFO_CLASS
;
2428 if (!buffer
) return STATUS_ACCESS_VIOLATION
;
2430 if ((status
= server_get_unix_fd( handle
, FILE_LIST_DIRECTORY
, &fd
, &needs_close
, &type
, NULL
)))
2433 if (type
!= FD_TYPE_DIR
)
2435 if (needs_close
) close( fd
);
2436 return STATUS_INVALID_PARAMETER
;
2439 io
->Information
= 0;
2441 mutex_lock( &dir_mutex
);
2443 cwd
= open( ".", O_RDONLY
);
2444 if (fchdir( fd
) != -1)
2446 if (!(status
= get_cached_dir_data( handle
, &data
, fd
, mask
)))
2448 union file_directory_info
*last_info
= NULL
;
2450 if (restart_scan
) data
->pos
= 0;
2452 while (!status
&& data
->pos
< data
->count
)
2454 status
= get_dir_data_entry( data
, buffer
, io
, length
, info_class
, &last_info
);
2455 if (!status
|| status
== STATUS_BUFFER_OVERFLOW
) data
->pos
++;
2456 if (single_entry
&& last_info
) break;
2459 if (!last_info
) status
= STATUS_NO_MORE_FILES
;
2460 else if (status
== STATUS_MORE_ENTRIES
) status
= STATUS_SUCCESS
;
2462 io
->u
.Status
= status
;
2464 if (cwd
== -1 || fchdir( cwd
) == -1) chdir( "/" );
2466 else status
= errno_to_status( errno
);
2468 mutex_unlock( &dir_mutex
);
2470 if (needs_close
) close( fd
);
2471 if (cwd
!= -1) close( cwd
);
2472 TRACE( "=> %x (%ld)\n", status
, io
->Information
);
2477 /***********************************************************************
2480 * Find a file in a directory the hard way, by doing a case-insensitive search.
2481 * The file found is appended to unix_name at pos.
2482 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2484 static NTSTATUS
find_file_in_dir( char *unix_name
, int pos
, const WCHAR
*name
, int length
,
2485 BOOLEAN check_case
, BOOLEAN
*is_win_dir
)
2487 WCHAR buffer
[MAX_DIR_ENTRY_LEN
];
2488 BOOLEAN is_name_8_dot_3
;
2494 /* try a shortcut for this directory */
2496 unix_name
[pos
++] = '/';
2497 ret
= ntdll_wcstoumbs( name
, length
, unix_name
+ pos
, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
2498 if (ret
>= 0 && ret
<= MAX_DIR_ENTRY_LEN
)
2500 unix_name
[pos
+ ret
] = 0;
2501 if (!stat( unix_name
, &st
))
2503 if (is_win_dir
) *is_win_dir
= is_same_file( &windir
, &st
);
2504 return STATUS_SUCCESS
;
2507 if (check_case
) goto not_found
; /* we want an exact match */
2509 if (pos
> 1) unix_name
[pos
- 1] = 0;
2510 else unix_name
[1] = 0; /* keep the initial slash */
2512 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2514 is_name_8_dot_3
= is_legal_8dot3_name( name
, length
);
2515 #ifndef VFAT_IOCTL_READDIR_BOTH
2516 is_name_8_dot_3
= is_name_8_dot_3
&& length
>= 8 && name
[4] == '~';
2519 if (!is_name_8_dot_3
&& !get_dir_case_sensitivity( unix_name
)) goto not_found
;
2521 /* now look for it through the directory */
2523 #ifdef VFAT_IOCTL_READDIR_BOTH
2524 if (is_name_8_dot_3
)
2526 int fd
= open( unix_name
, O_RDONLY
| O_DIRECTORY
);
2529 KERNEL_DIRENT kde
[2];
2531 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) != -1)
2533 unix_name
[pos
- 1] = '/';
2534 while (kde
[0].d_reclen
)
2536 if (kde
[1].d_name
[0])
2538 ret
= ntdll_umbstowcs( kde
[1].d_name
, strlen(kde
[1].d_name
),
2539 buffer
, MAX_DIR_ENTRY_LEN
);
2540 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2542 strcpy( unix_name
+ pos
, kde
[1].d_name
);
2547 ret
= ntdll_umbstowcs( kde
[0].d_name
, strlen(kde
[0].d_name
),
2548 buffer
, MAX_DIR_ENTRY_LEN
);
2549 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2551 strcpy( unix_name
+ pos
,
2552 kde
[1].d_name
[0] ? kde
[1].d_name
: kde
[0].d_name
);
2556 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) == -1)
2562 /* if that did not work, restore previous state of unix_name */
2563 unix_name
[pos
- 1] = 0;
2567 /* fall through to normal handling */
2569 #endif /* VFAT_IOCTL_READDIR_BOTH */
2571 if (!(dir
= opendir( unix_name
))) return errno_to_status( errno
);
2573 unix_name
[pos
- 1] = '/';
2574 while ((de
= readdir( dir
)))
2576 ret
= ntdll_umbstowcs( de
->d_name
, strlen(de
->d_name
), buffer
, MAX_DIR_ENTRY_LEN
);
2577 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2579 strcpy( unix_name
+ pos
, de
->d_name
);
2584 if (!is_name_8_dot_3
) continue;
2586 if (!is_legal_8dot3_name( buffer
, ret
))
2588 WCHAR short_nameW
[12];
2589 ret
= hash_short_file_name( buffer
, ret
, short_nameW
);
2590 if (ret
== length
&& !wcsnicmp( short_nameW
, name
, length
))
2592 strcpy( unix_name
+ pos
, de
->d_name
);
2601 unix_name
[pos
- 1] = 0;
2602 return STATUS_OBJECT_PATH_NOT_FOUND
;
2605 if (is_win_dir
&& !stat( unix_name
, &st
)) *is_win_dir
= is_same_file( &windir
, &st
);
2606 return STATUS_SUCCESS
;
2612 static const WCHAR catrootW
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2613 static const WCHAR catroot2W
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2614 static const WCHAR driversstoreW
[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','s','t','o','r','e',0};
2615 static const WCHAR driversetcW
[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2616 static const WCHAR logfilesW
[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2617 static const WCHAR spoolW
[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2618 static const WCHAR system32W
[] = {'s','y','s','t','e','m','3','2',0};
2619 static const WCHAR sysnativeW
[] = {'s','y','s','n','a','t','i','v','e',0};
2620 static const WCHAR regeditW
[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2624 const WCHAR
*source
;
2625 const char *unix_target
;
2629 { catroot2W
, NULL
},
2630 { driversstoreW
, NULL
},
2631 { driversetcW
, NULL
},
2632 { logfilesW
, NULL
},
2634 { system32W
, "syswow64" },
2635 { sysnativeW
, "system32" },
2636 { regeditW
, "syswow64/regedit.exe" }
2639 static unsigned int nb_redirects
;
2642 /***********************************************************************
2645 static void init_redirects(void)
2647 static const char windows_dir
[] = "/dosdevices/c:/windows";
2651 if (!(dir
= malloc( strlen(config_dir
) + sizeof(windows_dir
) ))) return;
2652 strcpy( dir
, config_dir
);
2653 strcat( dir
, windows_dir
);
2654 if (!stat( dir
, &st
))
2656 windir
.dev
= st
.st_dev
;
2657 windir
.ino
= st
.st_ino
;
2658 nb_redirects
= ARRAY_SIZE( redirects
);
2660 else ERR( "%s: %s\n", dir
, strerror(errno
) );
2666 /***********************************************************************
2669 * Check if path matches a redirect name. If yes, return matched length.
2671 static int match_redirect( const WCHAR
*path
, int len
, const WCHAR
*redir
, BOOLEAN check_case
)
2678 while (i
< len
&& !IS_SEPARATOR(path
[i
])) i
++;
2681 if (wcsncmp( path
+ start
, redir
, i
- start
)) return 0;
2685 if (wcsnicmp( path
+ start
, redir
, i
- start
)) return 0;
2688 while (i
< len
&& IS_SEPARATOR(path
[i
])) i
++;
2689 if (!*redir
) return i
;
2690 if (*redir
++ != '\\') return 0;
2696 /***********************************************************************
2699 * Retrieve the Unix path corresponding to a redirected path if any.
2701 static int get_redirect_path( char *unix_name
, int pos
, const WCHAR
*name
, int length
, BOOLEAN check_case
)
2706 for (i
= 0; i
< nb_redirects
; i
++)
2708 if ((len
= match_redirect( name
, length
, redirects
[i
].source
, check_case
)))
2710 if (!redirects
[i
].unix_target
) break;
2711 unix_name
[pos
++] = '/';
2712 strcpy( unix_name
+ pos
, redirects
[i
].unix_target
);
2721 /* there are no redirects on 64-bit */
2722 static int get_redirect_path( char *unix_name
, int pos
, const WCHAR
*name
, int length
, BOOLEAN check_case
)
2729 /***********************************************************************
2732 void init_files(void)
2735 if (is_wow64
) init_redirects();
2737 /* a couple of directories that we don't want to return in directory searches */
2738 ignore_file( config_dir
);
2739 ignore_file( "/dev" );
2740 ignore_file( "/proc" );
2742 ignore_file( "/sys" );
2744 /* retrieve initial umask */
2745 start_umask
= umask( 0777 );
2746 umask( start_umask
);
2750 /******************************************************************************
2753 * Get the Unix path of a DOS device.
2755 static NTSTATUS
get_dos_device( const WCHAR
*name
, UINT name_len
, char **unix_name_ret
)
2758 char *unix_name
, *new_name
, *dev
;
2762 /* make sure the device name is ASCII */
2763 for (i
= 0; i
< name_len
; i
++)
2764 if (name
[i
] <= 32 || name
[i
] >= 127) return STATUS_BAD_DEVICE_TYPE
;
2766 unix_len
= strlen(config_dir
) + sizeof("/dosdevices/") + name_len
+ 1;
2768 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
2770 strcpy( unix_name
, config_dir
);
2771 strcat( unix_name
, "/dosdevices/" );
2772 dev
= unix_name
+ strlen(unix_name
);
2774 for (i
= 0; i
< name_len
; i
++) dev
[i
] = (name
[i
] >= 'A' && name
[i
] <= 'Z' ? name
[i
] + 32 : name
[i
]);
2777 /* special case for drive devices */
2778 if (name_len
== 2 && dev
[1] == ':')
2786 if (!stat( unix_name
, &st
))
2788 TRACE( "%s -> %s\n", debugstr_wn(name
,name_len
), debugstr_a(unix_name
) );
2789 *unix_name_ret
= unix_name
;
2790 return STATUS_SUCCESS
;
2794 /* now try some defaults for it */
2795 if (!strcmp( dev
, "aux" ))
2797 strcpy( dev
, "com1" );
2800 if (!strcmp( dev
, "prn" ))
2802 strcpy( dev
, "lpt1" );
2807 if (dev
[1] == ':' && dev
[2] == ':') /* drive device */
2809 dev
[2] = 0; /* remove last ':' to get the drive mount point symlink */
2810 new_name
= get_default_drive_device( unix_name
);
2813 if (!new_name
) break;
2815 unix_name
= new_name
;
2816 dev
= NULL
; /* last try */
2819 return STATUS_BAD_DEVICE_TYPE
;
2823 /* return the length of the DOS namespace prefix if any */
2824 static inline int get_dos_prefix_len( const UNICODE_STRING
*name
)
2826 static const WCHAR nt_prefixW
[] = {'\\','?','?','\\'};
2827 static const WCHAR dosdev_prefixW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2829 if (name
->Length
>= sizeof(nt_prefixW
) &&
2830 !memcmp( name
->Buffer
, nt_prefixW
, sizeof(nt_prefixW
) ))
2831 return ARRAY_SIZE( nt_prefixW
);
2833 if (name
->Length
>= sizeof(dosdev_prefixW
) &&
2834 !wcsnicmp( name
->Buffer
, dosdev_prefixW
, ARRAY_SIZE( dosdev_prefixW
)))
2835 return ARRAY_SIZE( dosdev_prefixW
);
2841 /***********************************************************************
2842 * remove_last_componentA
2844 * Remove the last component of the path. Helper for find_drive_rootA.
2846 static inline unsigned int remove_last_componentA( const char *path
, unsigned int len
)
2852 /* find start of the last path component */
2853 unsigned int prev
= len
;
2854 if (prev
<= 1) break; /* reached root */
2855 while (prev
> 1 && path
[prev
- 1] != '/') prev
--;
2856 /* does removing it take us up a level? */
2857 if (len
- prev
!= 1 || path
[prev
] != '.') /* not '.' */
2859 if (len
- prev
== 2 && path
[prev
] == '.' && path
[prev
+1] == '.') /* is it '..'? */
2864 /* strip off trailing slashes */
2865 while (prev
> 1 && path
[prev
- 1] == '/') prev
--;
2872 /***********************************************************************
2875 * Find a drive for which the root matches the beginning of the given path.
2876 * This can be used to translate a Unix path into a drive + DOS path.
2877 * Return value is the drive, or -1 on error. On success, ppath is modified
2878 * to point to the beginning of the DOS path.
2880 static NTSTATUS
find_drive_rootA( LPCSTR
*ppath
, unsigned int len
, int *drive_ret
)
2882 /* Starting with the full path, check if the device and inode match any of
2883 * the wine 'drives'. If not then remove the last path component and try
2884 * again. If the last component was a '..' then skip a normal component
2885 * since it's a directory that's ascended back out of.
2889 const char *path
= *ppath
;
2891 struct file_identity info
[MAX_DOS_DRIVES
];
2893 /* get device and inode of all drives */
2894 if (!get_drives_info( info
)) return STATUS_OBJECT_PATH_NOT_FOUND
;
2896 /* strip off trailing slashes */
2897 while (len
> 1 && path
[len
- 1] == '/') len
--;
2899 /* make a copy of the path */
2900 if (!(buffer
= malloc( len
+ 1 ))) return STATUS_NO_MEMORY
;
2901 memcpy( buffer
, path
, len
);
2906 if (!stat( buffer
, &st
) && S_ISDIR( st
.st_mode
))
2908 /* Find the drive */
2909 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
2911 if ((info
[drive
].dev
== st
.st_dev
) && (info
[drive
].ino
== st
.st_ino
))
2913 if (len
== 1) len
= 0; /* preserve root slash in returned path */
2914 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2915 debugstr_a(path
), 'A' + drive
, debugstr_a(buffer
), debugstr_a(path
+ len
));
2919 return STATUS_SUCCESS
;
2923 if (len
<= 1) break; /* reached root */
2924 len
= remove_last_componentA( buffer
, len
);
2928 return STATUS_OBJECT_PATH_NOT_FOUND
;
2932 /******************************************************************************
2935 static void rebuild_nt_name( const UNICODE_STRING
*nameW
, DWORD prefix_len
,
2936 const char *unix_name
, UNICODE_STRING
*nt_name
)
2941 while (*unix_name
== '/') unix_name
++;
2942 nt_name
->MaximumLength
= (prefix_len
+ strlen(unix_name
) + 2) * sizeof(WCHAR
);
2943 if (!(buf
= malloc( nt_name
->MaximumLength
))) return;
2944 nt_name
->Buffer
= buf
;
2945 memcpy( buf
, nameW
->Buffer
, prefix_len
* sizeof(WCHAR
) );
2946 if (prefix_len
&& buf
[prefix_len
- 1] != '\\') buf
[prefix_len
++] = '\\';
2948 len
= ntdll_umbstowcs( unix_name
, strlen(unix_name
), buf
, strlen(unix_name
) );
2949 for (; len
; len
--, buf
++) if (*buf
== '/') *buf
= '\\';
2951 nt_name
->Length
= (buf
- nt_name
->Buffer
) * sizeof(WCHAR
);
2955 /******************************************************************************
2958 * Recursively search directories from the dir queue for a given inode.
2960 static NTSTATUS
find_file_id( char **unix_name
, ULONG
*len
, ULONGLONG file_id
, dev_t dev
)
2967 char *name
= *unix_name
;
2969 while (!(status
= next_dir_in_queue( name
)))
2971 if (!(dir
= opendir( name
))) continue;
2972 TRACE( "searching %s for %s\n", debugstr_a(name
), wine_dbgstr_longlong(file_id
) );
2973 pos
= strlen( name
);
2974 if (pos
+ MAX_DIR_ENTRY_LEN
>= *len
/ sizeof(WCHAR
))
2976 if (!(name
= realloc( name
, *len
* 2 )))
2979 return STATUS_NO_MEMORY
;
2985 while ((de
= readdir( dir
)))
2987 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
2988 strcpy( name
+ pos
, de
->d_name
);
2989 if (lstat( name
, &st
) == -1) continue;
2990 if (st
.st_dev
!= dev
) continue;
2991 if (st
.st_ino
== file_id
)
2994 return STATUS_SUCCESS
;
2996 if (!S_ISDIR( st
.st_mode
)) continue;
2997 if ((status
= add_dir_to_queue( name
)) != STATUS_SUCCESS
)
3009 /******************************************************************************
3010 * file_id_to_unix_file_name
3012 * Lookup a file from its file id instead of its name.
3014 static NTSTATUS
file_id_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char **unix_name_ret
,
3015 UNICODE_STRING
*nt_name
)
3017 enum server_fd_type type
;
3018 int old_cwd
, root_fd
, needs_close
;
3023 struct stat st
, root_st
;
3025 if (attr
->ObjectName
->Length
!= sizeof(ULONGLONG
)) return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3026 if (!attr
->RootDirectory
) return STATUS_INVALID_PARAMETER
;
3027 memcpy( &file_id
, attr
->ObjectName
->Buffer
, sizeof(file_id
) );
3029 len
= 2 * MAX_DIR_ENTRY_LEN
+ 4;
3030 if (!(unix_name
= malloc( len
))) return STATUS_NO_MEMORY
;
3031 strcpy( unix_name
, "." );
3033 if ((status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3036 if (type
!= FD_TYPE_DIR
)
3038 status
= STATUS_OBJECT_TYPE_MISMATCH
;
3042 fstat( root_fd
, &root_st
);
3043 if (root_st
.st_ino
== file_id
) /* shortcut for "." */
3045 status
= STATUS_SUCCESS
;
3049 mutex_lock( &dir_mutex
);
3050 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3052 /* shortcut for ".." */
3053 if (!stat( "..", &st
) && st
.st_dev
== root_st
.st_dev
&& st
.st_ino
== file_id
)
3055 strcpy( unix_name
, ".." );
3056 status
= STATUS_SUCCESS
;
3060 status
= add_dir_to_queue( "." );
3062 status
= find_file_id( &unix_name
, &len
, file_id
, root_st
.st_dev
);
3063 if (!status
) /* get rid of "./" prefix */
3064 memmove( unix_name
, unix_name
+ 2, strlen(unix_name
) - 1 );
3067 if (fchdir( old_cwd
) == -1) chdir( "/" );
3069 else status
= errno_to_status( errno
);
3070 mutex_unlock( &dir_mutex
);
3071 if (old_cwd
!= -1) close( old_cwd
);
3074 if (status
== STATUS_SUCCESS
)
3076 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id
), debugstr_a(unix_name
) );
3077 *unix_name_ret
= unix_name
;
3078 if (nt_name
) rebuild_nt_name( attr
->ObjectName
, 0, unix_name
, nt_name
);
3082 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id
), attr
->RootDirectory
);
3085 if (needs_close
) close( root_fd
);
3090 /******************************************************************************
3093 * Helper for nt_to_unix_file_name
3095 static NTSTATUS
lookup_unix_name( const WCHAR
*name
, int name_len
, char **buffer
, int unix_len
, int pos
,
3096 UINT disposition
, BOOLEAN check_case
)
3101 char *unix_name
= *buffer
;
3103 const BOOL redirect
= FALSE
;
3105 const BOOL redirect
= NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
];
3108 /* try a shortcut first */
3110 while (name_len
&& IS_SEPARATOR(*name
))
3116 unix_name
[pos
] = '/';
3117 ret
= ntdll_wcstoumbs( name
, name_len
, unix_name
+ pos
+ 1, unix_len
- pos
- 1, TRUE
);
3118 if (ret
>= 0 && ret
< unix_len
- pos
- 1)
3121 unix_name
[pos
+ 1 + ret
] = 0;
3122 for (p
= unix_name
+ pos
; *p
; p
++) if (*p
== '\\') *p
= '/';
3123 if (!name_len
|| !redirect
|| (!strstr( unix_name
, "/windows/") && strncmp( unix_name
, "windows/", 8 )))
3125 if (!stat( unix_name
, &st
))
3127 if (disposition
== FILE_CREATE
)
3128 return STATUS_OBJECT_NAME_COLLISION
;
3129 return STATUS_SUCCESS
;
3134 if (!name_len
) /* empty name -> drive root doesn't exist */
3135 return STATUS_OBJECT_PATH_NOT_FOUND
;
3136 if (check_case
&& !redirect
&& (disposition
== FILE_OPEN
|| disposition
== FILE_OVERWRITE
))
3137 return STATUS_OBJECT_NAME_NOT_FOUND
;
3139 /* now do it component by component */
3143 const WCHAR
*end
, *next
;
3144 BOOLEAN is_win_dir
= FALSE
;
3147 while (end
< name
+ name_len
&& !IS_SEPARATOR(*end
)) end
++;
3149 while (next
< name
+ name_len
&& IS_SEPARATOR(*next
)) next
++;
3150 name_len
-= next
- name
;
3152 /* grow the buffer if needed */
3154 if (unix_len
- pos
< MAX_DIR_ENTRY_LEN
+ 2)
3157 unix_len
+= 2 * MAX_DIR_ENTRY_LEN
;
3158 if (!(new_name
= realloc( unix_name
, unix_len
))) return STATUS_NO_MEMORY
;
3159 unix_name
= *buffer
= new_name
;
3162 status
= find_file_in_dir( unix_name
, pos
, name
, end
- name
,
3163 check_case
, redirect
? &is_win_dir
: NULL
);
3165 /* if this is the last element, not finding it is not necessarily fatal */
3168 if (status
== STATUS_OBJECT_PATH_NOT_FOUND
)
3170 status
= STATUS_OBJECT_NAME_NOT_FOUND
;
3171 if (disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
3173 ret
= ntdll_wcstoumbs( name
, end
- name
, unix_name
+ pos
+ 1, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
3174 if (ret
> 0 && ret
<= MAX_DIR_ENTRY_LEN
)
3176 unix_name
[pos
] = '/';
3177 unix_name
[pos
+ 1 + ret
] = 0;
3178 status
= STATUS_NO_SUCH_FILE
;
3183 else if (status
== STATUS_SUCCESS
&& disposition
== FILE_CREATE
)
3185 status
= STATUS_OBJECT_NAME_COLLISION
;
3189 if (status
!= STATUS_SUCCESS
) break;
3191 pos
+= strlen( unix_name
+ pos
);
3194 if (is_win_dir
&& (len
= get_redirect_path( unix_name
, pos
, name
, name_len
, check_case
)))
3198 pos
+= strlen( unix_name
+ pos
);
3199 TRACE( "redirecting -> %s + %s\n", debugstr_a(unix_name
), debugstr_w(name
) );
3207 /******************************************************************************
3208 * nt_to_unix_file_name_attr
3210 static NTSTATUS
nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES
*attr
, char **name_ret
,
3211 UNICODE_STRING
*nt_name
, UINT disposition
)
3213 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, 0 };
3214 enum server_fd_type type
;
3215 int old_cwd
, root_fd
, needs_close
;
3216 const WCHAR
*name
, *p
;
3218 int name_len
, unix_len
;
3221 if (!attr
->RootDirectory
) /* without root dir fall back to normal lookup */
3222 return nt_to_unix_file_name( attr
->ObjectName
, name_ret
, nt_name
, disposition
);
3224 name
= attr
->ObjectName
->Buffer
;
3225 name_len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
3227 if (name_len
&& IS_SEPARATOR(name
[0])) return STATUS_INVALID_PARAMETER
;
3229 /* check for invalid characters */
3230 for (p
= name
; p
< name
+ name_len
; p
++)
3231 if (*p
< 32 || wcschr( invalid_charsW
, *p
)) return STATUS_OBJECT_NAME_INVALID
;
3233 unix_len
= name_len
* 3 + MAX_DIR_ENTRY_LEN
+ 3;
3234 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3237 if (!(status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3239 if (type
!= FD_TYPE_DIR
)
3241 if (needs_close
) close( root_fd
);
3242 status
= STATUS_BAD_DEVICE_TYPE
;
3246 mutex_lock( &dir_mutex
);
3247 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3249 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, 1,
3250 disposition
, FALSE
);
3251 if (fchdir( old_cwd
) == -1) chdir( "/" );
3253 else status
= errno_to_status( errno
);
3254 mutex_unlock( &dir_mutex
);
3255 if (old_cwd
!= -1) close( old_cwd
);
3256 if (needs_close
) close( root_fd
);
3259 else if (status
== STATUS_OBJECT_TYPE_MISMATCH
) status
= STATUS_BAD_DEVICE_TYPE
;
3261 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3263 TRACE( "%s -> %s\n", debugstr_us(attr
->ObjectName
), debugstr_a(unix_name
) );
3264 *name_ret
= unix_name
;
3265 if (nt_name
) rebuild_nt_name( attr
->ObjectName
, 0, unix_name
, nt_name
);
3269 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
3276 /******************************************************************************
3277 * nt_to_unix_file_name
3279 * Convert a file name from NT namespace to Unix namespace.
3281 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3282 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3283 * returned, but the unix name is still filled in properly.
3285 NTSTATUS
nt_to_unix_file_name( const UNICODE_STRING
*nameW
, char **unix_name_ret
,
3286 UNICODE_STRING
*nt_name
, UINT disposition
)
3288 static const WCHAR unixW
[] = {'u','n','i','x'};
3289 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, 0 };
3291 NTSTATUS status
= STATUS_SUCCESS
;
3292 const WCHAR
*name
, *p
;
3295 int pos
, ret
, name_len
, unix_len
, prefix_len
;
3296 WCHAR prefix
[MAX_DIR_ENTRY_LEN
+ 1];
3297 BOOLEAN check_case
= FALSE
;
3298 BOOLEAN is_unix
= FALSE
;
3300 name
= nameW
->Buffer
;
3301 name_len
= nameW
->Length
/ sizeof(WCHAR
);
3303 if (!name_len
|| !IS_SEPARATOR(name
[0])) return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3305 if (!(pos
= get_dos_prefix_len( nameW
)))
3306 return STATUS_BAD_DEVICE_TYPE
; /* no DOS prefix, assume NT native name */
3311 if (!name_len
) return STATUS_OBJECT_NAME_INVALID
;
3313 /* check for sub-directory */
3314 for (pos
= 0; pos
< name_len
&& pos
<= MAX_DIR_ENTRY_LEN
; pos
++)
3316 if (IS_SEPARATOR(name
[pos
])) break;
3317 if (name
[pos
] < 32 || wcschr( invalid_charsW
, name
[pos
] ))
3318 return STATUS_OBJECT_NAME_INVALID
;
3319 prefix
[pos
] = (name
[pos
] >= 'A' && name
[pos
] <= 'Z') ? name
[pos
] + 'a' - 'A' : name
[pos
];
3321 if (pos
> MAX_DIR_ENTRY_LEN
) return STATUS_OBJECT_NAME_INVALID
;
3323 if (pos
== name_len
) /* no subdir, plain DOS device */
3324 return get_dos_device( name
, name_len
, unix_name_ret
);
3327 prefix
[prefix_len
] = 0;
3330 name_len
-= prefix_len
;
3332 /* check for invalid characters (all chars except 0 are valid for unix) */
3333 is_unix
= (prefix_len
== 4 && !memcmp( prefix
, unixW
, sizeof(unixW
) ));
3336 for (p
= name
; p
< name
+ name_len
; p
++)
3337 if (!*p
) return STATUS_OBJECT_NAME_INVALID
;
3342 for (p
= name
; p
< name
+ name_len
; p
++)
3343 if (*p
< 32 || wcschr( invalid_charsW
, *p
)) return STATUS_OBJECT_NAME_INVALID
;
3346 unix_len
= (prefix_len
+ name_len
) * 3 + MAX_DIR_ENTRY_LEN
+ 3;
3347 unix_len
+= strlen(config_dir
) + sizeof("/dosdevices/");
3348 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3349 strcpy( unix_name
, config_dir
);
3350 strcat( unix_name
, "/dosdevices/" );
3351 pos
= strlen(unix_name
);
3353 ret
= ntdll_wcstoumbs( prefix
, prefix_len
, unix_name
+ pos
, unix_len
- pos
- 1, TRUE
);
3357 return STATUS_OBJECT_NAME_INVALID
;
3361 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3363 if (prefix_len
!= 2 || prefix
[1] != ':')
3366 if (lstat( unix_name
, &st
) == -1 && errno
== ENOENT
)
3371 return STATUS_BAD_DEVICE_TYPE
;
3373 pos
= 0; /* fall back to unix root */
3377 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, pos
, disposition
, check_case
);
3378 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3380 TRACE( "%s -> %s\n", debugstr_us(nameW
), debugstr_a(unix_name
) );
3381 *unix_name_ret
= unix_name
;
3382 if (nt_name
) rebuild_nt_name( nameW
, name
- nameW
->Buffer
, unix_name
+ pos
, nt_name
);
3386 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
3393 /******************************************************************************
3394 * wine_nt_to_unix_file_name
3396 * Convert a file name from NT namespace to Unix namespace.
3398 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3399 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3400 * returned, but the unix name is still filled in properly.
3402 NTSTATUS CDECL
wine_nt_to_unix_file_name( const UNICODE_STRING
*nameW
, char *nameA
, SIZE_T
*size
,
3405 char *buffer
= NULL
;
3406 NTSTATUS status
= nt_to_unix_file_name( nameW
, &buffer
, NULL
, disposition
);
3410 if (*size
> strlen(buffer
)) strcpy( nameA
, buffer
);
3411 else status
= STATUS_BUFFER_TOO_SMALL
;
3412 *size
= strlen(buffer
) + 1;
3419 /******************************************************************
3420 * unix_to_nt_file_name
3422 NTSTATUS
unix_to_nt_file_name( const char *name
, WCHAR
**nt
)
3424 static const WCHAR unix_prefixW
[] = {'\\','?','?','\\','u','n','i','x',0};
3425 WCHAR dos_prefixW
[] = {'\\','?','?','\\','A',':','\\',0};
3426 const WCHAR
*prefix
= unix_prefixW
;
3427 unsigned int lenW
, lenA
= strlen(name
);
3428 const char *path
= name
;
3433 status
= find_drive_rootA( &path
, lenA
, &drive
);
3434 lenA
-= path
- name
;
3436 if (status
== STATUS_SUCCESS
)
3438 while (lenA
&& path
[0] == '/') { lenA
--; path
++; }
3439 dos_prefixW
[4] += drive
;
3440 prefix
= dos_prefixW
;
3442 else if (status
!= STATUS_OBJECT_PATH_NOT_FOUND
) return status
;
3444 lenW
= wcslen( prefix
);
3445 if (!(buffer
= malloc( (lenA
+ lenW
+ 1) * sizeof(WCHAR
) ))) return STATUS_NO_MEMORY
;
3446 memcpy( buffer
, prefix
, lenW
* sizeof(WCHAR
) );
3447 lenW
+= ntdll_umbstowcs( path
, lenA
, buffer
+ lenW
, lenA
);
3449 for (p
= buffer
; *p
; p
++) if (*p
== '/') *p
= '\\';
3451 return STATUS_SUCCESS
;
3455 /******************************************************************
3456 * wine_unix_to_nt_file_name
3458 NTSTATUS CDECL
wine_unix_to_nt_file_name( const char *name
, WCHAR
*buffer
, SIZE_T
*size
)
3460 WCHAR
*nt_name
= NULL
;
3463 if (name
[0] != '/') return STATUS_INVALID_PARAMETER
; /* relative paths are not supported */
3465 status
= unix_to_nt_file_name( name
, &nt_name
);
3468 if (*size
> wcslen(nt_name
)) wcscpy( buffer
, nt_name
);
3469 else status
= STATUS_BUFFER_TOO_SMALL
;
3470 *size
= wcslen(nt_name
) + 1;
3477 /***********************************************************************
3480 * Unmount the specified device.
3482 static NTSTATUS
unmount_device( HANDLE handle
)
3485 int unix_fd
, needs_close
;
3487 if (!(status
= server_get_unix_fd( handle
, 0, &unix_fd
, &needs_close
, NULL
, NULL
)))
3490 char *mount_point
= NULL
;
3492 if (fstat( unix_fd
, &st
) == -1 || !is_valid_mounted_device( &st
))
3493 status
= STATUS_INVALID_PARAMETER
;
3496 if ((mount_point
= get_device_mount_point( st
.st_rdev
)))
3499 static const char umount
[] = "diskutil unmount >/dev/null 2>&1 ";
3501 static const char umount
[] = "umount >/dev/null 2>&1 ";
3503 char *cmd
= malloc( strlen(mount_point
)+sizeof(umount
));
3506 strcpy( cmd
, umount
);
3507 strcat( cmd
, mount_point
);
3511 /* umount will fail to release the loop device since we still have
3512 a handle to it, so we release it here */
3513 if (major(st
.st_rdev
) == LOOP_MAJOR
) ioctl( unix_fd
, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3516 free( mount_point
);
3519 if (needs_close
) close( unix_fd
);
3525 /***********************************************************************
3526 * set_show_dot_files
3528 void CDECL
set_show_dot_files( BOOL enable
)
3530 show_dot_files
= enable
;
3534 /******************************************************************************
3537 * Helper for NtCreateFile that takes a Unix path.
3539 NTSTATUS
open_unix_file( HANDLE
*handle
, const char *unix_name
, ACCESS_MASK access
,
3540 OBJECT_ATTRIBUTES
*attr
, ULONG attributes
, ULONG sharing
, ULONG disposition
,
3541 ULONG options
, void *ea_buffer
, ULONG ea_length
)
3543 struct object_attributes
*objattr
;
3547 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
3549 SERVER_START_REQ( create_file
)
3551 req
->access
= access
;
3552 req
->sharing
= sharing
;
3553 req
->create
= disposition
;
3554 req
->options
= options
;
3555 req
->attrs
= attributes
;
3556 wine_server_add_data( req
, objattr
, len
);
3557 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
3558 status
= wine_server_call( req
);
3559 *handle
= wine_server_ptr_handle( reply
->handle
);
3567 /******************************************************************************
3568 * NtCreateFile (NTDLL.@)
3570 NTSTATUS WINAPI
NtCreateFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
3571 IO_STATUS_BLOCK
*io
, LARGE_INTEGER
*alloc_size
,
3572 ULONG attributes
, ULONG sharing
, ULONG disposition
,
3573 ULONG options
, void *ea_buffer
, ULONG ea_length
)
3575 UNICODE_STRING nt_name
= { 0 };
3577 BOOL created
= FALSE
;
3579 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
3580 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
3581 handle
, access
, debugstr_us(attr
->ObjectName
), attr
->Attributes
,
3582 attr
->RootDirectory
, attr
->SecurityDescriptor
, io
, alloc_size
,
3583 attributes
, sharing
, disposition
, options
, ea_buffer
, ea_length
);
3585 if (!attr
|| !attr
->ObjectName
) return STATUS_INVALID_PARAMETER
;
3587 if (alloc_size
) FIXME( "alloc_size not supported\n" );
3589 if (options
& FILE_OPEN_BY_FILE_ID
)
3590 io
->u
.Status
= file_id_to_unix_file_name( attr
, &unix_name
, &nt_name
);
3592 io
->u
.Status
= nt_to_unix_file_name_attr( attr
, &unix_name
, &nt_name
, disposition
);
3594 if (io
->u
.Status
== STATUS_BAD_DEVICE_TYPE
)
3596 SERVER_START_REQ( open_file_object
)
3598 req
->access
= access
;
3599 req
->attributes
= attr
->Attributes
;
3600 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
3601 req
->sharing
= sharing
;
3602 req
->options
= options
;
3603 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
3604 io
->u
.Status
= wine_server_call( req
);
3605 *handle
= wine_server_ptr_handle( reply
->handle
);
3608 if (io
->u
.Status
== STATUS_SUCCESS
) io
->Information
= FILE_OPENED
;
3609 return io
->u
.Status
;
3612 if (io
->u
.Status
== STATUS_NO_SUCH_FILE
&& disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
3615 io
->u
.Status
= STATUS_SUCCESS
;
3618 if (io
->u
.Status
== STATUS_SUCCESS
)
3620 OBJECT_ATTRIBUTES nt_attr
= *attr
;
3622 if (nt_name
.Buffer
) nt_attr
.ObjectName
= &nt_name
;
3623 io
->u
.Status
= open_unix_file( handle
, unix_name
, access
, &nt_attr
, attributes
,
3624 sharing
, disposition
, options
, ea_buffer
, ea_length
);
3625 free( nt_name
.Buffer
);
3628 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), io
->u
.Status
);
3630 if (io
->u
.Status
== STATUS_SUCCESS
)
3632 if (created
) io
->Information
= FILE_CREATED
;
3633 else switch(disposition
)
3635 case FILE_SUPERSEDE
:
3636 io
->Information
= FILE_SUPERSEDED
;
3639 io
->Information
= FILE_CREATED
;
3643 io
->Information
= FILE_OPENED
;
3645 case FILE_OVERWRITE
:
3646 case FILE_OVERWRITE_IF
:
3647 io
->Information
= FILE_OVERWRITTEN
;
3651 else if (io
->u
.Status
== STATUS_TOO_MANY_OPENED_FILES
)
3654 if (!once
++) ERR_(winediag
)( "Too many open files, ulimit -n probably needs to be increased\n" );
3657 return io
->u
.Status
;
3661 /******************************************************************************
3662 * NtOpenFile (NTDLL.@)
3664 NTSTATUS WINAPI
NtOpenFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
3665 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG options
)
3667 return NtCreateFile( handle
, access
, attr
, io
, NULL
, 0, sharing
, FILE_OPEN
, options
, NULL
, 0 );
3671 /******************************************************************************
3672 * NtCreateMailslotFile (NTDLL.@)
3674 NTSTATUS WINAPI
NtCreateMailslotFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
3675 IO_STATUS_BLOCK
*io
, ULONG options
, ULONG quota
, ULONG msg_size
,
3676 LARGE_INTEGER
*timeout
)
3680 struct object_attributes
*objattr
;
3682 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
3683 handle
, access
, attr
, io
, options
, quota
, msg_size
, timeout
);
3685 if (!handle
) return STATUS_ACCESS_VIOLATION
;
3686 if (!attr
) return STATUS_INVALID_PARAMETER
;
3688 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
3690 SERVER_START_REQ( create_mailslot
)
3692 req
->access
= access
;
3693 req
->max_msgsize
= msg_size
;
3694 req
->read_timeout
= timeout
? timeout
->QuadPart
: -1;
3695 wine_server_add_data( req
, objattr
, len
);
3696 if (!(status
= wine_server_call( req
))) *handle
= wine_server_ptr_handle( reply
->handle
);
3705 /******************************************************************
3706 * NtCreateNamedPipeFile (NTDLL.@)
3708 NTSTATUS WINAPI
NtCreateNamedPipeFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
3709 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG dispo
, ULONG options
,
3710 ULONG pipe_type
, ULONG read_mode
, ULONG completion_mode
,
3711 ULONG max_inst
, ULONG inbound_quota
, ULONG outbound_quota
,
3712 LARGE_INTEGER
*timeout
)
3716 struct object_attributes
*objattr
;
3718 if (!attr
) return STATUS_INVALID_PARAMETER
;
3720 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
3721 handle
, access
, debugstr_us(attr
->ObjectName
), io
, sharing
, dispo
,
3722 options
, pipe_type
, read_mode
, completion_mode
, max_inst
, inbound_quota
,
3723 outbound_quota
, timeout
);
3725 /* assume we only get relative timeout */
3726 if (timeout
->QuadPart
> 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout
->QuadPart
) );
3728 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
3730 SERVER_START_REQ( create_named_pipe
)
3732 req
->access
= access
;
3733 req
->options
= options
;
3734 req
->sharing
= sharing
;
3736 (pipe_type
? NAMED_PIPE_MESSAGE_STREAM_WRITE
: 0) |
3737 (read_mode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0) |
3738 (completion_mode
? NAMED_PIPE_NONBLOCKING_MODE
: 0);
3739 req
->maxinstances
= max_inst
;
3740 req
->outsize
= outbound_quota
;
3741 req
->insize
= inbound_quota
;
3742 req
->timeout
= timeout
->QuadPart
;
3743 wine_server_add_data( req
, objattr
, len
);
3744 if (!(status
= wine_server_call( req
))) *handle
= wine_server_ptr_handle( reply
->handle
);
3753 /******************************************************************
3754 * NtDeleteFile (NTDLL.@)
3756 NTSTATUS WINAPI
NtDeleteFile( OBJECT_ATTRIBUTES
*attr
)
3762 status
= NtCreateFile( &handle
, GENERIC_READ
| GENERIC_WRITE
| DELETE
, attr
, &io
, NULL
, 0,
3763 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, FILE_OPEN
,
3764 FILE_DELETE_ON_CLOSE
, NULL
, 0 );
3765 if (status
== STATUS_SUCCESS
) NtClose( handle
);
3770 /******************************************************************************
3771 * NtQueryFullAttributesFile (NTDLL.@)
3773 NTSTATUS WINAPI
NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES
*attr
,
3774 FILE_NETWORK_OPEN_INFORMATION
*info
)
3779 if (!(status
= nt_to_unix_file_name_attr( attr
, &unix_name
, NULL
, FILE_OPEN
)))
3784 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
3785 status
= errno_to_status( errno
);
3786 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
3787 status
= STATUS_INVALID_INFO_CLASS
;
3790 FILE_BASIC_INFORMATION basic
;
3791 FILE_STANDARD_INFORMATION std
;
3793 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
3794 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
3796 info
->CreationTime
= basic
.CreationTime
;
3797 info
->LastAccessTime
= basic
.LastAccessTime
;
3798 info
->LastWriteTime
= basic
.LastWriteTime
;
3799 info
->ChangeTime
= basic
.ChangeTime
;
3800 info
->AllocationSize
= std
.AllocationSize
;
3801 info
->EndOfFile
= std
.EndOfFile
;
3802 info
->FileAttributes
= basic
.FileAttributes
;
3803 if (is_hidden_file( attr
->ObjectName
)) info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
3807 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
3812 /******************************************************************************
3813 * NtQueryAttributesFile (NTDLL.@)
3815 NTSTATUS WINAPI
NtQueryAttributesFile( const OBJECT_ATTRIBUTES
*attr
, FILE_BASIC_INFORMATION
*info
)
3820 if (!(status
= nt_to_unix_file_name_attr( attr
, &unix_name
, NULL
, FILE_OPEN
)))
3825 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
3826 status
= errno_to_status( errno
);
3827 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
3828 status
= STATUS_INVALID_INFO_CLASS
;
3831 status
= fill_file_info( &st
, attributes
, info
, FileBasicInformation
);
3832 if (is_hidden_file( attr
->ObjectName
)) info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
3836 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
3841 /******************************************************************************
3842 * NtQueryInformationFile (NTDLL.@)
3844 NTSTATUS WINAPI
NtQueryInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
3845 void *ptr
, LONG len
, FILE_INFORMATION_CLASS
class )
3847 static const size_t info_sizes
[] =
3850 sizeof(FILE_DIRECTORY_INFORMATION
), /* FileDirectoryInformation */
3851 sizeof(FILE_FULL_DIRECTORY_INFORMATION
), /* FileFullDirectoryInformation */
3852 sizeof(FILE_BOTH_DIRECTORY_INFORMATION
), /* FileBothDirectoryInformation */
3853 sizeof(FILE_BASIC_INFORMATION
), /* FileBasicInformation */
3854 sizeof(FILE_STANDARD_INFORMATION
), /* FileStandardInformation */
3855 sizeof(FILE_INTERNAL_INFORMATION
), /* FileInternalInformation */
3856 sizeof(FILE_EA_INFORMATION
), /* FileEaInformation */
3857 0, /* FileAccessInformation */
3858 sizeof(FILE_NAME_INFORMATION
), /* FileNameInformation */
3859 sizeof(FILE_RENAME_INFORMATION
)-sizeof(WCHAR
), /* FileRenameInformation */
3860 0, /* FileLinkInformation */
3861 sizeof(FILE_NAMES_INFORMATION
)-sizeof(WCHAR
), /* FileNamesInformation */
3862 sizeof(FILE_DISPOSITION_INFORMATION
), /* FileDispositionInformation */
3863 sizeof(FILE_POSITION_INFORMATION
), /* FilePositionInformation */
3864 sizeof(FILE_FULL_EA_INFORMATION
), /* FileFullEaInformation */
3865 0, /* FileModeInformation */
3866 sizeof(FILE_ALIGNMENT_INFORMATION
), /* FileAlignmentInformation */
3867 sizeof(FILE_ALL_INFORMATION
), /* FileAllInformation */
3868 sizeof(FILE_ALLOCATION_INFORMATION
), /* FileAllocationInformation */
3869 sizeof(FILE_END_OF_FILE_INFORMATION
), /* FileEndOfFileInformation */
3870 0, /* FileAlternateNameInformation */
3871 sizeof(FILE_STREAM_INFORMATION
)-sizeof(WCHAR
), /* FileStreamInformation */
3872 sizeof(FILE_PIPE_INFORMATION
), /* FilePipeInformation */
3873 sizeof(FILE_PIPE_LOCAL_INFORMATION
), /* FilePipeLocalInformation */
3874 0, /* FilePipeRemoteInformation */
3875 sizeof(FILE_MAILSLOT_QUERY_INFORMATION
), /* FileMailslotQueryInformation */
3876 0, /* FileMailslotSetInformation */
3877 0, /* FileCompressionInformation */
3878 0, /* FileObjectIdInformation */
3879 0, /* FileCompletionInformation */
3880 0, /* FileMoveClusterInformation */
3881 0, /* FileQuotaInformation */
3882 0, /* FileReparsePointInformation */
3883 sizeof(FILE_NETWORK_OPEN_INFORMATION
), /* FileNetworkOpenInformation */
3884 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
), /* FileAttributeTagInformation */
3885 0, /* FileTrackingInformation */
3886 0, /* FileIdBothDirectoryInformation */
3887 0, /* FileIdFullDirectoryInformation */
3888 0, /* FileValidDataLengthInformation */
3889 0, /* FileShortNameInformation */
3890 0, /* FileIoCompletionNotificationInformation, */
3891 0, /* FileIoStatusBlockRangeInformation */
3892 0, /* FileIoPriorityHintInformation */
3893 0, /* FileSfioReserveInformation */
3894 0, /* FileSfioVolumeInformation */
3895 0, /* FileHardLinkInformation */
3896 0, /* FileProcessIdsUsingFileInformation */
3897 0, /* FileNormalizedNameInformation */
3898 0, /* FileNetworkPhysicalNameInformation */
3899 0, /* FileIdGlobalTxDirectoryInformation */
3900 0, /* FileIsRemoteDeviceInformation */
3901 0, /* FileAttributeCacheInformation */
3902 0, /* FileNumaNodeInformation */
3903 0, /* FileStandardLinkInformation */
3904 0, /* FileRemoteProtocolInformation */
3905 0, /* FileRenameInformationBypassAccessCheck */
3906 0, /* FileLinkInformationBypassAccessCheck */
3907 0, /* FileVolumeNameInformation */
3908 sizeof(FILE_ID_INFORMATION
), /* FileIdInformation */
3909 0, /* FileIdExtdDirectoryInformation */
3910 0, /* FileReplaceCompletionInformation */
3911 0, /* FileHardLinkFullIdInformation */
3912 0, /* FileIdExtdBothDirectoryInformation */
3916 int fd
, needs_close
= FALSE
;
3918 unsigned int options
;
3920 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, len
, class);
3922 io
->Information
= 0;
3924 if (class <= 0 || class >= FileMaximumInformation
)
3925 return io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
3926 if (!info_sizes
[class])
3927 return server_get_file_info( handle
, io
, ptr
, len
, class );
3928 if (len
< info_sizes
[class])
3929 return io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
3931 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, &options
)))
3933 if (io
->u
.Status
!= STATUS_BAD_DEVICE_TYPE
) return io
->u
.Status
;
3934 return server_get_file_info( handle
, io
, ptr
, len
, class );
3939 case FileBasicInformation
:
3940 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1)
3941 io
->u
.Status
= errno_to_status( errno
);
3942 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
3943 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
3945 fill_file_info( &st
, attr
, ptr
, class );
3947 case FileStandardInformation
:
3949 FILE_STANDARD_INFORMATION
*info
= ptr
;
3951 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
3954 fill_file_info( &st
, attr
, info
, class );
3955 info
->DeletePending
= FALSE
; /* FIXME */
3959 case FilePositionInformation
:
3961 FILE_POSITION_INFORMATION
*info
= ptr
;
3962 off_t res
= lseek( fd
, 0, SEEK_CUR
);
3963 if (res
== (off_t
)-1) io
->u
.Status
= errno_to_status( errno
);
3964 else info
->CurrentByteOffset
.QuadPart
= res
;
3967 case FileInternalInformation
:
3968 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
3969 else fill_file_info( &st
, attr
, ptr
, class );
3971 case FileEaInformation
:
3973 FILE_EA_INFORMATION
*info
= ptr
;
3977 case FileEndOfFileInformation
:
3978 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
3979 else fill_file_info( &st
, attr
, ptr
, class );
3981 case FileAllInformation
:
3983 FILE_ALL_INFORMATION
*info
= ptr
;
3986 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
3987 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
3988 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
3989 else if (!(io
->u
.Status
= server_get_unix_name( handle
, &unix_name
)))
3991 LONG name_len
= len
- FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
);
3993 fill_file_info( &st
, attr
, info
, FileAllInformation
);
3994 info
->StandardInformation
.DeletePending
= FALSE
; /* FIXME */
3995 info
->EaInformation
.EaSize
= 0;
3996 info
->AccessInformation
.AccessFlags
= 0; /* FIXME */
3997 info
->PositionInformation
.CurrentByteOffset
.QuadPart
= lseek( fd
, 0, SEEK_CUR
);
3998 info
->ModeInformation
.Mode
= 0; /* FIXME */
3999 info
->AlignmentInformation
.AlignmentRequirement
= 1; /* FIXME */
4001 io
->u
.Status
= fill_name_info( unix_name
, &info
->NameInformation
, &name_len
);
4003 io
->Information
= FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
) + name_len
;
4007 case FileMailslotQueryInformation
:
4009 FILE_MAILSLOT_QUERY_INFORMATION
*info
= ptr
;
4011 SERVER_START_REQ( set_mailslot_info
)
4013 req
->handle
= wine_server_obj_handle( handle
);
4015 io
->u
.Status
= wine_server_call( req
);
4016 if( io
->u
.Status
== STATUS_SUCCESS
)
4018 info
->MaximumMessageSize
= reply
->max_msgsize
;
4019 info
->MailslotQuota
= 0;
4020 info
->NextMessageSize
= 0;
4021 info
->MessagesAvailable
= 0;
4022 info
->ReadTimeout
.QuadPart
= reply
->read_timeout
;
4029 ULONG size
= info
->MaximumMessageSize
? info
->MaximumMessageSize
: 0x10000;
4030 if (size
> 0x10000) size
= 0x10000;
4031 if ((tmpbuf
= malloc( size
)))
4033 if (!server_get_unix_fd( handle
, FILE_READ_DATA
, &fd
, &needs_close
, NULL
, NULL
))
4035 int res
= recv( fd
, tmpbuf
, size
, MSG_PEEK
);
4036 info
->MessagesAvailable
= (res
> 0);
4037 info
->NextMessageSize
= (res
>= 0) ? res
: MAILSLOT_NO_MESSAGE
;
4038 if (needs_close
) close( fd
);
4045 case FileNameInformation
:
4047 FILE_NAME_INFORMATION
*info
= ptr
;
4050 if (!(io
->u
.Status
= server_get_unix_name( handle
, &unix_name
)))
4052 LONG name_len
= len
- FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
);
4053 io
->u
.Status
= fill_name_info( unix_name
, info
, &name_len
);
4055 io
->Information
= FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
) + name_len
;
4059 case FileNetworkOpenInformation
:
4061 FILE_NETWORK_OPEN_INFORMATION
*info
= ptr
;
4064 if (!(io
->u
.Status
= server_get_unix_name( handle
, &unix_name
)))
4069 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4070 io
->u
.Status
= errno_to_status( errno
);
4071 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4072 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4075 FILE_BASIC_INFORMATION basic
;
4076 FILE_STANDARD_INFORMATION std
;
4078 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
4079 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
4081 info
->CreationTime
= basic
.CreationTime
;
4082 info
->LastAccessTime
= basic
.LastAccessTime
;
4083 info
->LastWriteTime
= basic
.LastWriteTime
;
4084 info
->ChangeTime
= basic
.ChangeTime
;
4085 info
->AllocationSize
= std
.AllocationSize
;
4086 info
->EndOfFile
= std
.EndOfFile
;
4087 info
->FileAttributes
= basic
.FileAttributes
;
4093 case FileIdInformation
:
4094 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4097 struct mountmgr_unix_drive drive
;
4098 FILE_ID_INFORMATION
*info
= ptr
;
4100 info
->VolumeSerialNumber
= 0;
4101 if (!(io
->u
.Status
= get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) )))
4102 info
->VolumeSerialNumber
= drive
.serial
;
4103 memset( &info
->FileId
, 0, sizeof(info
->FileId
) );
4104 *(ULONGLONG
*)&info
->FileId
= st
.st_ino
;
4107 case FileAttributeTagInformation
:
4108 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) io
->u
.Status
= errno_to_status( errno
);
4111 FILE_ATTRIBUTE_TAG_INFORMATION
*info
= ptr
;
4112 info
->FileAttributes
= attr
;
4113 info
->ReparseTag
= 0; /* FIXME */
4114 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, &st
))
4115 info
->ReparseTag
= IO_REPARSE_TAG_MOUNT_POINT
;
4119 FIXME("Unsupported class (%d)\n", class);
4120 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
4123 if (needs_close
) close( fd
);
4124 if (io
->u
.Status
== STATUS_SUCCESS
&& !io
->Information
) io
->Information
= info_sizes
[class];
4125 return io
->u
.Status
;
4129 /******************************************************************************
4130 * NtSetInformationFile (NTDLL.@)
4132 NTSTATUS WINAPI
NtSetInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
4133 void *ptr
, ULONG len
, FILE_INFORMATION_CLASS
class )
4135 int fd
, needs_close
;
4137 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, len
, class );
4139 io
->u
.Status
= STATUS_SUCCESS
;
4142 case FileBasicInformation
:
4143 if (len
>= sizeof(FILE_BASIC_INFORMATION
))
4146 const FILE_BASIC_INFORMATION
*info
= ptr
;
4147 LARGE_INTEGER mtime
, atime
;
4149 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4150 return io
->u
.Status
;
4152 mtime
.QuadPart
= info
->LastWriteTime
.QuadPart
== -1 ? 0 : info
->LastWriteTime
.QuadPart
;
4153 atime
.QuadPart
= info
->LastAccessTime
.QuadPart
== -1 ? 0 : info
->LastAccessTime
.QuadPart
;
4155 if (atime
.QuadPart
|| mtime
.QuadPart
)
4156 io
->u
.Status
= set_file_times( fd
, &mtime
, &atime
);
4158 if (io
->u
.Status
== STATUS_SUCCESS
&& info
->FileAttributes
)
4160 if (fstat( fd
, &st
) == -1) io
->u
.Status
= errno_to_status( errno
);
4163 if (info
->FileAttributes
& FILE_ATTRIBUTE_READONLY
)
4165 if (S_ISDIR( st
.st_mode
))
4166 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
4168 st
.st_mode
&= ~0222; /* clear write permission bits */
4172 /* add write permission only where we already have read permission */
4173 st
.st_mode
|= (0600 | ((st
.st_mode
& 044) >> 1)) & (~start_umask
);
4175 if (fchmod( fd
, st
.st_mode
) == -1) io
->u
.Status
= errno_to_status( errno
);
4179 if (needs_close
) close( fd
);
4181 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4184 case FilePositionInformation
:
4185 if (len
>= sizeof(FILE_POSITION_INFORMATION
))
4187 const FILE_POSITION_INFORMATION
*info
= ptr
;
4189 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4190 return io
->u
.Status
;
4192 if (lseek( fd
, info
->CurrentByteOffset
.QuadPart
, SEEK_SET
) == (off_t
)-1)
4193 io
->u
.Status
= errno_to_status( errno
);
4195 if (needs_close
) close( fd
);
4197 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4200 case FileEndOfFileInformation
:
4201 if (len
>= sizeof(FILE_END_OF_FILE_INFORMATION
))
4204 const FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
4206 if ((io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4207 return io
->u
.Status
;
4209 /* first try normal truncate */
4210 if (ftruncate( fd
, (off_t
)info
->EndOfFile
.QuadPart
) != -1) break;
4212 /* now check for the need to extend the file */
4213 if (fstat( fd
, &st
) != -1 && (off_t
)info
->EndOfFile
.QuadPart
> st
.st_size
)
4215 static const char zero
;
4217 /* extend the file one byte beyond the requested size and then truncate it */
4218 /* this should work around ftruncate implementations that can't extend files */
4219 if (pwrite( fd
, &zero
, 1, (off_t
)info
->EndOfFile
.QuadPart
) != -1 &&
4220 ftruncate( fd
, (off_t
)info
->EndOfFile
.QuadPart
) != -1) break;
4222 io
->u
.Status
= errno_to_status( errno
);
4224 if (needs_close
) close( fd
);
4226 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4229 case FilePipeInformation
:
4230 if (len
>= sizeof(FILE_PIPE_INFORMATION
))
4232 FILE_PIPE_INFORMATION
*info
= ptr
;
4234 if ((info
->CompletionMode
| info
->ReadMode
) & ~1)
4236 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
4240 SERVER_START_REQ( set_named_pipe_info
)
4242 req
->handle
= wine_server_obj_handle( handle
);
4243 req
->flags
= (info
->CompletionMode
? NAMED_PIPE_NONBLOCKING_MODE
: 0) |
4244 (info
->ReadMode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0);
4245 io
->u
.Status
= wine_server_call( req
);
4249 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4252 case FileMailslotSetInformation
:
4254 FILE_MAILSLOT_SET_INFORMATION
*info
= ptr
;
4256 SERVER_START_REQ( set_mailslot_info
)
4258 req
->handle
= wine_server_obj_handle( handle
);
4259 req
->flags
= MAILSLOT_SET_READ_TIMEOUT
;
4260 req
->read_timeout
= info
->ReadTimeout
.QuadPart
;
4261 io
->u
.Status
= wine_server_call( req
);
4267 case FileCompletionInformation
:
4268 if (len
>= sizeof(FILE_COMPLETION_INFORMATION
))
4270 FILE_COMPLETION_INFORMATION
*info
= ptr
;
4272 SERVER_START_REQ( set_completion_info
)
4274 req
->handle
= wine_server_obj_handle( handle
);
4275 req
->chandle
= wine_server_obj_handle( info
->CompletionPort
);
4276 req
->ckey
= info
->CompletionKey
;
4277 io
->u
.Status
= wine_server_call( req
);
4281 io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4284 case FileIoCompletionNotificationInformation
:
4285 if (len
>= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
))
4287 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
*info
= ptr
;
4289 if (info
->Flags
& FILE_SKIP_SET_USER_EVENT_ON_FAST_IO
)
4290 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4292 SERVER_START_REQ( set_fd_completion_mode
)
4294 req
->handle
= wine_server_obj_handle( handle
);
4295 req
->flags
= info
->Flags
;
4296 io
->u
.Status
= wine_server_call( req
);
4300 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4303 case FileIoPriorityHintInformation
:
4304 if (len
>= sizeof(FILE_IO_PRIORITY_HINT_INFO
))
4306 FILE_IO_PRIORITY_HINT_INFO
*info
= ptr
;
4307 if (info
->PriorityHint
< MaximumIoPriorityHintType
)
4308 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info
->PriorityHint
);
4310 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
4312 else io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4315 case FileAllInformation
:
4316 io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4319 case FileValidDataLengthInformation
:
4320 if (len
>= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
))
4323 const FILE_VALID_DATA_LENGTH_INFORMATION
*info
= ptr
;
4325 if ((io
->u
.Status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, NULL
, NULL
)))
4326 return io
->u
.Status
;
4328 if (fstat( fd
, &st
) == -1) io
->u
.Status
= errno_to_status( errno
);
4329 else if (info
->ValidDataLength
.QuadPart
<= 0 || (off_t
)info
->ValidDataLength
.QuadPart
> st
.st_size
)
4330 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
4333 #ifdef HAVE_FALLOCATE
4334 if (fallocate( fd
, 0, 0, (off_t
)info
->ValidDataLength
.QuadPart
) == -1)
4336 NTSTATUS status
= errno_to_status( errno
);
4337 if (status
== STATUS_NOT_SUPPORTED
) WARN( "fallocate not supported on this filesystem\n" );
4338 else io
->u
.Status
= status
;
4341 FIXME( "setting valid data length not supported\n" );
4344 if (needs_close
) close( fd
);
4346 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4349 case FileDispositionInformation
:
4350 if (len
>= sizeof(FILE_DISPOSITION_INFORMATION
))
4352 FILE_DISPOSITION_INFORMATION
*info
= ptr
;
4354 SERVER_START_REQ( set_fd_disp_info
)
4356 req
->handle
= wine_server_obj_handle( handle
);
4357 req
->unlink
= info
->DoDeleteFile
;
4358 io
->u
.Status
= wine_server_call( req
);
4362 io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4365 case FileRenameInformation
:
4366 if (len
>= sizeof(FILE_RENAME_INFORMATION
))
4368 FILE_RENAME_INFORMATION
*info
= ptr
;
4369 UNICODE_STRING name_str
, nt_name
= { 0 };
4370 OBJECT_ATTRIBUTES attr
;
4373 name_str
.Buffer
= info
->FileName
;
4374 name_str
.Length
= info
->FileNameLength
;
4375 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4377 attr
.Length
= sizeof(attr
);
4378 attr
.ObjectName
= &name_str
;
4379 attr
.RootDirectory
= info
->RootDirectory
;
4380 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
4382 io
->u
.Status
= nt_to_unix_file_name_attr( &attr
, &unix_name
, &nt_name
, FILE_OPEN_IF
);
4383 if (io
->u
.Status
!= STATUS_SUCCESS
&& io
->u
.Status
!= STATUS_NO_SUCH_FILE
)
4386 SERVER_START_REQ( set_fd_name_info
)
4388 req
->handle
= wine_server_obj_handle( handle
);
4389 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4390 req
->namelen
= nt_name
.Length
;
4392 req
->replace
= info
->ReplaceIfExists
;
4393 wine_server_add_data( req
, nt_name
.Buffer
, nt_name
.Length
);
4394 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4395 io
->u
.Status
= wine_server_call( req
);
4400 free( nt_name
.Buffer
);
4402 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4405 case FileLinkInformation
:
4406 if (len
>= sizeof(FILE_LINK_INFORMATION
))
4408 FILE_LINK_INFORMATION
*info
= ptr
;
4409 UNICODE_STRING name_str
, nt_name
= { 0 };
4410 OBJECT_ATTRIBUTES attr
;
4413 name_str
.Buffer
= info
->FileName
;
4414 name_str
.Length
= info
->FileNameLength
;
4415 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4417 attr
.Length
= sizeof(attr
);
4418 attr
.ObjectName
= &name_str
;
4419 attr
.RootDirectory
= info
->RootDirectory
;
4420 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
4422 io
->u
.Status
= nt_to_unix_file_name_attr( &attr
, &unix_name
, &nt_name
, FILE_OPEN_IF
);
4423 if (io
->u
.Status
!= STATUS_SUCCESS
&& io
->u
.Status
!= STATUS_NO_SUCH_FILE
)
4426 SERVER_START_REQ( set_fd_name_info
)
4428 req
->handle
= wine_server_obj_handle( handle
);
4429 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4430 req
->namelen
= nt_name
.Length
;
4432 req
->replace
= info
->ReplaceIfExists
;
4433 wine_server_add_data( req
, nt_name
.Buffer
, nt_name
.Length
);
4434 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4435 io
->u
.Status
= wine_server_call( req
);
4440 free( nt_name
.Buffer
);
4442 else io
->u
.Status
= STATUS_INVALID_PARAMETER_3
;
4446 FIXME("Unsupported class (%d)\n", class);
4447 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
4450 io
->Information
= 0;
4451 return io
->u
.Status
;
4455 /***********************************************************************
4456 * Asynchronous file I/O *
4459 typedef NTSTATUS
async_callback_t( void *user
, IO_STATUS_BLOCK
*io
, NTSTATUS status
);
4463 async_callback_t
*callback
; /* must be the first field */
4464 struct async_fileio
*next
;
4468 struct async_fileio_read
4470 struct async_fileio io
;
4472 unsigned int already
;
4477 struct async_fileio_write
4479 struct async_fileio io
;
4481 unsigned int already
;
4485 struct async_fileio_read_changes
4487 struct async_fileio io
;
4496 struct async_fileio io
;
4497 void *buffer
; /* buffer for output */
4498 ULONG size
; /* size of buffer */
4501 static struct async_fileio
*fileio_freelist
;
4503 static void release_fileio( struct async_fileio
*io
)
4507 struct async_fileio
*next
= fileio_freelist
;
4509 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist
, io
, next
) == next
) return;
4513 static struct async_fileio
*alloc_fileio( DWORD size
, async_callback_t callback
, HANDLE handle
)
4515 /* first free remaining previous fileinfos */
4516 struct async_fileio
*io
= InterlockedExchangePointer( (void **)&fileio_freelist
, NULL
);
4520 struct async_fileio
*next
= io
->next
;
4525 if ((io
= malloc( size
)))
4527 io
->callback
= callback
;
4528 io
->handle
= handle
;
4533 static async_data_t
server_async( HANDLE handle
, struct async_fileio
*user
, HANDLE event
,
4534 PIO_APC_ROUTINE apc
, void *apc_context
, IO_STATUS_BLOCK
*io
)
4537 async
.handle
= wine_server_obj_handle( handle
);
4538 async
.user
= wine_server_client_ptr( user
);
4539 async
.iosb
= wine_server_client_ptr( io
);
4540 async
.event
= wine_server_obj_handle( event
);
4541 async
.apc
= wine_server_client_ptr( apc
);
4542 async
.apc_context
= wine_server_client_ptr( apc_context
);
4546 static NTSTATUS
wait_async( HANDLE handle
, BOOL alertable
, IO_STATUS_BLOCK
*io
)
4548 if (NtWaitForSingleObject( handle
, alertable
, NULL
)) return STATUS_PENDING
;
4549 return io
->u
.Status
;
4552 /* callback for irp async I/O completion */
4553 static NTSTATUS
irp_completion( void *user
, IO_STATUS_BLOCK
*io
, NTSTATUS status
)
4555 struct async_irp
*async
= user
;
4556 ULONG information
= 0;
4558 if (status
== STATUS_ALERTED
)
4560 SERVER_START_REQ( get_async_result
)
4562 req
->user_arg
= wine_server_client_ptr( async
);
4563 wine_server_set_reply( req
, async
->buffer
, async
->size
);
4564 status
= virtual_locked_server_call( req
);
4565 information
= reply
->size
;
4569 if (status
!= STATUS_PENDING
)
4571 io
->u
.Status
= status
;
4572 io
->Information
= information
;
4573 release_fileio( &async
->io
);
4578 static NTSTATUS
async_read_proc( void *user
, IO_STATUS_BLOCK
*iosb
, NTSTATUS status
)
4580 struct async_fileio_read
*fileio
= user
;
4581 int fd
, needs_close
, result
;
4585 case STATUS_ALERTED
: /* got some new data */
4586 /* check to see if the data is ready (non-blocking) */
4587 if ((status
= server_get_unix_fd( fileio
->io
.handle
, FILE_READ_DATA
, &fd
,
4588 &needs_close
, NULL
, NULL
)))
4591 result
= virtual_locked_read(fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
-fileio
->already
);
4592 if (needs_close
) close( fd
);
4596 if (errno
== EAGAIN
|| errno
== EINTR
)
4597 status
= STATUS_PENDING
;
4598 else /* check to see if the transfer is complete */
4599 status
= errno_to_status( errno
);
4601 else if (result
== 0)
4603 status
= fileio
->already
? STATUS_SUCCESS
: STATUS_PIPE_BROKEN
;
4607 fileio
->already
+= result
;
4608 if (fileio
->already
>= fileio
->count
|| fileio
->avail_mode
)
4609 status
= STATUS_SUCCESS
;
4611 status
= STATUS_PENDING
;
4615 case STATUS_TIMEOUT
:
4616 case STATUS_IO_TIMEOUT
:
4617 if (fileio
->already
) status
= STATUS_SUCCESS
;
4620 if (status
!= STATUS_PENDING
)
4622 iosb
->u
.Status
= status
;
4623 iosb
->Information
= fileio
->already
;
4624 release_fileio( &fileio
->io
);
4629 static NTSTATUS
async_write_proc( void *user
, IO_STATUS_BLOCK
*iosb
, NTSTATUS status
)
4631 struct async_fileio_write
*fileio
= user
;
4632 int result
, fd
, needs_close
;
4633 enum server_fd_type type
;
4637 case STATUS_ALERTED
:
4638 /* write some data (non-blocking) */
4639 if ((status
= server_get_unix_fd( fileio
->io
.handle
, FILE_WRITE_DATA
, &fd
,
4640 &needs_close
, &type
, NULL
)))
4643 if (!fileio
->count
&& (type
== FD_TYPE_MAILSLOT
|| type
== FD_TYPE_SOCKET
))
4644 result
= send( fd
, fileio
->buffer
, 0, 0 );
4646 result
= write( fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
- fileio
->already
);
4648 if (needs_close
) close( fd
);
4652 if (errno
== EAGAIN
|| errno
== EINTR
) status
= STATUS_PENDING
;
4653 else status
= errno_to_status( errno
);
4657 fileio
->already
+= result
;
4658 status
= (fileio
->already
< fileio
->count
) ? STATUS_PENDING
: STATUS_SUCCESS
;
4662 case STATUS_TIMEOUT
:
4663 case STATUS_IO_TIMEOUT
:
4664 if (fileio
->already
) status
= STATUS_SUCCESS
;
4667 if (status
!= STATUS_PENDING
)
4669 iosb
->u
.Status
= status
;
4670 iosb
->Information
= fileio
->already
;
4671 release_fileio( &fileio
->io
);
4676 /* do a read call through the server */
4677 static NTSTATUS
server_read_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
4678 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG size
,
4679 LARGE_INTEGER
*offset
, ULONG
*key
)
4681 struct async_irp
*async
;
4686 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4687 return STATUS_NO_MEMORY
;
4689 async
->buffer
= buffer
;
4692 SERVER_START_REQ( read
)
4694 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, io
);
4695 req
->pos
= offset
? offset
->QuadPart
: 0;
4696 wine_server_set_reply( req
, buffer
, size
);
4697 status
= virtual_locked_server_call( req
);
4698 wait_handle
= wine_server_ptr_handle( reply
->wait
);
4699 options
= reply
->options
;
4700 if (wait_handle
&& status
!= STATUS_PENDING
)
4702 io
->u
.Status
= status
;
4703 io
->Information
= wine_server_reply_size( reply
);
4708 if (status
!= STATUS_PENDING
) free( async
);
4710 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
), io
);
4714 /* do a write call through the server */
4715 static NTSTATUS
server_write_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
4716 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG size
,
4717 LARGE_INTEGER
*offset
, ULONG
*key
)
4719 struct async_irp
*async
;
4724 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4725 return STATUS_NO_MEMORY
;
4727 async
->buffer
= NULL
;
4730 SERVER_START_REQ( write
)
4732 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, io
);
4733 req
->pos
= offset
? offset
->QuadPart
: 0;
4734 wine_server_add_data( req
, buffer
, size
);
4735 status
= wine_server_call( req
);
4736 wait_handle
= wine_server_ptr_handle( reply
->wait
);
4737 options
= reply
->options
;
4738 if (wait_handle
&& status
!= STATUS_PENDING
)
4740 io
->u
.Status
= status
;
4741 io
->Information
= reply
->size
;
4746 if (status
!= STATUS_PENDING
) free( async
);
4748 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
), io
);
4752 /* do an ioctl call through the server */
4753 static NTSTATUS
server_ioctl_file( HANDLE handle
, HANDLE event
,
4754 PIO_APC_ROUTINE apc
, PVOID apc_context
,
4755 IO_STATUS_BLOCK
*io
, ULONG code
,
4756 const void *in_buffer
, ULONG in_size
,
4757 PVOID out_buffer
, ULONG out_size
)
4759 struct async_irp
*async
;
4764 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4765 return STATUS_NO_MEMORY
;
4766 async
->buffer
= out_buffer
;
4767 async
->size
= out_size
;
4769 SERVER_START_REQ( ioctl
)
4772 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, io
);
4773 wine_server_add_data( req
, in_buffer
, in_size
);
4774 if ((code
& 3) != METHOD_BUFFERED
) wine_server_add_data( req
, out_buffer
, out_size
);
4775 wine_server_set_reply( req
, out_buffer
, out_size
);
4776 status
= virtual_locked_server_call( req
);
4777 wait_handle
= wine_server_ptr_handle( reply
->wait
);
4778 options
= reply
->options
;
4779 if (wait_handle
&& status
!= STATUS_PENDING
)
4781 io
->u
.Status
= status
;
4782 io
->Information
= wine_server_reply_size( reply
);
4787 if (status
== STATUS_NOT_SUPPORTED
)
4788 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
4789 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
4791 if (status
!= STATUS_PENDING
) free( async
);
4793 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
), io
);
4800 int interval
; /* max interval between two bytes */
4801 int total
; /* total timeout for the whole operation */
4802 int end_time
; /* absolute time of end of operation */
4805 /* retrieve the I/O timeouts to use for a given handle */
4806 static NTSTATUS
get_io_timeouts( HANDLE handle
, enum server_fd_type type
, ULONG count
, BOOL is_read
,
4807 struct io_timeouts
*timeouts
)
4809 NTSTATUS status
= STATUS_SUCCESS
;
4811 timeouts
->interval
= timeouts
->total
= -1;
4815 case FD_TYPE_SERIAL
:
4817 /* GetCommTimeouts */
4821 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
4822 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
4827 if (st
.ReadIntervalTimeout
)
4828 timeouts
->interval
= st
.ReadIntervalTimeout
;
4830 if (st
.ReadTotalTimeoutMultiplier
|| st
.ReadTotalTimeoutConstant
)
4832 timeouts
->total
= st
.ReadTotalTimeoutConstant
;
4833 if (st
.ReadTotalTimeoutMultiplier
!= MAXDWORD
)
4834 timeouts
->total
+= count
* st
.ReadTotalTimeoutMultiplier
;
4836 else if (st
.ReadIntervalTimeout
== MAXDWORD
)
4837 timeouts
->interval
= timeouts
->total
= 0;
4841 if (st
.WriteTotalTimeoutMultiplier
|| st
.WriteTotalTimeoutConstant
)
4843 timeouts
->total
= st
.WriteTotalTimeoutConstant
;
4844 if (st
.WriteTotalTimeoutMultiplier
!= MAXDWORD
)
4845 timeouts
->total
+= count
* st
.WriteTotalTimeoutMultiplier
;
4850 case FD_TYPE_MAILSLOT
:
4853 timeouts
->interval
= 0; /* return as soon as we got something */
4854 SERVER_START_REQ( set_mailslot_info
)
4856 req
->handle
= wine_server_obj_handle( handle
);
4858 if (!(status
= wine_server_call( req
)) &&
4859 reply
->read_timeout
!= TIMEOUT_INFINITE
)
4860 timeouts
->total
= reply
->read_timeout
/ -10000;
4865 case FD_TYPE_SOCKET
:
4867 if (is_read
) timeouts
->interval
= 0; /* return as soon as we got something */
4872 if (timeouts
->total
!= -1) timeouts
->end_time
= NtGetTickCount() + timeouts
->total
;
4873 return STATUS_SUCCESS
;
4877 /* retrieve the timeout for the next wait, in milliseconds */
4878 static inline int get_next_io_timeout( const struct io_timeouts
*timeouts
, ULONG already
)
4882 if (timeouts
->total
!= -1)
4884 ret
= timeouts
->end_time
- NtGetTickCount();
4885 if (ret
< 0) ret
= 0;
4887 if (already
&& timeouts
->interval
!= -1)
4889 if (ret
== -1 || ret
> timeouts
->interval
) ret
= timeouts
->interval
;
4895 /* retrieve the avail_mode flag for async reads */
4896 static NTSTATUS
get_io_avail_mode( HANDLE handle
, enum server_fd_type type
, BOOL
*avail_mode
)
4898 NTSTATUS status
= STATUS_SUCCESS
;
4902 case FD_TYPE_SERIAL
:
4904 /* GetCommTimeouts */
4908 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
4909 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
4911 *avail_mode
= (!st
.ReadTotalTimeoutMultiplier
&&
4912 !st
.ReadTotalTimeoutConstant
&&
4913 st
.ReadIntervalTimeout
== MAXDWORD
);
4916 case FD_TYPE_MAILSLOT
:
4917 case FD_TYPE_SOCKET
:
4922 *avail_mode
= FALSE
;
4928 /* register an async I/O for a file read; helper for NtReadFile */
4929 static NTSTATUS
register_async_file_read( HANDLE handle
, HANDLE event
,
4930 PIO_APC_ROUTINE apc
, void *apc_user
,
4931 IO_STATUS_BLOCK
*iosb
, void *buffer
,
4932 ULONG already
, ULONG length
, BOOL avail_mode
)
4934 struct async_fileio_read
*fileio
;
4937 if (!(fileio
= (struct async_fileio_read
*)alloc_fileio( sizeof(*fileio
), async_read_proc
, handle
)))
4938 return STATUS_NO_MEMORY
;
4940 fileio
->already
= already
;
4941 fileio
->count
= length
;
4942 fileio
->buffer
= buffer
;
4943 fileio
->avail_mode
= avail_mode
;
4945 SERVER_START_REQ( register_async
)
4947 req
->type
= ASYNC_TYPE_READ
;
4948 req
->count
= length
;
4949 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, iosb
);
4950 status
= wine_server_call( req
);
4954 if (status
!= STATUS_PENDING
) free( fileio
);
4958 static void add_completion( HANDLE handle
, ULONG_PTR value
, NTSTATUS status
, ULONG info
, BOOL async
)
4960 SERVER_START_REQ( add_fd_completion
)
4962 req
->handle
= wine_server_obj_handle( handle
);
4963 req
->cvalue
= value
;
4964 req
->status
= status
;
4965 req
->information
= info
;
4967 wine_server_call( req
);
4972 static NTSTATUS
set_pending_write( HANDLE device
)
4976 SERVER_START_REQ( set_serial_info
)
4978 req
->handle
= wine_server_obj_handle( device
);
4979 req
->flags
= SERIALINFO_PENDING_WRITE
;
4980 status
= wine_server_call( req
);
4987 /******************************************************************************
4988 * NtReadFile (NTDLL.@)
4990 NTSTATUS WINAPI
NtReadFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
4991 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
4992 LARGE_INTEGER
*offset
, ULONG
*key
)
4994 int result
, unix_handle
, needs_close
;
4995 unsigned int options
;
4996 struct io_timeouts timeouts
;
4997 NTSTATUS status
, ret_status
;
4999 enum server_fd_type type
;
5000 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5001 BOOL send_completion
= FALSE
, async_read
, timeout_init_done
= FALSE
;
5003 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5004 handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5006 if (!io
) return STATUS_ACCESS_VIOLATION
;
5008 status
= server_get_unix_fd( handle
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5009 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5011 if (!virtual_check_buffer_for_write( buffer
, length
)) return STATUS_ACCESS_VIOLATION
;
5013 if (status
== STATUS_BAD_DEVICE_TYPE
)
5014 return server_read_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5016 async_read
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5018 if (type
== FD_TYPE_FILE
)
5020 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5022 status
= STATUS_INVALID_PARAMETER
;
5026 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5028 /* async I/O doesn't make sense on regular files */
5029 while ((result
= virtual_locked_pread( unix_handle
, buffer
, length
, offset
->QuadPart
)) == -1)
5033 status
= errno_to_status( errno
);
5037 if (!async_read
) /* update file pointer position */
5038 lseek( unix_handle
, offset
->QuadPart
+ result
, SEEK_SET
);
5041 status
= (total
|| !length
) ? STATUS_SUCCESS
: STATUS_END_OF_FILE
;
5045 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5047 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5049 status
= STATUS_INVALID_PARAMETER
;
5054 if (type
== FD_TYPE_SERIAL
&& async_read
&& length
)
5056 /* an asynchronous serial port read with a read interval timeout needs to
5057 skip the synchronous read to make sure that the server starts the read
5058 interval timer after the first read */
5059 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5060 if (timeouts
.interval
)
5062 status
= register_async_file_read( handle
, event
, apc
, apc_user
, io
,
5063 buffer
, total
, length
, FALSE
);
5070 if ((result
= virtual_locked_read( unix_handle
, (char *)buffer
+ total
, length
- total
)) >= 0)
5073 if (!result
|| total
== length
)
5077 status
= STATUS_SUCCESS
;
5084 case FD_TYPE_DEVICE
:
5085 status
= length
? STATUS_END_OF_FILE
: STATUS_SUCCESS
;
5087 case FD_TYPE_SERIAL
:
5090 status
= STATUS_SUCCESS
;
5095 status
= STATUS_PIPE_BROKEN
;
5099 else if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5101 else if (errno
!= EAGAIN
)
5103 if (errno
== EINTR
) continue;
5104 if (!total
) status
= errno_to_status( errno
);
5112 if ((status
= get_io_avail_mode( handle
, type
, &avail_mode
))) goto err
;
5113 if (total
&& avail_mode
)
5115 status
= STATUS_SUCCESS
;
5118 status
= register_async_file_read( handle
, event
, apc
, apc_user
, io
,
5119 buffer
, total
, length
, avail_mode
);
5122 else /* synchronous read, wait for the fd to become ready */
5127 if (!timeout_init_done
)
5129 timeout_init_done
= TRUE
;
5130 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5131 if (event
) NtResetEvent( event
, NULL
);
5133 timeout
= get_next_io_timeout( &timeouts
, total
);
5135 pfd
.fd
= unix_handle
;
5136 pfd
.events
= POLLIN
;
5138 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
5140 if (total
) /* return with what we got so far */
5141 status
= STATUS_SUCCESS
;
5143 status
= (type
== FD_TYPE_MAILSLOT
) ? STATUS_IO_TIMEOUT
: STATUS_TIMEOUT
;
5146 if (ret
== -1 && errno
!= EINTR
)
5148 status
= errno_to_status( errno
);
5151 /* will now restart the read */
5156 send_completion
= cvalue
!= 0;
5159 if (needs_close
) close( unix_handle
);
5160 if (status
== STATUS_SUCCESS
|| (status
== STATUS_END_OF_FILE
&& (!async_read
|| type
== FD_TYPE_FILE
)))
5162 io
->u
.Status
= status
;
5163 io
->Information
= total
;
5164 TRACE("= SUCCESS (%u)\n", total
);
5165 if (event
) NtSetEvent( event
, NULL
);
5166 if (apc
&& (!status
|| async_read
)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5167 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5171 TRACE("= 0x%08x\n", status
);
5172 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5175 ret_status
= async_read
&& type
== FD_TYPE_FILE
&& (status
== STATUS_SUCCESS
|| status
== STATUS_END_OF_FILE
)
5176 ? STATUS_PENDING
: status
;
5178 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
5183 /******************************************************************************
5184 * NtReadFileScatter (NTDLL.@)
5186 NTSTATUS WINAPI
NtReadFileScatter( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5187 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
5188 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
5190 int result
, unix_handle
, needs_close
;
5191 unsigned int options
;
5193 ULONG pos
= 0, total
= 0;
5194 enum server_fd_type type
;
5195 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5196 BOOL send_completion
= FALSE
;
5198 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5199 file
, event
, apc
, apc_user
, io
, segments
, length
, offset
, key
);
5201 if (!io
) return STATUS_ACCESS_VIOLATION
;
5203 status
= server_get_unix_fd( file
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5204 if (status
) return status
;
5206 if ((type
!= FD_TYPE_FILE
) ||
5207 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
5208 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
5210 status
= STATUS_INVALID_PARAMETER
;
5216 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5217 result
= pread( unix_handle
, (char *)segments
->Buffer
+ pos
,
5218 min( length
- pos
, page_size
- pos
), offset
->QuadPart
+ total
);
5220 result
= read( unix_handle
, (char *)segments
->Buffer
+ pos
, min( length
- pos
, page_size
- pos
) );
5224 if (errno
== EINTR
) continue;
5225 status
= errno_to_status( errno
);
5231 if ((pos
+= result
) == page_size
)
5238 if (total
== 0) status
= STATUS_END_OF_FILE
;
5240 send_completion
= cvalue
!= 0;
5242 if (needs_close
) close( unix_handle
);
5243 io
->u
.Status
= status
;
5244 io
->Information
= total
;
5245 TRACE("= 0x%08x (%u)\n", status
, total
);
5246 if (event
) NtSetEvent( event
, NULL
);
5247 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5248 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5249 if (send_completion
) add_completion( file
, cvalue
, status
, total
, TRUE
);
5251 return STATUS_PENDING
;
5254 if (needs_close
) close( unix_handle
);
5255 if (event
) NtResetEvent( event
, NULL
);
5256 TRACE("= 0x%08x\n", status
);
5261 /******************************************************************************
5262 * NtWriteFile (NTDLL.@)
5264 NTSTATUS WINAPI
NtWriteFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5265 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG length
,
5266 LARGE_INTEGER
*offset
, ULONG
*key
)
5268 int result
, unix_handle
, needs_close
;
5269 unsigned int options
;
5270 struct io_timeouts timeouts
;
5271 NTSTATUS status
, ret_status
;
5273 enum server_fd_type type
;
5274 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5275 BOOL send_completion
= FALSE
, async_write
, append_write
= FALSE
, timeout_init_done
= FALSE
;
5276 LARGE_INTEGER offset_eof
;
5278 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5279 handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5281 if (!io
) return STATUS_ACCESS_VIOLATION
;
5283 status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5284 if (status
== STATUS_ACCESS_DENIED
)
5286 status
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &unix_handle
,
5287 &needs_close
, &type
, &options
);
5288 append_write
= TRUE
;
5290 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5292 async_write
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5294 if (!virtual_check_buffer_for_read( buffer
, length
))
5296 status
= STATUS_INVALID_USER_BUFFER
;
5300 if (status
== STATUS_BAD_DEVICE_TYPE
)
5301 return server_write_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5303 if (type
== FD_TYPE_FILE
)
5306 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5308 status
= STATUS_INVALID_PARAMETER
;
5314 offset_eof
.QuadPart
= FILE_WRITE_TO_END_OF_FILE
;
5315 offset
= &offset_eof
;
5318 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5320 off_t off
= offset
->QuadPart
;
5322 if (offset
->QuadPart
== FILE_WRITE_TO_END_OF_FILE
)
5326 if (fstat( unix_handle
, &st
) == -1)
5328 status
= errno_to_status( errno
);
5333 else if (offset
->QuadPart
< 0)
5335 status
= STATUS_INVALID_PARAMETER
;
5339 /* async I/O doesn't make sense on regular files */
5340 while ((result
= pwrite( unix_handle
, buffer
, length
, off
)) == -1)
5344 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5345 else status
= errno_to_status( errno
);
5350 if (!async_write
) /* update file pointer position */
5351 lseek( unix_handle
, off
+ result
, SEEK_SET
);
5354 status
= STATUS_SUCCESS
;
5358 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5361 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5363 status
= STATUS_INVALID_PARAMETER
;
5370 /* zero-length writes on sockets may not work with plain write(2) */
5371 if (!length
&& (type
== FD_TYPE_MAILSLOT
|| type
== FD_TYPE_SOCKET
))
5372 result
= send( unix_handle
, buffer
, 0, 0 );
5374 result
= write( unix_handle
, (const char *)buffer
+ total
, length
- total
);
5379 if (total
== length
)
5381 status
= STATUS_SUCCESS
;
5384 if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5386 else if (errno
!= EAGAIN
)
5388 if (errno
== EINTR
) continue;
5391 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5392 else status
= errno_to_status( errno
);
5399 struct async_fileio_write
*fileio
;
5401 fileio
= (struct async_fileio_write
*)alloc_fileio( sizeof(*fileio
), async_write_proc
, handle
);
5404 status
= STATUS_NO_MEMORY
;
5407 fileio
->already
= total
;
5408 fileio
->count
= length
;
5409 fileio
->buffer
= buffer
;
5411 SERVER_START_REQ( register_async
)
5413 req
->type
= ASYNC_TYPE_WRITE
;
5414 req
->count
= length
;
5415 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, io
);
5416 status
= wine_server_call( req
);
5420 if (status
!= STATUS_PENDING
) free( fileio
);
5423 else /* synchronous write, wait for the fd to become ready */
5428 if (!timeout_init_done
)
5430 timeout_init_done
= TRUE
;
5431 if ((status
= get_io_timeouts( handle
, type
, length
, FALSE
, &timeouts
)))
5433 if (event
) NtResetEvent( event
, NULL
);
5435 timeout
= get_next_io_timeout( &timeouts
, total
);
5437 pfd
.fd
= unix_handle
;
5438 pfd
.events
= POLLOUT
;
5440 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
5442 /* return with what we got so far */
5443 status
= total
? STATUS_SUCCESS
: STATUS_TIMEOUT
;
5446 if (ret
== -1 && errno
!= EINTR
)
5448 status
= errno_to_status( errno
);
5451 /* will now restart the write */
5456 send_completion
= cvalue
!= 0;
5459 if (needs_close
) close( unix_handle
);
5461 if (type
== FD_TYPE_SERIAL
&& (status
== STATUS_SUCCESS
|| status
== STATUS_PENDING
))
5462 set_pending_write( handle
);
5464 if (status
== STATUS_SUCCESS
)
5466 io
->u
.Status
= status
;
5467 io
->Information
= total
;
5468 TRACE("= SUCCESS (%u)\n", total
);
5469 if (event
) NtSetEvent( event
, NULL
);
5470 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5471 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5475 TRACE("= 0x%08x\n", status
);
5476 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5479 ret_status
= async_write
&& type
== FD_TYPE_FILE
&& status
== STATUS_SUCCESS
? STATUS_PENDING
: status
;
5480 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
5485 /******************************************************************************
5486 * NtWriteFileGather (NTDLL.@)
5488 NTSTATUS WINAPI
NtWriteFileGather( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5489 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
5490 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
5492 int result
, unix_handle
, needs_close
;
5493 unsigned int options
;
5495 ULONG pos
= 0, total
= 0;
5496 enum server_fd_type type
;
5497 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5498 BOOL send_completion
= FALSE
;
5500 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5501 file
, event
, apc
, apc_user
, io
, segments
, length
, offset
, key
);
5503 if (length
% page_size
) return STATUS_INVALID_PARAMETER
;
5504 if (!io
) return STATUS_ACCESS_VIOLATION
;
5506 status
= server_get_unix_fd( file
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5507 if (status
) return status
;
5509 if ((type
!= FD_TYPE_FILE
) ||
5510 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
5511 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
5513 status
= STATUS_INVALID_PARAMETER
;
5519 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5520 result
= pwrite( unix_handle
, (char *)segments
->Buffer
+ pos
,
5521 page_size
- pos
, offset
->QuadPart
+ total
);
5523 result
= write( unix_handle
, (char *)segments
->Buffer
+ pos
, page_size
- pos
);
5527 if (errno
== EINTR
) continue;
5528 if (errno
== EFAULT
)
5530 status
= STATUS_INVALID_USER_BUFFER
;
5533 status
= errno_to_status( errno
);
5538 status
= STATUS_DISK_FULL
;
5543 if ((pos
+= result
) == page_size
)
5550 send_completion
= cvalue
!= 0;
5553 if (needs_close
) close( unix_handle
);
5554 if (status
== STATUS_SUCCESS
)
5556 io
->u
.Status
= status
;
5557 io
->Information
= total
;
5558 TRACE("= SUCCESS (%u)\n", total
);
5559 if (event
) NtSetEvent( event
, NULL
);
5560 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5561 (ULONG_PTR
)apc_user
, (ULONG_PTR
)io
, 0 );
5565 TRACE("= 0x%08x\n", status
);
5566 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5568 if (send_completion
) add_completion( file
, cvalue
, status
, total
, FALSE
);
5573 /******************************************************************************
5574 * NtDeviceIoControlFile (NTDLL.@)
5576 NTSTATUS WINAPI
NtDeviceIoControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5577 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
5578 void *out_buffer
, ULONG out_size
)
5580 ULONG device
= (code
>> 16);
5581 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
5583 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5584 handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
5588 case FILE_DEVICE_DISK
:
5589 case FILE_DEVICE_CD_ROM
:
5590 case FILE_DEVICE_DVD
:
5591 case FILE_DEVICE_CONTROLLER
:
5592 case FILE_DEVICE_MASS_STORAGE
:
5593 status
= cdrom_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5594 in_buffer
, in_size
, out_buffer
, out_size
);
5596 case FILE_DEVICE_SERIAL_PORT
:
5597 status
= serial_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5598 in_buffer
, in_size
, out_buffer
, out_size
);
5600 case FILE_DEVICE_TAPE
:
5601 status
= tape_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5602 in_buffer
, in_size
, out_buffer
, out_size
);
5606 if (status
== STATUS_NOT_SUPPORTED
|| status
== STATUS_BAD_DEVICE_TYPE
)
5607 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5608 in_buffer
, in_size
, out_buffer
, out_size
);
5610 if (status
!= STATUS_PENDING
) io
->u
.Status
= status
;
5615 /* Tell Valgrind to ignore any holes in structs we will be passing to the
5617 static void ignore_server_ioctl_struct_holes( ULONG code
, const void *in_buffer
, ULONG in_size
)
5619 #ifdef VALGRIND_MAKE_MEM_DEFINED
5620 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
5622 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
5623 if ((size) >= FIELD_OFFSET(t, f2)) \
5624 VALGRIND_MAKE_MEM_DEFINED( \
5625 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
5626 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
5631 case FSCTL_PIPE_WAIT
:
5632 IGNORE_STRUCT_HOLE(in_buffer
, in_size
, FILE_PIPE_WAIT_FOR_BUFFER
, TimeoutSpecified
, Name
);
5639 /******************************************************************************
5640 * NtFsControlFile (NTDLL.@)
5642 NTSTATUS WINAPI
NtFsControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5643 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
5644 void *out_buffer
, ULONG out_size
)
5648 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5649 handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
5651 if (!io
) return STATUS_INVALID_PARAMETER
;
5653 ignore_server_ioctl_struct_holes( code
, in_buffer
, in_size
);
5657 case FSCTL_DISMOUNT_VOLUME
:
5658 status
= server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5659 in_buffer
, in_size
, out_buffer
, out_size
);
5660 if (!status
) status
= unmount_device( handle
);
5663 case FSCTL_PIPE_IMPERSONATE
:
5664 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
5665 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5666 in_buffer
, in_size
, out_buffer
, out_size
);
5668 case FSCTL_IS_VOLUME_MOUNTED
:
5669 case FSCTL_LOCK_VOLUME
:
5670 case FSCTL_UNLOCK_VOLUME
:
5671 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
5672 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
5673 status
= STATUS_SUCCESS
;
5676 case FSCTL_GET_RETRIEVAL_POINTERS
:
5678 RETRIEVAL_POINTERS_BUFFER
*buffer
= (RETRIEVAL_POINTERS_BUFFER
*)out_buffer
;
5680 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
5682 if (out_size
>= sizeof(RETRIEVAL_POINTERS_BUFFER
))
5684 buffer
->ExtentCount
= 1;
5685 buffer
->StartingVcn
.QuadPart
= 1;
5686 buffer
->Extents
[0].NextVcn
.QuadPart
= 0;
5687 buffer
->Extents
[0].Lcn
.QuadPart
= 0;
5688 io
->Information
= sizeof(RETRIEVAL_POINTERS_BUFFER
);
5689 status
= STATUS_SUCCESS
;
5693 io
->Information
= 0;
5694 status
= STATUS_BUFFER_TOO_SMALL
;
5699 case FSCTL_GET_OBJECT_ID
:
5701 FILE_OBJECTID_BUFFER
*info
= out_buffer
;
5702 int fd
, needs_close
;
5705 io
->Information
= 0;
5706 if (out_size
>= sizeof(*info
))
5708 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
5711 if (needs_close
) close( fd
);
5712 memset( info
, 0, sizeof(*info
) );
5713 memcpy( info
->ObjectId
, &st
.st_dev
, sizeof(st
.st_dev
) );
5714 memcpy( info
->ObjectId
+ 8, &st
.st_ino
, sizeof(st
.st_ino
) );
5715 io
->Information
= sizeof(*info
);
5717 else status
= STATUS_BUFFER_TOO_SMALL
;
5721 case FSCTL_SET_SPARSE
:
5722 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
5723 io
->Information
= 0;
5724 status
= STATUS_SUCCESS
;
5727 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5728 in_buffer
, in_size
, out_buffer
, out_size
);
5731 if (status
!= STATUS_PENDING
) io
->u
.Status
= status
;
5736 /******************************************************************************
5737 * NtFlushBuffersFile (NTDLL.@)
5739 NTSTATUS WINAPI
NtFlushBuffersFile( HANDLE handle
, IO_STATUS_BLOCK
*io
)
5743 enum server_fd_type type
;
5744 int fd
, needs_close
;
5746 if (!io
|| !virtual_check_buffer_for_write( io
, sizeof(*io
) )) return STATUS_ACCESS_VIOLATION
;
5748 ret
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, &type
, NULL
);
5749 if (ret
== STATUS_ACCESS_DENIED
)
5750 ret
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &fd
, &needs_close
, &type
, NULL
);
5752 if (!ret
&& (type
== FD_TYPE_FILE
|| type
== FD_TYPE_DIR
|| type
== FD_TYPE_CHAR
))
5754 if (fsync(fd
)) ret
= errno_to_status( errno
);
5756 io
->Information
= 0;
5758 else if (!ret
&& type
== FD_TYPE_SERIAL
)
5760 ret
= serial_FlushBuffersFile( fd
);
5762 else if (ret
!= STATUS_ACCESS_DENIED
)
5764 struct async_irp
*async
;
5766 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
5767 return STATUS_NO_MEMORY
;
5768 async
->buffer
= NULL
;
5771 SERVER_START_REQ( flush
)
5773 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, io
);
5774 ret
= wine_server_call( req
);
5775 wait_handle
= wine_server_ptr_handle( reply
->event
);
5776 if (wait_handle
&& ret
!= STATUS_PENDING
)
5779 io
->Information
= 0;
5784 if (ret
!= STATUS_PENDING
) free( async
);
5786 if (wait_handle
) ret
= wait_async( wait_handle
, FALSE
, io
);
5789 if (needs_close
) close( fd
);
5794 /**************************************************************************
5795 * NtCancelIoFile (NTDLL.@)
5797 NTSTATUS WINAPI
NtCancelIoFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
)
5799 TRACE( "%p %p\n", handle
, io_status
);
5801 SERVER_START_REQ( cancel_async
)
5803 req
->handle
= wine_server_obj_handle( handle
);
5804 req
->only_thread
= TRUE
;
5805 io_status
->u
.Status
= wine_server_call( req
);
5808 return io_status
->u
.Status
;
5812 /**************************************************************************
5813 * NtCancelIoFileEx (NTDLL.@)
5815 NTSTATUS WINAPI
NtCancelIoFileEx( HANDLE handle
, IO_STATUS_BLOCK
*io
, IO_STATUS_BLOCK
*io_status
)
5817 TRACE( "%p %p %p\n", handle
, io
, io_status
);
5819 SERVER_START_REQ( cancel_async
)
5821 req
->handle
= wine_server_obj_handle( handle
);
5822 req
->iosb
= wine_server_client_ptr( io
);
5823 io_status
->u
.Status
= wine_server_call( req
);
5826 return io_status
->u
.Status
;
5830 /******************************************************************
5831 * NtLockFile (NTDLL.@)
5833 NTSTATUS WINAPI
NtLockFile( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void* apc_user
,
5834 IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
5835 LARGE_INTEGER
*count
, ULONG
*key
, BOOLEAN dont_wait
, BOOLEAN exclusive
)
5842 if (apc
|| io_status
|| key
)
5844 FIXME("Unimplemented yet parameter\n");
5845 return STATUS_NOT_IMPLEMENTED
;
5847 if (apc_user
&& !warn
++) FIXME("I/O completion on lock not implemented yet\n");
5851 SERVER_START_REQ( lock_file
)
5853 req
->handle
= wine_server_obj_handle( file
);
5854 req
->offset
= offset
->QuadPart
;
5855 req
->count
= count
->QuadPart
;
5856 req
->shared
= !exclusive
;
5857 req
->wait
= !dont_wait
;
5858 ret
= wine_server_call( req
);
5859 handle
= wine_server_ptr_handle( reply
->handle
);
5860 async
= reply
->overlapped
;
5863 if (ret
!= STATUS_PENDING
)
5865 if (!ret
&& event
) NtSetEvent( event
, NULL
);
5870 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
5871 if (handle
) NtClose( handle
);
5872 return STATUS_PENDING
;
5876 NtWaitForSingleObject( handle
, FALSE
, NULL
);
5879 else /* Unix lock conflict, sleep a bit and retry */
5882 time
.QuadPart
= -100 * (ULONGLONG
)10000;
5883 NtDelayExecution( FALSE
, &time
);
5889 /******************************************************************
5890 * NtUnlockFile (NTDLL.@)
5892 NTSTATUS WINAPI
NtUnlockFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
5893 LARGE_INTEGER
*count
, ULONG
*key
)
5897 TRACE( "%p %x%08x %x%08x\n",
5898 handle
, offset
->u
.HighPart
, offset
->u
.LowPart
, count
->u
.HighPart
, count
->u
.LowPart
);
5900 if (io_status
|| key
)
5902 FIXME("Unimplemented yet parameter\n");
5903 return STATUS_NOT_IMPLEMENTED
;
5906 SERVER_START_REQ( unlock_file
)
5908 req
->handle
= wine_server_obj_handle( handle
);
5909 req
->offset
= offset
->QuadPart
;
5910 req
->count
= count
->QuadPart
;
5911 status
= wine_server_call( req
);
5918 static NTSTATUS
read_changes_apc( void *user
, IO_STATUS_BLOCK
*iosb
, NTSTATUS status
)
5920 struct async_fileio_read_changes
*fileio
= user
;
5923 if (status
== STATUS_ALERTED
)
5925 SERVER_START_REQ( read_change
)
5927 req
->handle
= wine_server_obj_handle( fileio
->io
.handle
);
5928 wine_server_set_reply( req
, fileio
->data
, fileio
->data_size
);
5929 status
= wine_server_call( req
);
5930 size
= wine_server_reply_size( reply
);
5934 if (status
== STATUS_SUCCESS
&& fileio
->buffer
)
5936 FILE_NOTIFY_INFORMATION
*pfni
= fileio
->buffer
;
5937 int i
, left
= fileio
->buffer_size
;
5938 DWORD
*last_entry_offset
= NULL
;
5939 struct filesystem_event
*event
= (struct filesystem_event
*)fileio
->data
;
5941 while (size
&& left
>= sizeof(*pfni
))
5943 DWORD len
= (left
- offsetof(FILE_NOTIFY_INFORMATION
, FileName
)) / sizeof(WCHAR
);
5945 /* convert to an NT style path */
5946 for (i
= 0; i
< event
->len
; i
++)
5947 if (event
->name
[i
] == '/') event
->name
[i
] = '\\';
5949 pfni
->Action
= event
->action
;
5950 pfni
->FileNameLength
= ntdll_umbstowcs( event
->name
, event
->len
, pfni
->FileName
, len
);
5951 last_entry_offset
= &pfni
->NextEntryOffset
;
5953 if (pfni
->FileNameLength
== len
) break;
5955 i
= offsetof(FILE_NOTIFY_INFORMATION
, FileName
[pfni
->FileNameLength
]);
5956 pfni
->FileNameLength
*= sizeof(WCHAR
);
5957 pfni
->NextEntryOffset
= i
;
5958 pfni
= (FILE_NOTIFY_INFORMATION
*)((char*)pfni
+ i
);
5961 i
= (offsetof(struct filesystem_event
, name
[event
->len
])
5962 + sizeof(int)-1) / sizeof(int) * sizeof(int);
5963 event
= (struct filesystem_event
*)((char*)event
+ i
);
5969 status
= STATUS_NOTIFY_ENUM_DIR
;
5974 if (last_entry_offset
) *last_entry_offset
= 0;
5975 size
= fileio
->buffer_size
- left
;
5980 status
= STATUS_NOTIFY_ENUM_DIR
;
5985 if (status
!= STATUS_PENDING
)
5987 iosb
->u
.Status
= status
;
5988 iosb
->Information
= size
;
5989 release_fileio( &fileio
->io
);
5994 #define FILE_NOTIFY_ALL ( \
5995 FILE_NOTIFY_CHANGE_FILE_NAME | \
5996 FILE_NOTIFY_CHANGE_DIR_NAME | \
5997 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
5998 FILE_NOTIFY_CHANGE_SIZE | \
5999 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6000 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6001 FILE_NOTIFY_CHANGE_CREATION | \
6002 FILE_NOTIFY_CHANGE_SECURITY )
6004 /******************************************************************************
6005 * NtNotifyChangeDirectoryFile (NTDLL.@)
6007 NTSTATUS WINAPI
NtNotifyChangeDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
,
6008 void *apc_context
, IO_STATUS_BLOCK
*iosb
, void *buffer
,
6009 ULONG buffer_size
, ULONG filter
, BOOLEAN subtree
)
6011 struct async_fileio_read_changes
*fileio
;
6013 ULONG size
= max( 4096, buffer_size
);
6015 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6016 handle
, event
, apc
, apc_context
, iosb
, buffer
, buffer_size
, filter
, subtree
);
6018 if (!iosb
) return STATUS_ACCESS_VIOLATION
;
6019 if (filter
== 0 || (filter
& ~FILE_NOTIFY_ALL
)) return STATUS_INVALID_PARAMETER
;
6021 fileio
= (struct async_fileio_read_changes
*)alloc_fileio(
6022 offsetof(struct async_fileio_read_changes
, data
[size
]), read_changes_apc
, handle
);
6023 if (!fileio
) return STATUS_NO_MEMORY
;
6025 fileio
->buffer
= buffer
;
6026 fileio
->buffer_size
= buffer_size
;
6027 fileio
->data_size
= size
;
6029 SERVER_START_REQ( read_directory_changes
)
6031 req
->filter
= filter
;
6032 req
->want_data
= (buffer
!= NULL
);
6033 req
->subtree
= subtree
;
6034 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_context
, iosb
);
6035 status
= wine_server_call( req
);
6039 if (status
!= STATUS_PENDING
) free( fileio
);
6044 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6045 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6046 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION
*info
, const char *fstypename
,
6047 unsigned int flags
)
6049 if (!strcmp("cd9660", fstypename
) || !strcmp("udf", fstypename
))
6051 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6052 /* Don't assume read-only, let the mount options set it below */
6053 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6055 else if (!strcmp("nfs", fstypename
) || !strcmp("nwfs", fstypename
) ||
6056 !strcmp("smbfs", fstypename
) || !strcmp("afpfs", fstypename
))
6058 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6059 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6061 else if (!strcmp("procfs", fstypename
))
6062 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6064 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6066 if (flags
& MNT_RDONLY
)
6067 info
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
6069 if (!(flags
& MNT_LOCAL
))
6071 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6072 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6077 static inline BOOL
is_device_placeholder( int fd
)
6079 static const char wine_placeholder
[] = "Wine device placeholder";
6080 char buffer
[sizeof(wine_placeholder
)-1];
6082 if (pread( fd
, buffer
, sizeof(wine_placeholder
) - 1, 0 ) != sizeof(wine_placeholder
) - 1)
6084 return !memcmp( buffer
, wine_placeholder
, sizeof(wine_placeholder
) - 1 );
6087 static NTSTATUS
get_device_info( int fd
, FILE_FS_DEVICE_INFORMATION
*info
)
6091 info
->Characteristics
= 0;
6092 if (fstat( fd
, &st
) < 0) return errno_to_status( errno
);
6093 if (S_ISCHR( st
.st_mode
))
6095 info
->DeviceType
= FILE_DEVICE_UNKNOWN
;
6097 switch(major(st
.st_rdev
))
6100 info
->DeviceType
= FILE_DEVICE_NULL
;
6103 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6106 info
->DeviceType
= FILE_DEVICE_PARALLEL_PORT
;
6108 case SCSI_TAPE_MAJOR
:
6109 info
->DeviceType
= FILE_DEVICE_TAPE
;
6112 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6115 if (ioctl(fd
, FIODTYPE
, &d_type
) == 0)
6120 info
->DeviceType
= FILE_DEVICE_TAPE
;
6123 info
->DeviceType
= FILE_DEVICE_DISK
;
6126 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6128 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6130 info
->DeviceType
= FILE_DEVICE_NULL
;
6134 /* no special d_type for parallel ports */
6139 else if (S_ISBLK( st
.st_mode
))
6141 info
->DeviceType
= FILE_DEVICE_DISK
;
6143 else if (S_ISFIFO( st
.st_mode
) || S_ISSOCK( st
.st_mode
))
6145 info
->DeviceType
= FILE_DEVICE_NAMED_PIPE
;
6147 else if (is_device_placeholder( fd
))
6149 info
->DeviceType
= FILE_DEVICE_DISK
;
6151 else /* regular file or directory */
6153 #if defined(linux) && defined(HAVE_FSTATFS)
6156 /* check for floppy disk */
6157 if (major(st
.st_dev
) == FLOPPY_MAJOR
)
6158 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6160 if (fstatfs( fd
, &stfs
) < 0) stfs
.f_type
= 0;
6161 switch (stfs
.f_type
)
6163 case 0x9660: /* iso9660 */
6164 case 0x9fa1: /* supermount */
6165 case 0x15013346: /* udf */
6166 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6167 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6169 case 0x6969: /* nfs */
6170 case 0xff534d42: /* cifs */
6171 case 0xfe534d42: /* smb2 */
6172 case 0x517b: /* smbfs */
6173 case 0x564c: /* ncpfs */
6174 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6175 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6177 case 0x1373: /* devfs */
6178 case 0x9fa0: /* procfs */
6179 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6181 case 0x01021994: /* tmpfs */
6182 case 0x28cd3d45: /* cramfs */
6183 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6184 * filesystems are rare on Windows, and some programs refuse to
6185 * recognize them as valid. */
6187 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6190 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6193 if (fstatfs( fd
, &stfs
) < 0)
6194 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6196 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flags
);
6197 #elif defined(__NetBSD__)
6198 struct statvfs stfs
;
6200 if (fstatvfs( fd
, &stfs
) < 0)
6201 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6203 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flag
);
6205 /* Use dkio to work out device types */
6207 # include <sys/dkio.h>
6208 # include <sys/vtoc.h>
6209 struct dk_cinfo dkinf
;
6210 int retval
= ioctl(fd
, DKIOCINFO
, &dkinf
);
6212 WARN("Unable to get disk device type information - assuming a disk like device\n");
6213 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6215 switch (dkinf
.dki_ctype
)
6218 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6219 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6223 case DKC_INTEL82072
:
6224 case DKC_INTEL82077
:
6225 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6226 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6229 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6230 * filesystems are rare on Windows, and some programs refuse to
6231 * recognize them as valid. */
6233 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6238 if (!warned
++) FIXME( "device info not properly supported on this platform\n" );
6239 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6241 info
->Characteristics
|= FILE_DEVICE_IS_MOUNTED
;
6243 return STATUS_SUCCESS
;
6247 /******************************************************************************
6248 * NtQueryVolumeInformationFile (NTDLL.@)
6250 NTSTATUS WINAPI
NtQueryVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
6251 void *buffer
, ULONG length
,
6252 FS_INFORMATION_CLASS info_class
)
6254 int fd
, needs_close
;
6257 io
->u
.Status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
6258 if (io
->u
.Status
== STATUS_BAD_DEVICE_TYPE
)
6260 struct async_irp
*async
;
6264 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
6265 return STATUS_NO_MEMORY
;
6266 async
->buffer
= buffer
;
6267 async
->size
= length
;
6269 SERVER_START_REQ( get_volume_info
)
6271 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, io
);
6272 req
->handle
= wine_server_obj_handle( handle
);
6273 req
->info_class
= info_class
;
6274 wine_server_set_reply( req
, buffer
, length
);
6275 status
= wine_server_call( req
);
6276 if (status
!= STATUS_PENDING
)
6278 io
->u
.Status
= status
;
6279 io
->Information
= wine_server_reply_size( reply
);
6281 wait_handle
= wine_server_ptr_handle( reply
->wait
);
6284 if (status
!= STATUS_PENDING
) free( async
);
6285 if (wait_handle
) status
= wait_async( wait_handle
, FALSE
, io
);
6288 else if (io
->u
.Status
) return io
->u
.Status
;
6290 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
6291 io
->Information
= 0;
6293 switch( info_class
)
6295 case FileFsLabelInformation
:
6296 FIXME( "%p: label info not supported\n", handle
);
6299 case FileFsSizeInformation
:
6300 if (length
< sizeof(FILE_FS_SIZE_INFORMATION
))
6301 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
6304 FILE_FS_SIZE_INFORMATION
*info
= buffer
;
6306 if (fstat( fd
, &st
) < 0)
6308 io
->u
.Status
= errno_to_status( errno
);
6311 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
6313 io
->u
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
6318 /* Linux's fstatvfs is buggy */
6319 #if !defined(linux) || !defined(HAVE_FSTATFS)
6320 struct statvfs stfs
;
6322 if (fstatvfs( fd
, &stfs
) < 0)
6324 io
->u
.Status
= errno_to_status( errno
);
6327 bsize
= stfs
.f_frsize
;
6330 if (fstatfs( fd
, &stfs
) < 0)
6332 io
->u
.Status
= errno_to_status( errno
);
6335 bsize
= stfs
.f_bsize
;
6337 if (bsize
== 2048) /* assume CD-ROM */
6339 info
->BytesPerSector
= 2048;
6340 info
->SectorsPerAllocationUnit
= 1;
6344 info
->BytesPerSector
= 512;
6345 info
->SectorsPerAllocationUnit
= 8;
6347 info
->TotalAllocationUnits
.QuadPart
= bsize
* stfs
.f_blocks
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
6348 info
->AvailableAllocationUnits
.QuadPart
= bsize
* stfs
.f_bavail
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
6349 io
->Information
= sizeof(*info
);
6350 io
->u
.Status
= STATUS_SUCCESS
;
6355 case FileFsDeviceInformation
:
6356 if (length
< sizeof(FILE_FS_DEVICE_INFORMATION
))
6357 io
->u
.Status
= STATUS_BUFFER_TOO_SMALL
;
6360 FILE_FS_DEVICE_INFORMATION
*info
= buffer
;
6362 if ((io
->u
.Status
= get_device_info( fd
, info
)) == STATUS_SUCCESS
)
6363 io
->Information
= sizeof(*info
);
6367 case FileFsAttributeInformation
:
6369 static const WCHAR fatW
[] = {'F','A','T'};
6370 static const WCHAR fat32W
[] = {'F','A','T','3','2'};
6371 static const WCHAR ntfsW
[] = {'N','T','F','S'};
6372 static const WCHAR cdfsW
[] = {'C','D','F','S'};
6373 static const WCHAR udfW
[] = {'U','D','F'};
6375 FILE_FS_ATTRIBUTE_INFORMATION
*info
= buffer
;
6376 struct mountmgr_unix_drive drive
;
6377 enum mountmgr_fs_type fs_type
= MOUNTMGR_FS_TYPE_NTFS
;
6379 if (length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
))
6381 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
6385 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) )) fs_type
= drive
.fs_type
;
6390 if (!fstatfs( fd
, &stfs
))
6392 #if defined(linux) && defined(HAVE_FSTATFS)
6393 switch (stfs
.f_type
)
6396 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6399 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6402 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6405 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6406 if (!strcmp( stfs
.f_fstypename
, "cd9660" ))
6407 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6408 else if (!strcmp( stfs
.f_fstypename
, "udf" ))
6409 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6410 else if (!strcmp( stfs
.f_fstypename
, "msdos" )) /* FreeBSD < 5, Apple */
6411 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6412 else if (!strcmp( stfs
.f_fstypename
, "msdosfs" )) /* FreeBSD >= 5 */
6413 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6420 case MOUNTMGR_FS_TYPE_ISO9660
:
6421 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
;
6422 info
->MaximumComponentNameLength
= 221;
6423 info
->FileSystemNameLength
= min( sizeof(cdfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6424 memcpy(info
->FileSystemName
, cdfsW
, info
->FileSystemNameLength
);
6426 case MOUNTMGR_FS_TYPE_UDF
:
6427 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
| FILE_UNICODE_ON_DISK
| FILE_CASE_SENSITIVE_SEARCH
;
6428 info
->MaximumComponentNameLength
= 255;
6429 info
->FileSystemNameLength
= min( sizeof(udfW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6430 memcpy(info
->FileSystemName
, udfW
, info
->FileSystemNameLength
);
6432 case MOUNTMGR_FS_TYPE_FAT
:
6433 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
6434 info
->MaximumComponentNameLength
= 255;
6435 info
->FileSystemNameLength
= min( sizeof(fatW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6436 memcpy(info
->FileSystemName
, fatW
, info
->FileSystemNameLength
);
6438 case MOUNTMGR_FS_TYPE_FAT32
:
6439 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
6440 info
->MaximumComponentNameLength
= 255;
6441 info
->FileSystemNameLength
= min( sizeof(fat32W
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6442 memcpy(info
->FileSystemName
, fat32W
, info
->FileSystemNameLength
);
6445 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_PERSISTENT_ACLS
;
6446 info
->MaximumComponentNameLength
= 255;
6447 info
->FileSystemNameLength
= min( sizeof(ntfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6448 memcpy(info
->FileSystemName
, ntfsW
, info
->FileSystemNameLength
);
6452 io
->Information
= offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) + info
->FileSystemNameLength
;
6453 io
->u
.Status
= STATUS_SUCCESS
;
6457 case FileFsVolumeInformation
:
6459 FILE_FS_VOLUME_INFORMATION
*info
= buffer
;
6461 struct mountmgr_unix_drive
*drive
= (struct mountmgr_unix_drive
*)data
;
6464 if (length
< sizeof(FILE_FS_VOLUME_INFORMATION
))
6466 io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
6470 if (get_mountmgr_fs_info( handle
, fd
, drive
, sizeof(data
) ))
6472 io
->u
.Status
= STATUS_NOT_IMPLEMENTED
;
6476 label
= (WCHAR
*)((char *)drive
+ drive
->label_offset
);
6477 info
->VolumeCreationTime
.QuadPart
= 0; /* FIXME */
6478 info
->VolumeSerialNumber
= drive
->serial
;
6479 info
->VolumeLabelLength
= min( wcslen( label
) * sizeof(WCHAR
),
6480 length
- offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) );
6481 info
->SupportsObjects
= (drive
->fs_type
== MOUNTMGR_FS_TYPE_NTFS
);
6482 memcpy( info
->VolumeLabel
, label
, info
->VolumeLabelLength
);
6483 io
->Information
= offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) + info
->VolumeLabelLength
;
6484 io
->u
.Status
= STATUS_SUCCESS
;
6488 case FileFsControlInformation
:
6489 FIXME( "%p: control info not supported\n", handle
);
6492 case FileFsFullSizeInformation
:
6493 FIXME( "%p: full size info not supported\n", handle
);
6496 case FileFsObjectIdInformation
:
6497 FIXME( "%p: object id info not supported\n", handle
);
6500 case FileFsMaximumInformation
:
6501 FIXME( "%p: maximum info not supported\n", handle
);
6505 io
->u
.Status
= STATUS_INVALID_PARAMETER
;
6508 if (needs_close
) close( fd
);
6509 return io
->u
.Status
;
6513 /******************************************************************************
6514 * NtSetVolumeInformationFile (NTDLL.@)
6516 NTSTATUS WINAPI
NtSetVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *info
,
6517 ULONG length
, FS_INFORMATION_CLASS
class )
6519 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle
, io
, info
, length
, class );
6520 return STATUS_SUCCESS
;
6524 /******************************************************************
6525 * NtQueryEaFile (NTDLL.@)
6527 NTSTATUS WINAPI
NtQueryEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
6528 BOOLEAN single_entry
, void *list
, ULONG list_len
,
6529 ULONG
*index
, BOOLEAN restart
)
6531 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) stub\n",
6532 handle
, io
, buffer
, length
, single_entry
, list
, list_len
, index
, restart
);
6533 return STATUS_ACCESS_DENIED
;
6537 /******************************************************************
6538 * NtSetEaFile (NTDLL.@)
6540 NTSTATUS WINAPI
NtSetEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
)
6542 FIXME( "(%p,%p,%p,%d) stub\n", handle
, io
, buffer
, length
);
6543 return STATUS_ACCESS_DENIED
;
6547 /* convert type information from server format; helper for NtQueryObject */
6548 static void *put_object_type_info( OBJECT_TYPE_INFORMATION
*p
, struct object_type_info
*info
)
6550 const ULONG align
= sizeof(DWORD_PTR
) - 1;
6552 memset( p
, 0, sizeof(*p
) );
6553 p
->TypeName
.Buffer
= (WCHAR
*)(p
+ 1);
6554 p
->TypeName
.Length
= info
->name_len
;
6555 p
->TypeName
.MaximumLength
= info
->name_len
+ sizeof(WCHAR
);
6556 p
->TotalNumberOfObjects
= info
->obj_count
;
6557 p
->TotalNumberOfHandles
= info
->handle_count
;
6558 p
->HighWaterNumberOfObjects
= info
->obj_max
;
6559 p
->HighWaterNumberOfHandles
= info
->handle_max
;
6560 p
->TypeIndex
= info
->index
+ 2;
6561 p
->GenericMapping
.GenericRead
= info
->mapping
.read
;
6562 p
->GenericMapping
.GenericWrite
= info
->mapping
.write
;
6563 p
->GenericMapping
.GenericExecute
= info
->mapping
.exec
;
6564 p
->GenericMapping
.GenericAll
= info
->mapping
.all
;
6565 p
->ValidAccessMask
= info
->valid_access
;
6566 memcpy( p
->TypeName
.Buffer
, info
+ 1, info
->name_len
);
6567 p
->TypeName
.Buffer
[info
->name_len
/ sizeof(WCHAR
)] = 0;
6568 return (char *)(p
+ 1) + ((p
->TypeName
.MaximumLength
+ align
) & ~align
);
6571 /**************************************************************************
6572 * NtQueryObject (NTDLL.@)
6574 NTSTATUS WINAPI
NtQueryObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
6575 void *ptr
, ULONG len
, ULONG
*used_len
)
6579 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle
, info_class
, ptr
, len
, used_len
);
6581 if (used_len
) *used_len
= 0;
6585 case ObjectBasicInformation
:
6587 OBJECT_BASIC_INFORMATION
*p
= ptr
;
6589 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
6591 SERVER_START_REQ( get_object_info
)
6593 req
->handle
= wine_server_obj_handle( handle
);
6594 status
= wine_server_call( req
);
6595 if (status
== STATUS_SUCCESS
)
6597 memset( p
, 0, sizeof(*p
) );
6598 p
->GrantedAccess
= reply
->access
;
6599 p
->PointerCount
= reply
->ref_count
;
6600 p
->HandleCount
= reply
->handle_count
;
6601 if (used_len
) *used_len
= sizeof(*p
);
6608 case ObjectNameInformation
:
6610 OBJECT_NAME_INFORMATION
*p
= ptr
;
6614 /* first try as a file object */
6616 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
6618 if (!(status
= unix_to_nt_file_name( unix_name
, &nt_name
)))
6620 ULONG size
= (wcslen(nt_name
) + 1) * sizeof(WCHAR
);
6621 if (len
< sizeof(*p
)) status
= STATUS_INFO_LENGTH_MISMATCH
;
6622 else if (len
< sizeof(*p
) + size
) status
= STATUS_BUFFER_OVERFLOW
;
6625 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
6626 p
->Name
.Length
= size
- sizeof(WCHAR
);
6627 p
->Name
.MaximumLength
= size
;
6628 wcscpy( p
->Name
.Buffer
, nt_name
);
6630 if (used_len
) *used_len
= sizeof(*p
) + size
;
6636 else if (status
!= STATUS_OBJECT_TYPE_MISMATCH
) break;
6638 /* not a file, treat as a generic object */
6640 SERVER_START_REQ( get_object_info
)
6642 req
->handle
= wine_server_obj_handle( handle
);
6643 if (len
> sizeof(*p
)) wine_server_set_reply( req
, p
+ 1, len
- sizeof(*p
) );
6644 status
= wine_server_call( req
);
6645 if (status
== STATUS_SUCCESS
)
6647 if (!reply
->total
) /* no name */
6649 if (sizeof(*p
) > len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
6650 else memset( p
, 0, sizeof(*p
) );
6651 if (used_len
) *used_len
= sizeof(*p
);
6653 else if (sizeof(*p
) + reply
->total
+ sizeof(WCHAR
) > len
)
6655 if (used_len
) *used_len
= sizeof(*p
) + reply
->total
+ sizeof(WCHAR
);
6656 status
= STATUS_INFO_LENGTH_MISMATCH
;
6660 ULONG res
= wine_server_reply_size( reply
);
6661 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
6662 p
->Name
.Length
= res
;
6663 p
->Name
.MaximumLength
= res
+ sizeof(WCHAR
);
6664 p
->Name
.Buffer
[res
/ sizeof(WCHAR
)] = 0;
6665 if (used_len
) *used_len
= sizeof(*p
) + p
->Name
.MaximumLength
;
6673 case ObjectTypeInformation
:
6675 OBJECT_TYPE_INFORMATION
*p
= ptr
;
6676 char buffer
[sizeof(struct object_type_info
) + 64];
6677 struct object_type_info
*info
= (struct object_type_info
*)buffer
;
6679 SERVER_START_REQ( get_object_type
)
6681 req
->handle
= wine_server_obj_handle( handle
);
6682 wine_server_set_reply( req
, buffer
, sizeof(buffer
) );
6683 status
= wine_server_call( req
);
6687 if (sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
) <= len
)
6689 put_object_type_info( p
, info
);
6690 if (used_len
) *used_len
= sizeof(*p
) + p
->TypeName
.MaximumLength
;
6694 if (used_len
) *used_len
= sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
);
6695 status
= STATUS_INFO_LENGTH_MISMATCH
;
6700 case ObjectTypesInformation
:
6702 OBJECT_TYPES_INFORMATION
*types
= ptr
;
6703 OBJECT_TYPE_INFORMATION
*p
;
6704 struct object_type_info
*buffer
;
6705 /* assume at most 32 types, with an average 16-char name */
6706 ULONG size
= 32 * (sizeof(struct object_type_info
) + 16 * sizeof(WCHAR
));
6707 ULONG i
, count
, pos
, total
, align
= sizeof(DWORD_PTR
) - 1;
6709 buffer
= malloc( size
);
6710 SERVER_START_REQ( get_object_types
)
6712 wine_server_set_reply( req
, buffer
, size
);
6713 status
= wine_server_call( req
);
6714 count
= reply
->count
;
6719 if (len
>= sizeof(*types
)) types
->NumberOfTypes
= count
;
6720 total
= (sizeof(*types
) + align
) & ~align
;
6721 p
= (OBJECT_TYPE_INFORMATION
*)((char *)ptr
+ total
);
6722 for (i
= pos
= 0; i
< count
; i
++)
6724 struct object_type_info
*info
= (struct object_type_info
*)((char *)buffer
+ pos
);
6725 pos
+= sizeof(*info
) + ((info
->name_len
+ 3) & ~3);
6726 total
+= sizeof(*p
) + ((info
->name_len
+ sizeof(WCHAR
) + align
) & ~align
);
6727 if (total
<= len
) p
= put_object_type_info( p
, info
);
6729 if (used_len
) *used_len
= total
;
6730 if (total
> len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
6732 else if (status
== STATUS_BUFFER_OVERFLOW
) FIXME( "size %u too small\n", size
);
6738 case ObjectDataInformation
:
6740 OBJECT_DATA_INFORMATION
* p
= ptr
;
6742 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
6744 SERVER_START_REQ( set_handle_info
)
6746 req
->handle
= wine_server_obj_handle( handle
);
6749 status
= wine_server_call( req
);
6750 if (status
== STATUS_SUCCESS
)
6752 p
->InheritHandle
= (reply
->old_flags
& HANDLE_FLAG_INHERIT
) != 0;
6753 p
->ProtectFromClose
= (reply
->old_flags
& HANDLE_FLAG_PROTECT_FROM_CLOSE
) != 0;
6754 if (used_len
) *used_len
= sizeof(*p
);
6762 FIXME("Unsupported information class %u\n", info_class
);
6763 status
= STATUS_NOT_IMPLEMENTED
;
6770 /**************************************************************************
6771 * NtSetInformationObject (NTDLL.@)
6773 NTSTATUS WINAPI
NtSetInformationObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
6774 void *ptr
, ULONG len
)
6778 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle
, info_class
, ptr
, len
);
6782 case ObjectDataInformation
:
6784 OBJECT_DATA_INFORMATION
* p
= ptr
;
6786 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
6788 SERVER_START_REQ( set_handle_info
)
6790 req
->handle
= wine_server_obj_handle( handle
);
6791 req
->mask
= HANDLE_FLAG_INHERIT
| HANDLE_FLAG_PROTECT_FROM_CLOSE
;
6792 if (p
->InheritHandle
) req
->flags
|= HANDLE_FLAG_INHERIT
;
6793 if (p
->ProtectFromClose
) req
->flags
|= HANDLE_FLAG_PROTECT_FROM_CLOSE
;
6794 status
= wine_server_call( req
);
6801 FIXME("Unsupported information class %u\n", info_class
);
6802 status
= STATUS_NOT_IMPLEMENTED
;