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
30 #include <sys/types.h>
46 #ifdef HAVE_SYS_STATVFS_H
47 # include <sys/statvfs.h>
49 #ifdef HAVE_SYS_SYSCALL_H
50 # include <sys/syscall.h>
52 #include <sys/socket.h>
54 #include <sys/ioctl.h>
55 #ifdef HAVE_SYS_ATTR_H
59 # include <sys/mkdev.h>
60 #elif defined(MAJOR_IN_SYSMACROS)
61 # include <sys/sysmacros.h>
63 #ifdef HAVE_SYS_VNODE_H
65 # include <stdint.h> /* needed for kfreebsd */
67 /* Work around a conflict with Solaris' system list defined in sys/list.h. */
69 #define list_next SYSLIST_NEXT
70 #define list_prev SYSLIST_PREV
71 #define list_head SYSLIST_HEAD
72 #define list_tail SYSLIST_TAIL
73 #define list_move_tail SYSLIST_MOVE_TAIL
74 #define list_remove SYSLIST_REMOVE
75 #include <sys/vnode.h>
84 #ifdef HAVE_LINUX_IOCTL_H
85 #include <linux/ioctl.h>
87 #ifdef HAVE_LINUX_MAJOR_H
88 # include <linux/major.h>
90 #ifdef HAVE_SYS_PARAM_H
91 #include <sys/param.h>
93 #ifdef HAVE_SYS_CONF_H
96 #ifdef HAVE_SYS_MOUNT_H
97 #include <sys/mount.h>
99 #ifdef HAVE_SYS_STATFS_H
100 #include <sys/statfs.h>
102 #ifdef HAVE_SYS_XATTR_H
103 #include <sys/xattr.h>
105 #ifdef HAVE_SYS_EXTATTR_H
106 #undef XATTR_ADDITIONAL_OPTIONS
107 #include <sys/extattr.h>
112 #include "ntstatus.h"
113 #define WIN32_NO_STATUS
116 #include "winioctl.h"
117 #include "winternl.h"
118 #include "ddk/ntddk.h"
119 #include "ddk/ntddser.h"
121 #define WINE_MOUNTMGR_EXTENSIONS
122 #include "ddk/mountmgr.h"
123 #include "wine/server.h"
124 #include "wine/list.h"
125 #include "wine/debug.h"
126 #include "unix_private.h"
128 WINE_DEFAULT_DEBUG_CHANNEL(file
);
129 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
131 #define MAX_DOS_DRIVES 26
133 /* just in case... */
134 #undef VFAT_IOCTL_READDIR_BOTH
135 #undef EXT2_IOC_GETFLAGS
136 #undef EXT4_CASEFOLD_FL
140 /* We want the real kernel dirent structure, not the libc one */
145 unsigned short d_reclen
;
149 /* Define the VFAT ioctl to get both short and long file names */
150 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
152 /* Define the ext2 ioctl for handling extra attributes */
153 #define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
155 /* Case-insensitivity attribute */
156 #define EXT4_CASEFOLD_FL 0x40000000
159 # define O_DIRECTORY 0200000 /* must be directory */
162 #ifndef AT_NO_AUTOMOUNT
163 #define AT_NO_AUTOMOUNT 0x800
168 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
170 #define INVALID_NT_CHARS '*','?','<','>','|','"'
171 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
173 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
175 #define MAX_IGNORED_FILES 4
177 #ifndef XATTR_USER_PREFIX
178 # define XATTR_USER_PREFIX "user."
180 #ifndef XATTR_USER_PREFIX_LEN
181 # define XATTR_USER_PREFIX_LEN (sizeof(XATTR_USER_PREFIX) - 1)
184 #define SAMBA_XATTR_DOS_ATTRIB XATTR_USER_PREFIX "DOSATTRIB"
185 #define XATTR_ATTRIBS_MASK (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)
193 static struct file_identity ignored_files
[MAX_IGNORED_FILES
];
194 static unsigned int ignored_files_count
;
196 union file_directory_info
199 FILE_DIRECTORY_INFORMATION dir
;
200 FILE_BOTH_DIRECTORY_INFORMATION both
;
201 FILE_FULL_DIRECTORY_INFORMATION full
;
202 FILE_ID_BOTH_DIRECTORY_INFORMATION id_both
;
203 FILE_ID_FULL_DIRECTORY_INFORMATION id_full
;
204 FILE_ID_GLOBAL_TX_DIR_INFORMATION id_tx
;
205 FILE_NAMES_INFORMATION names
;
208 struct dir_data_buffer
210 struct dir_data_buffer
*next
; /* next buffer in the list */
211 unsigned int size
; /* total size of the buffer */
212 unsigned int pos
; /* current position in the buffer */
216 struct dir_data_names
218 const WCHAR
*long_name
; /* long file name in Unicode */
219 const WCHAR
*short_name
; /* short file name in Unicode */
220 const char *unix_name
; /* Unix file name in host encoding */
225 unsigned int size
; /* size of the names array */
226 unsigned int count
; /* count of used entries in the names array */
227 unsigned int pos
; /* current reading position in the names array */
228 struct file_identity id
; /* directory file identity */
229 struct dir_data_names
*names
; /* directory file names */
230 struct dir_data_buffer
*buffer
; /* head of data buffers list */
233 static const unsigned int dir_data_buffer_initial_size
= 4096;
234 static const unsigned int dir_data_cache_initial_size
= 256;
235 static const unsigned int dir_data_names_initial_size
= 64;
237 static struct dir_data
**dir_data_cache
;
238 static unsigned int dir_data_cache_size
;
240 static BOOL show_dot_files
;
241 static mode_t start_umask
;
243 /* at some point we may want to allow Winelib apps to set this */
244 static const BOOL is_case_sensitive
= FALSE
;
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 BOOL
is_wildcard( WCHAR c
)
325 return c
== '*' || c
== '?' || c
== '>' || c
== '<' || c
== '\"';
328 static inline BOOL
has_wildcard( const UNICODE_STRING
*mask
)
332 if (!mask
) return TRUE
;
333 for (i
= 0; i
< mask
->Length
/ sizeof(WCHAR
); i
++)
334 if (is_wildcard( mask
->Buffer
[i
] )) return TRUE
;
339 NTSTATUS
errno_to_status( int err
)
341 TRACE( "errno = %d\n", err
);
344 case EAGAIN
: return STATUS_SHARING_VIOLATION
;
345 case EBADF
: return STATUS_INVALID_HANDLE
;
346 case EBUSY
: return STATUS_DEVICE_BUSY
;
347 case ENOSPC
: return STATUS_DISK_FULL
;
350 case EACCES
: return STATUS_ACCESS_DENIED
;
351 case ENOTDIR
: return STATUS_OBJECT_PATH_NOT_FOUND
;
352 case ENOENT
: return STATUS_OBJECT_NAME_NOT_FOUND
;
353 case EISDIR
: return STATUS_INVALID_DEVICE_REQUEST
;
355 case ENFILE
: return STATUS_TOO_MANY_OPENED_FILES
;
356 case EINVAL
: return STATUS_INVALID_PARAMETER
;
357 case ENOTEMPTY
: return STATUS_DIRECTORY_NOT_EMPTY
;
358 case EPIPE
: return STATUS_PIPE_DISCONNECTED
;
359 case EIO
: return STATUS_DEVICE_NOT_READY
;
361 case ENOMEDIUM
: return STATUS_NO_MEDIA_IN_DEVICE
;
363 case ENXIO
: return STATUS_NO_SUCH_DEVICE
;
365 case EOPNOTSUPP
:return STATUS_NOT_SUPPORTED
;
366 case ECONNRESET
:return STATUS_PIPE_DISCONNECTED
;
367 case EFAULT
: return STATUS_ACCESS_VIOLATION
;
368 case ESPIPE
: return STATUS_ILLEGAL_FUNCTION
;
369 case ELOOP
: return STATUS_REPARSE_POINT_NOT_RESOLVED
;
370 #ifdef ETIME /* Missing on FreeBSD */
371 case ETIME
: return STATUS_IO_TIMEOUT
;
373 case ENOEXEC
: /* ?? */
374 case EEXIST
: /* ?? */
376 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err
);
377 return STATUS_UNSUCCESSFUL
;
382 static int xattr_fremove( int filedes
, const char *name
)
384 #ifdef HAVE_SYS_XATTR_H
385 # ifdef XATTR_ADDITIONAL_OPTIONS
386 return fremovexattr( filedes
, name
, 0 );
388 return fremovexattr( filedes
, name
);
390 #elif defined(HAVE_SYS_EXTATTR_H)
391 return extattr_delete_fd( filedes
, EXTATTR_NAMESPACE_USER
, &name
[XATTR_USER_PREFIX_LEN
] );
399 static int xattr_fset( int filedes
, const char *name
, const void *value
, size_t size
)
401 #ifdef HAVE_SYS_XATTR_H
402 # ifdef XATTR_ADDITIONAL_OPTIONS
403 return fsetxattr( filedes
, name
, value
, size
, 0, 0 );
405 return fsetxattr( filedes
, name
, value
, size
, 0 );
407 #elif defined(HAVE_SYS_EXTATTR_H)
408 return extattr_set_fd( filedes
, EXTATTR_NAMESPACE_USER
, &name
[XATTR_USER_PREFIX_LEN
],
417 /* On macOS, getxattr() is significantly slower than listxattr()
418 * (even for files with no extended attributes).
421 static BOOL
xattr_exists( const char **path
, int *filedes
, const char *name
)
427 ret
= listxattr( *path
, xattrs
, sizeof(xattrs
), 0 );
429 ret
= flistxattr( *filedes
, xattrs
, sizeof(xattrs
), 0 );
431 return errno
== ERANGE
;
435 if (!strcmp( name
, &xattrs
[i
] ))
437 i
+= strlen(&xattrs
[i
]) + 1;
446 static int xattr_get( const char *path
, const char *name
, void *value
, size_t size
)
449 if (!xattr_exists( &path
, NULL
, name
))
453 #ifdef HAVE_SYS_XATTR_H
454 # ifdef XATTR_ADDITIONAL_OPTIONS
455 return getxattr( path
, name
, value
, size
, 0, 0 );
457 return getxattr( path
, name
, value
, size
);
459 #elif defined(HAVE_SYS_EXTATTR_H)
460 return extattr_get_file( path
, EXTATTR_NAMESPACE_USER
, &name
[XATTR_USER_PREFIX_LEN
],
469 static int xattr_fget( int filedes
, const char *name
, void *value
, size_t size
)
472 if (!xattr_exists( NULL
, &filedes
, name
))
476 #ifdef HAVE_SYS_XATTR_H
477 # ifdef XATTR_ADDITIONAL_OPTIONS
478 return fgetxattr( filedes
, name
, value
, size
, 0, 0 );
480 return fgetxattr( filedes
, name
, value
, size
);
482 #elif defined(HAVE_SYS_EXTATTR_H)
483 return extattr_get_fd( filedes
, EXTATTR_NAMESPACE_USER
, &name
[XATTR_USER_PREFIX_LEN
],
492 /* get space from the current directory data buffer, allocating a new one if necessary */
493 static void *get_dir_data_space( struct dir_data
*data
, unsigned int size
)
495 struct dir_data_buffer
*buffer
= data
->buffer
;
498 if (!buffer
|| size
> buffer
->size
- buffer
->pos
)
500 unsigned int new_size
= buffer
? buffer
->size
* 2 : dir_data_buffer_initial_size
;
501 if (new_size
< size
) new_size
= size
;
502 if (!(buffer
= malloc( offsetof( struct dir_data_buffer
, data
[new_size
] ) ))) return NULL
;
504 buffer
->size
= new_size
;
505 buffer
->next
= data
->buffer
;
506 data
->buffer
= buffer
;
508 ret
= buffer
->data
+ buffer
->pos
;
513 /* add a string to the directory data buffer */
514 static const char *add_dir_data_nameA( struct dir_data
*data
, const char *name
)
516 /* keep buffer data WCHAR-aligned */
517 char *ptr
= get_dir_data_space( data
, (strlen( name
) + sizeof(WCHAR
)) & ~(sizeof(WCHAR
) - 1) );
518 if (ptr
) strcpy( ptr
, name
);
522 /* add a Unicode string to the directory data buffer */
523 static const WCHAR
*add_dir_data_nameW( struct dir_data
*data
, const WCHAR
*name
)
525 WCHAR
*ptr
= get_dir_data_space( data
, (wcslen( name
) + 1) * sizeof(WCHAR
) );
526 if (ptr
) wcscpy( ptr
, name
);
530 /* add an entry to the directory names array */
531 static BOOL
add_dir_data_names( struct dir_data
*data
, const WCHAR
*long_name
,
532 const WCHAR
*short_name
, const char *unix_name
)
534 static const WCHAR empty
[1];
535 struct dir_data_names
*names
= data
->names
;
537 if (data
->count
>= data
->size
)
539 unsigned int new_size
= max( data
->size
* 2, dir_data_names_initial_size
);
541 if (!(names
= realloc( names
, new_size
* sizeof(*names
) ))) return FALSE
;
542 data
->size
= new_size
;
548 if (!(names
[data
->count
].short_name
= add_dir_data_nameW( data
, short_name
))) return FALSE
;
550 else names
[data
->count
].short_name
= empty
;
552 if (!(names
[data
->count
].long_name
= add_dir_data_nameW( data
, long_name
))) return FALSE
;
553 if (!(names
[data
->count
].unix_name
= add_dir_data_nameA( data
, unix_name
))) return FALSE
;
558 /* free the complete directory data structure */
559 static void free_dir_data( struct dir_data
*data
)
561 struct dir_data_buffer
*buffer
, *next
;
565 for (buffer
= data
->buffer
; buffer
; buffer
= next
)
575 /* support for a directory queue for filesystem searches */
583 static struct list dir_queue
= LIST_INIT( dir_queue
);
585 static NTSTATUS
add_dir_to_queue( const char *name
)
587 int len
= strlen( name
) + 1;
588 struct dir_name
*dir
= malloc( offsetof( struct dir_name
, name
[len
] ));
589 if (!dir
) return STATUS_NO_MEMORY
;
590 strcpy( dir
->name
, name
);
591 list_add_tail( &dir_queue
, &dir
->entry
);
592 return STATUS_SUCCESS
;
595 static NTSTATUS
next_dir_in_queue( char *name
)
597 struct list
*head
= list_head( &dir_queue
);
600 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
601 strcpy( name
, dir
->name
);
602 list_remove( &dir
->entry
);
604 return STATUS_SUCCESS
;
606 return STATUS_OBJECT_NAME_NOT_FOUND
;
609 static void flush_dir_queue(void)
613 while ((head
= list_head( &dir_queue
)))
615 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
616 list_remove( &dir
->entry
);
624 static char *unescape_field( char *str
)
628 for (in
= out
= str
; *in
; in
++, out
++)
638 else if (in
[1] == '0' && in
[2] == '4' && in
[3] == '0')
643 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '1')
648 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '2')
653 else if (in
[1] == '1' && in
[2] == '3' && in
[3] == '4')
665 static inline char *get_field( char **str
)
669 ret
= strsep( str
, " \t" );
670 if (*str
) *str
+= strspn( *str
, " \t" );
674 /************************************************************************
675 * getmntent_replacement
677 * getmntent replacement for Android.
679 * NB returned static buffer is not thread safe; protect with mnt_mutex.
681 static struct mntent
*getmntent_replacement( FILE *f
)
683 static struct mntent entry
;
684 static char buf
[4096];
689 if (!fgets( buf
, sizeof(buf
), f
)) return NULL
;
690 p
= strchr( buf
, '\n' );
692 else /* Partially unread line, move file ptr to end */
695 while (fgets( tmp
, sizeof(tmp
), f
))
696 if (strchr( tmp
, '\n' )) break;
698 start
= buf
+ strspn( buf
, " \t" );
699 } while (start
[0] == '\0' || start
[0] == '#');
701 p
= get_field( &start
);
702 entry
.mnt_fsname
= p
? unescape_field( p
) : (char *)"";
704 p
= get_field( &start
);
705 entry
.mnt_dir
= p
? unescape_field( p
) : (char *)"";
707 p
= get_field( &start
);
708 entry
.mnt_type
= p
? unescape_field( p
) : (char *)"";
710 p
= get_field( &start
);
711 entry
.mnt_opts
= p
? unescape_field( p
) : (char *)"";
713 p
= get_field( &start
);
714 entry
.mnt_freq
= p
? atoi(p
) : 0;
716 p
= get_field( &start
);
717 entry
.mnt_passno
= p
? atoi(p
) : 0;
721 #define getmntent getmntent_replacement
724 /***********************************************************************
725 * parse_mount_entries
727 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
731 #include <sys/vfstab.h>
732 static char *parse_vfstab_entries( FILE *f
, dev_t dev
, ino_t ino
)
738 while (! getvfsent( f
, &entry
))
740 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
741 if (!strcmp( entry
.vfs_fstype
, "nfs" ) ||
742 !strcmp( entry
.vfs_fstype
, "smbfs" ) ||
743 !strcmp( entry
.vfs_fstype
, "ncpfs" )) continue;
745 if (stat( entry
.vfs_mountp
, &st
) == -1) continue;
746 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
747 if (!strcmp( entry
.vfs_fstype
, "fd" ))
749 if ((device
= strstr( entry
.vfs_mntopts
, "dev=" )))
751 char *p
= strchr( device
+ 4, ',' );
757 return entry
.vfs_special
;
764 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
766 struct mntent
*entry
;
770 while ((entry
= getmntent( f
)))
772 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
773 if (!strcmp( entry
->mnt_type
, "nfs" ) ||
774 !strcmp( entry
->mnt_type
, "cifs" ) ||
775 !strcmp( entry
->mnt_type
, "smbfs" ) ||
776 !strcmp( entry
->mnt_type
, "ncpfs" )) continue;
778 if (stat( entry
->mnt_dir
, &st
) == -1) continue;
779 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
780 if (!strcmp( entry
->mnt_type
, "supermount" ))
782 if ((device
= strstr( entry
->mnt_opts
, "dev=" )))
784 char *p
= strchr( device
+ 4, ',' );
789 else if (!stat( entry
->mnt_fsname
, &st
) && S_ISREG(st
.st_mode
))
791 /* if device is a regular file check for a loop mount */
792 if ((device
= strstr( entry
->mnt_opts
, "loop=" )))
794 char *p
= strchr( device
+ 5, ',' );
800 return entry
->mnt_fsname
;
806 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
808 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
813 while ((entry
= getfsent()))
815 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
816 if (!strcmp( entry
->fs_vfstype
, "nfs" ) ||
817 !strcmp( entry
->fs_vfstype
, "smbfs" ) ||
818 !strcmp( entry
->fs_vfstype
, "ncpfs" )) continue;
820 if (stat( entry
->fs_file
, &st
) == -1) continue;
821 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
822 return entry
->fs_spec
;
829 #include <sys/mnttab.h>
830 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
837 while (( ! getmntent( f
, &entry
) ))
839 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
840 if (!strcmp( entry
.mnt_fstype
, "nfs" ) ||
841 !strcmp( entry
.mnt_fstype
, "smbfs" ) ||
842 !strcmp( entry
.mnt_fstype
, "ncpfs" )) continue;
844 if (stat( entry
.mnt_mountp
, &st
) == -1) continue;
845 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
846 if (!strcmp( entry
.mnt_fstype
, "fd" ))
848 if ((device
= strstr( entry
.mnt_mntopts
, "dev=" )))
850 char *p
= strchr( device
+ 4, ',' );
856 return entry
.mnt_special
;
862 /***********************************************************************
863 * get_default_drive_device
865 * Return the default device to use for a given drive mount point.
867 static char *get_default_drive_device( const char *root
)
877 /* try to open it first to force it to get mounted */
878 if ((fd
= open( root
, O_RDONLY
| O_DIRECTORY
)) != -1)
880 res
= fstat( fd
, &st
);
883 /* now try normal stat just in case */
884 if (res
== -1) res
= stat( root
, &st
);
885 if (res
== -1) return NULL
;
887 mutex_lock( &mnt_mutex
);
890 if ((f
= fopen( "/proc/mounts", "r" )))
892 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
896 if ((f
= fopen( "/etc/mtab", "r" )))
898 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
901 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
902 if (!device
&& (f
= fopen( "/etc/fstab", "r" )))
904 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
908 if (device
) ret
= strdup( device
);
909 mutex_unlock( &mnt_mutex
);
911 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
916 /* try to open it first to force it to get mounted */
917 if ((fd
= open( root
, O_RDONLY
)) != -1)
919 res
= fstat( fd
, &st
);
922 /* now try normal stat just in case */
923 if (res
== -1) res
= stat( root
, &st
);
924 if (res
== -1) return NULL
;
926 mutex_lock( &mnt_mutex
);
928 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
929 * pass NULL. Leave the argument in for symmetry.
931 device
= parse_mount_entries( NULL
, st
.st_dev
, st
.st_ino
);
932 if (device
) ret
= strdup( device
);
933 mutex_unlock( &mnt_mutex
);
941 /* try to open it first to force it to get mounted */
942 if ((fd
= open( root
, O_RDONLY
)) != -1)
944 res
= fstat( fd
, &st
);
947 /* now try normal stat just in case */
948 if (res
== -1) res
= stat( root
, &st
);
949 if (res
== -1) return NULL
;
951 mutex_lock( &mnt_mutex
);
953 if ((f
= fopen( "/etc/mnttab", "r" )))
955 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
958 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
959 if (!device
&& (f
= fopen( "/etc/vfstab", "r" )))
961 device
= parse_vfstab_entries( f
, st
.st_dev
, st
.st_ino
);
964 if (device
) ret
= strdup( device
);
965 mutex_unlock( &mnt_mutex
);
967 #elif defined(__APPLE__)
968 struct statfs
*mntStat
;
974 static const char path_bsd_device
[] = "/dev/disk";
977 res
= stat( root
, &st
);
978 if (res
== -1) return NULL
;
983 mutex_lock( &mnt_mutex
);
985 mntSize
= getmntinfo(&mntStat
, MNT_NOWAIT
);
987 for (i
= 0; i
< mntSize
&& !ret
; i
++)
989 if (stat(mntStat
[i
].f_mntonname
, &st
) == -1) continue;
990 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
992 /* FIXME add support for mounted network drive */
993 if ( strncmp(mntStat
[i
].f_mntfromname
, path_bsd_device
, strlen(path_bsd_device
)) == 0)
995 /* set return value to the corresponding raw BSD node */
996 ret
= malloc( strlen(mntStat
[i
].f_mntfromname
) + 2 /* 2 : r and \0 */ );
999 strcpy(ret
, "/dev/r");
1000 strcat(ret
, mntStat
[i
].f_mntfromname
+sizeof("/dev/")-1);
1004 mutex_unlock( &mnt_mutex
);
1007 if (!warned
++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
1013 /***********************************************************************
1014 * get_device_mount_point
1016 * Return the current mount point for a device.
1018 static char *get_device_mount_point( dev_t dev
)
1025 mutex_lock( &mnt_mutex
);
1028 if ((f
= fopen( "/proc/mounts", "r" )))
1030 if ((f
= fopen( "/etc/mtab", "r" )))
1033 struct mntent
*entry
;
1037 while ((entry
= getmntent( f
)))
1039 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
1040 if (!strcmp( entry
->mnt_type
, "nfs" ) ||
1041 !strcmp( entry
->mnt_type
, "cifs" ) ||
1042 !strcmp( entry
->mnt_type
, "smbfs" ) ||
1043 !strcmp( entry
->mnt_type
, "ncpfs" )) continue;
1045 if (!strcmp( entry
->mnt_type
, "supermount" ))
1047 if ((device
= strstr( entry
->mnt_opts
, "dev=" )))
1050 if ((p
= strchr( device
, ',' ))) *p
= 0;
1053 else if (!stat( entry
->mnt_fsname
, &st
) && S_ISREG(st
.st_mode
))
1055 /* if device is a regular file check for a loop mount */
1056 if ((device
= strstr( entry
->mnt_opts
, "loop=" )))
1059 if ((p
= strchr( device
, ',' ))) *p
= 0;
1062 else device
= entry
->mnt_fsname
;
1064 if (device
&& !stat( device
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== dev
)
1066 ret
= strdup( entry
->mnt_dir
);
1072 mutex_unlock( &mnt_mutex
);
1073 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1074 struct statfs
*entry
;
1078 mutex_lock( &mnt_mutex
);
1080 size
= getmntinfo( &entry
, MNT_NOWAIT
);
1081 for (i
= 0; i
< size
; i
++)
1083 if (stat( entry
[i
].f_mntfromname
, &st
) == -1) continue;
1084 if (S_ISBLK(st
.st_mode
) && st
.st_rdev
== dev
)
1086 ret
= strdup( entry
[i
].f_mntonname
);
1090 mutex_unlock( &mnt_mutex
);
1093 if (!warned
++) FIXME( "unmounting devices not supported on this platform\n" );
1099 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1100 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1113 BOOLEAN case_sensitive
;
1119 vol_capabilities_attr_t caps
;
1122 /***********************************************************************
1125 * Checks if the specified file system is in the cache.
1127 static struct fs_cache
*look_up_fs_cache( dev_t dev
)
1130 for (i
= 0; i
< ARRAY_SIZE( fs_cache
); i
++)
1131 if (fs_cache
[i
].dev
== dev
)
1136 /***********************************************************************
1139 * Adds the specified file system to the cache.
1141 static void add_fs_cache( dev_t dev
, fsid_t fsid
, BOOLEAN case_sensitive
)
1144 struct fs_cache
*entry
= look_up_fs_cache( dev
);
1145 static int once
= 0;
1148 /* Update the cache */
1150 entry
->case_sensitive
= case_sensitive
;
1154 /* Add a new entry */
1155 for (i
= 0; i
< ARRAY_SIZE( fs_cache
); i
++)
1156 if (fs_cache
[i
].dev
== 0)
1158 /* This entry is empty, use it */
1159 fs_cache
[i
].dev
= dev
;
1160 fs_cache
[i
].fsid
= fsid
;
1161 fs_cache
[i
].case_sensitive
= case_sensitive
;
1165 /* Cache is out of space, warn */
1167 WARN( "FS cache is out of space, expect performance problems\n" );
1170 /***********************************************************************
1171 * get_dir_case_sensitivity_attr
1173 * Checks if the volume containing the specified directory is case
1174 * sensitive or not. Uses getattrlist(2).
1176 static int get_dir_case_sensitivity_attr( const char *dir
)
1179 struct attrlist attr
;
1180 struct vol_caps caps
;
1181 struct get_fsid get_fsid
;
1182 struct fs_cache
*entry
;
1184 /* First get the FS ID of the volume */
1185 attr
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1187 attr
.commonattr
= ATTR_CMN_DEVID
|ATTR_CMN_FSID
;
1188 attr
.volattr
= attr
.dirattr
= attr
.fileattr
= attr
.forkattr
= 0;
1190 if (getattrlist( dir
, &attr
, &get_fsid
, sizeof(get_fsid
), 0 ) != 0 ||
1191 get_fsid
.size
!= sizeof(get_fsid
))
1193 /* Try to look it up in the cache */
1194 entry
= look_up_fs_cache( get_fsid
.dev
);
1195 if (entry
&& !memcmp( &entry
->fsid
, &get_fsid
.fsid
, sizeof(fsid_t
) ))
1196 /* Cache lookup succeeded */
1197 return entry
->case_sensitive
;
1198 /* Cache is stale at this point, we have to update it */
1200 mntpoint
= get_device_mount_point( get_fsid
.dev
);
1201 /* Now look up the case-sensitivity */
1202 attr
.commonattr
= 0;
1203 attr
.volattr
= ATTR_VOL_INFO
|ATTR_VOL_CAPABILITIES
;
1204 if (getattrlist( mntpoint
, &attr
, &caps
, sizeof(caps
), 0 ) < 0)
1207 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, TRUE
);
1211 if (caps
.size
== sizeof(caps
) &&
1212 (caps
.caps
.valid
[VOL_CAPABILITIES_FORMAT
] &
1213 (VOL_CAP_FMT_CASE_SENSITIVE
| VOL_CAP_FMT_CASE_PRESERVING
)) ==
1214 (VOL_CAP_FMT_CASE_SENSITIVE
| VOL_CAP_FMT_CASE_PRESERVING
))
1218 if ((caps
.caps
.capabilities
[VOL_CAPABILITIES_FORMAT
] &
1219 VOL_CAP_FMT_CASE_SENSITIVE
) != VOL_CAP_FMT_CASE_SENSITIVE
)
1223 /* Update the cache */
1224 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, ret
);
1231 /***********************************************************************
1232 * get_dir_case_sensitivity_stat
1234 * Checks if the volume containing the specified directory is case
1235 * sensitive or not. Uses (f)statfs(2), statvfs(2), fstatat(2), or ioctl(2).
1237 static BOOLEAN
get_dir_case_sensitivity_stat( const char *dir
)
1239 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1242 if (statfs( dir
, &stfs
) == -1) return TRUE
;
1243 /* Assume these file systems are always case insensitive.*/
1244 if (!strcmp( stfs
.f_fstypename
, "fusefs" ) &&
1245 !strncmp( stfs
.f_mntfromname
, "ciopfs", 5 ))
1247 /* msdosfs was case-insensitive since FreeBSD 8, if not earlier */
1248 if (!strcmp( stfs
.f_fstypename
, "msdosfs" ) ||
1249 /* older CIFS protocol versions uppercase filename on the client,
1250 * newer versions should be case-insensitive on the server anyway */
1251 !strcmp( stfs
.f_fstypename
, "smbfs" ))
1253 /* no ntfs-3g: modern fusefs has no way to report the filesystem on FreeBSD
1254 * no cd9660 or udf, they're case-sensitive on FreeBSD
1257 if (!strcmp( stfs
.f_fstypename
, "msdos" ) ||
1258 !strcmp( stfs
.f_fstypename
, "cd9660" ) ||
1259 !strcmp( stfs
.f_fstypename
, "udf" ) ||
1260 !strcmp( stfs
.f_fstypename
, "ntfs" ))
1262 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1263 if (!strcmp( stfs
.f_fstypename
, "hfs" ) && (stfs
.f_fssubtype
== 0 ||
1264 stfs
.f_fssubtype
== 1 ||
1265 stfs
.f_fssubtype
== 128))
1268 /* The field says "reserved", but a quick look at the kernel source
1269 * tells us that this "reserved" field is really the same as the
1270 * "fssubtype" field from the inode64 structure (see munge_statfs()
1271 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1273 if (!strcmp( stfs
.f_fstypename
, "hfs" ) && (stfs
.f_reserved1
== 0 ||
1274 stfs
.f_reserved1
== 1 ||
1275 stfs
.f_reserved1
== 128))
1281 #elif defined(__NetBSD__)
1282 struct statvfs stfs
;
1284 if (statvfs( dir
, &stfs
) == -1) return TRUE
;
1285 /* Only assume CIOPFS is case insensitive. */
1286 if (strcmp( stfs
.f_fstypename
, "fusefs" ) ||
1287 strncmp( stfs
.f_mntfromname
, "ciopfs", 5 ))
1291 #elif defined(__linux__)
1292 BOOLEAN sens
= TRUE
;
1297 if ((fd
= open( dir
, O_RDONLY
| O_NONBLOCK
)) == -1)
1300 if (ioctl( fd
, EXT2_IOC_GETFLAGS
, &flags
) != -1 && (flags
& EXT4_CASEFOLD_FL
))
1304 else if (fstatfs( fd
, &stfs
) == 0 && /* CIOPFS is case insensitive. Instead of */
1305 stfs
.f_type
== 0x65735546 /* FUSE_SUPER_MAGIC */ && /* parsing mtab to discover if the FUSE FS */
1306 fstatat( fd
, ".ciopfs", &st
, AT_NO_AUTOMOUNT
) == 0) /* is CIOPFS, look for .ciopfs in the dir. */
1319 /***********************************************************************
1320 * get_dir_case_sensitivity
1322 * Checks if the volume containing the specified directory is case
1323 * sensitive or not. Uses multiple methods, depending on platform.
1325 static BOOLEAN
get_dir_case_sensitivity( const char *dir
)
1327 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1328 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1329 int case_sensitive
= get_dir_case_sensitivity_attr( dir
);
1330 if (case_sensitive
!= -1) return case_sensitive
;
1332 return get_dir_case_sensitivity_stat( dir
);
1336 /***********************************************************************
1339 * Check if the specified file should be hidden based on its unix path and the show dot files option.
1341 static BOOL
is_hidden_file( const char *name
)
1345 if (show_dot_files
) return FALSE
;
1347 p
= name
+ strlen( name
);
1348 while (p
> name
&& p
[-1] == '/') p
--;
1349 while (p
> name
&& p
[-1] != '/') p
--;
1350 if (*p
++ != '.') return FALSE
;
1351 if (!*p
|| *p
== '/') return FALSE
; /* "." directory */
1352 if (*p
++ != '.') return TRUE
;
1353 if (!*p
|| *p
== '/') return FALSE
; /* ".." directory */
1358 /***********************************************************************
1359 * hash_short_file_name
1361 * Transform a Unix file name into a hashed DOS name. If the name is not a valid
1362 * DOS name, it is replaced by a hashed version that fits in 8.3 format.
1363 * 'buffer' must be at least 12 characters long.
1364 * Returns length of short name in bytes; short name is NOT null-terminated.
1366 static ULONG
hash_short_file_name( const WCHAR
*name
, int length
, LPWSTR buffer
)
1368 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1370 LPCWSTR p
, ext
, end
= name
+ length
;
1372 unsigned short hash
;
1375 /* Compute the hash code of the file name */
1376 /* If you know something about hash functions, feel free to */
1377 /* insert a better algorithm here... */
1378 if (!is_case_sensitive
)
1380 for (p
= name
, hash
= 0xbeef; p
< end
- 1; p
++)
1381 hash
= (hash
<<3) ^ (hash
>>5) ^ towlower(*p
) ^ (towlower(p
[1]) << 8);
1382 hash
= (hash
<<3) ^ (hash
>>5) ^ towlower(*p
); /* Last character */
1386 for (p
= name
, hash
= 0xbeef; p
< end
- 1; p
++)
1387 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
1388 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
1391 /* Find last dot for start of the extension */
1393 while (*p
== '.') ++p
;
1394 for (p
= p
+ 1, ext
= NULL
; p
< end
- 1; p
++) if (*p
== '.') ext
= p
;
1396 /* Copy first 4 chars, replacing invalid chars with '_' */
1397 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; p
++)
1399 if (p
== end
|| p
== ext
) break;
1400 if (*p
== '.') continue;
1401 *dst
++ = is_invalid_dos_char(*p
) ? '_' : *p
;
1404 /* Pad to 5 chars with '~' */
1405 while (i
-- >= 0) *dst
++ = '~';
1407 /* Insert hash code converted to 3 ASCII chars */
1408 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
1409 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
1410 *dst
++ = hash_chars
[hash
& 0x1f];
1412 /* Copy the first 3 chars of the extension (if any) */
1416 for (i
= 3, ext
++; (i
> 0) && ext
< end
; i
--, ext
++)
1417 *dst
++ = is_invalid_dos_char(*ext
) ? '_' : *ext
;
1419 return dst
- buffer
;
1423 /***********************************************************************
1424 * match_filename_part
1426 * Recursive helper for match_filename().
1429 static BOOLEAN
match_filename_part( const WCHAR
*name
, const WCHAR
*name_end
, const WCHAR
*mask
, const WCHAR
*mask_end
)
1433 while (name
< name_end
&& mask
< mask_end
)
1439 while (mask
< mask_end
&& *mask
== '*') mask
++; /* Skip consecutive '*' */
1440 if (mask
== mask_end
) return TRUE
; /* end of mask is all '*', so match */
1442 while (name
< name_end
)
1444 c
= *mask
== '"' ? '.' : *mask
;
1445 if (!is_wildcard(c
))
1447 if (is_case_sensitive
)
1448 while (name
< name_end
&& (*name
!= c
)) name
++;
1450 while (name
< name_end
&& (towupper(*name
) != towupper(c
))) name
++;
1452 if (match_filename_part( name
, name_end
, mask
, mask_end
)) return TRUE
;
1458 const WCHAR
*next_dot
;
1459 BOOL had_dot
= FALSE
;
1462 while (name
< name_end
)
1465 while (next_dot
< name_end
&& *next_dot
!= '.') ++next_dot
;
1466 if (next_dot
== name_end
&& had_dot
) break;
1467 if (next_dot
< name_end
)
1472 if (mask
< mask_end
)
1474 while (name
< next_dot
)
1476 c
= *mask
== '"' ? '.' : *mask
;
1477 if (!is_wildcard(c
))
1479 if (is_case_sensitive
)
1480 while (name
< next_dot
&& (*name
!= c
)) name
++;
1482 while (name
< next_dot
&& (towupper(*name
) != towupper(c
))) name
++;
1484 if (match_filename_part( name
, name_end
, mask
, mask_end
)) return TRUE
;
1500 while (mask
< mask_end
&& *mask
== '>') mask
++;
1501 if (mask
== mask_end
) name
++;
1506 c
= *mask
== '"' ? '.' : *mask
;
1507 if (is_case_sensitive
&& c
!= *name
) return FALSE
;
1508 if (!is_case_sensitive
&& towupper(c
) != towupper(*name
)) return FALSE
;
1514 while (mask
< mask_end
&& (*mask
== '*' || *mask
== '<' || *mask
== '"' || *mask
== '>'))
1516 return (name
== name_end
&& mask
== mask_end
);
1520 /***********************************************************************
1523 * Check a file name against a mask.
1526 static BOOLEAN
match_filename( const WCHAR
*name
, int length
, const UNICODE_STRING
*mask_str
)
1528 /* Special handling for parent directory. */
1529 if (length
== 2 && name
[0] == '.' && name
[1] == '.') --length
;
1531 return match_filename_part( name
, name
+ length
, mask_str
->Buffer
,
1532 mask_str
->Buffer
+ mask_str
->Length
/ sizeof(WCHAR
));
1536 /***********************************************************************
1537 * is_legal_8dot3_name
1539 * Simplified version of RtlIsNameLegalDOS8Dot3.
1541 static BOOLEAN
is_legal_8dot3_name( const WCHAR
*name
, int len
)
1543 static const WCHAR invalid_chars
[] = { INVALID_DOS_CHARS
,':','/','\\',0 };
1546 if (len
> 12) return FALSE
;
1548 /* a starting . is invalid, except for . and .. */
1549 if (len
> 0 && name
[0] == '.') return (len
== 1 || (len
== 2 && name
[1] == '.'));
1551 for (i
= 0; i
< len
; i
++)
1553 if (name
[i
] > 0x7f) return FALSE
;
1554 if (wcschr( invalid_chars
, name
[i
] )) return FALSE
;
1557 if (dot
!= -1) return FALSE
;
1562 if (dot
== -1) return (len
<= 8);
1563 if (dot
> 8) return FALSE
;
1564 return (len
- dot
> 1 && len
- dot
< 5);
1568 /***********************************************************************
1571 * Add a file to the directory data if it matches the mask.
1573 static BOOL
append_entry( struct dir_data
*data
, const char *long_name
,
1574 const char *short_name
, const UNICODE_STRING
*mask
)
1576 int long_len
, short_len
;
1577 WCHAR long_nameW
[MAX_DIR_ENTRY_LEN
+ 1];
1578 WCHAR short_nameW
[13];
1580 long_len
= ntdll_umbstowcs( long_name
, strlen(long_name
), long_nameW
, ARRAY_SIZE(long_nameW
) );
1581 if (long_len
== ARRAY_SIZE(long_nameW
)) return TRUE
;
1582 long_nameW
[long_len
] = 0;
1586 short_len
= ntdll_umbstowcs( short_name
, strlen(short_name
),
1587 short_nameW
, ARRAY_SIZE( short_nameW
) - 1 );
1589 else /* generate a short name if necessary */
1592 if (!is_legal_8dot3_name( long_nameW
, long_len
))
1593 short_len
= hash_short_file_name( long_nameW
, long_len
, short_nameW
);
1595 short_nameW
[short_len
] = 0;
1596 wcsupr( short_nameW
);
1598 TRACE( "long %s short %s mask %s\n",
1599 debugstr_w( long_nameW
), debugstr_w( short_nameW
), debugstr_us( mask
));
1601 if (mask
&& !match_filename( long_nameW
, long_len
, mask
))
1603 if (!short_len
) return TRUE
; /* no short name to match */
1604 if (!match_filename( short_nameW
, short_len
, mask
)) return TRUE
;
1607 return add_dir_data_names( data
, long_nameW
, short_nameW
, long_name
);
1611 /* fetch the attributes of a file */
1612 static inline ULONG
get_file_attributes( const struct stat
*st
)
1616 if (S_ISDIR(st
->st_mode
))
1617 attr
= FILE_ATTRIBUTE_DIRECTORY
;
1619 attr
= FILE_ATTRIBUTE_ARCHIVE
;
1620 if (!(st
->st_mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
)))
1621 attr
|= FILE_ATTRIBUTE_READONLY
;
1626 /* decode the xattr-stored DOS attributes */
1627 static int parse_samba_dos_attrib_data( char *data
, int len
)
1632 if (len
> 2 && data
[0] == '0' && data
[1] == 'x')
1635 val
= strtol( data
, &end
, 16 );
1636 if (!*end
) return val
& XATTR_ATTRIBS_MASK
;
1641 if (!once
++) FIXME( "Unhandled " SAMBA_XATTR_DOS_ATTRIB
" extended attribute value.\n" );
1647 static BOOL
fd_is_mount_point( int fd
, const struct stat
*st
)
1650 return S_ISDIR( st
->st_mode
) && !fstatat( fd
, "..", &parent
, 0 )
1651 && (parent
.st_dev
!= st
->st_dev
|| parent
.st_ino
== st
->st_ino
);
1655 /* get the stat info and file attributes for a file (by file descriptor) */
1656 static int fd_get_file_info( int fd
, unsigned int options
, struct stat
*st
, ULONG
*attr
)
1662 ret
= fstat( fd
, st
);
1663 if (ret
== -1) return ret
;
1664 *attr
|= get_file_attributes( st
);
1665 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1666 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, st
))
1667 *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1669 attr_len
= xattr_fget( fd
, SAMBA_XATTR_DOS_ATTRIB
, attr_data
, sizeof(attr_data
)-1 );
1671 *attr
|= parse_samba_dos_attrib_data( attr_data
, attr_len
);
1674 if (errno
== ENOTSUP
) return ret
;
1676 if (errno
== ENODATA
) return ret
;
1679 if (errno
== ENOATTR
) return ret
;
1681 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB
". errno %d (%s)\n",
1682 errno
, strerror( errno
) );
1688 static int fd_set_dos_attrib( int fd
, UINT attr
, BOOL force_set
)
1690 /* we only store the HIDDEN and SYSTEM attributes */
1691 attr
&= XATTR_ATTRIBS_MASK
;
1692 if (force_set
|| attr
!= 0)
1694 /* encode the attributes in Samba 3 ASCII format. Samba 4 has extended
1695 * this format with more features, but retains compatibility with the
1696 * earlier format. */
1698 int len
= snprintf( data
, sizeof(data
), "0x%x", attr
);
1699 return xattr_fset( fd
, SAMBA_XATTR_DOS_ATTRIB
, data
, len
);
1701 else return xattr_fremove( fd
, SAMBA_XATTR_DOS_ATTRIB
);
1705 /* set the stat info and file attributes for a file (by file descriptor) */
1706 static NTSTATUS
fd_set_file_info( int fd
, UINT attr
, BOOL force_set_xattr
)
1710 if (fstat( fd
, &st
) == -1) return errno_to_status( errno
);
1711 if (attr
& FILE_ATTRIBUTE_READONLY
)
1713 if (S_ISDIR( st
.st_mode
))
1714 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
1716 st
.st_mode
&= ~0222; /* clear write permission bits */
1720 /* add write permission only where we already have read permission */
1721 st
.st_mode
|= (0600 | ((st
.st_mode
& 044) >> 1)) & (~start_umask
);
1723 if (fchmod( fd
, st
.st_mode
) == -1) return errno_to_status( errno
);
1725 /* if the file has multiple names, we can't be sure that it is safe to not
1726 set the extended attribute, since any of the names could start with a dot */
1727 force_set_xattr
= force_set_xattr
|| st
.st_nlink
> 1;
1729 if (fd_set_dos_attrib( fd
, attr
, force_set_xattr
) == -1 && errno
!= ENOTSUP
)
1730 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB
". errno %d (%s)\n",
1731 errno
, strerror( errno
) );
1733 return STATUS_SUCCESS
;
1737 /* get the stat info and file attributes for a file (by name) */
1738 static int get_file_info( const char *path
, struct stat
*st
, ULONG
*attr
)
1745 ret
= lstat( path
, st
);
1746 if (ret
== -1) return ret
;
1747 if (S_ISLNK( st
->st_mode
))
1749 ret
= stat( path
, st
);
1750 if (ret
== -1) return ret
;
1751 /* is a symbolic link and a directory, consider these "reparse points" */
1752 if (S_ISDIR( st
->st_mode
)) *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1754 else if (S_ISDIR( st
->st_mode
) && (parent_path
= malloc( strlen(path
) + 4 )))
1756 struct stat parent_st
;
1758 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1759 strcpy( parent_path
, path
);
1760 strcat( parent_path
, "/.." );
1761 if (!stat( parent_path
, &parent_st
)
1762 && (st
->st_dev
!= parent_st
.st_dev
|| st
->st_ino
== parent_st
.st_ino
))
1763 *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1765 free( parent_path
);
1767 *attr
|= get_file_attributes( st
);
1769 attr_len
= xattr_get( path
, SAMBA_XATTR_DOS_ATTRIB
, attr_data
, sizeof(attr_data
)-1 );
1771 *attr
|= parse_samba_dos_attrib_data( attr_data
, attr_len
);
1774 if (is_hidden_file( path
))
1775 *attr
|= FILE_ATTRIBUTE_HIDDEN
;
1776 if (errno
== ENOTSUP
) return ret
;
1778 if (errno
== ENODATA
) return ret
;
1781 if (errno
== ENOATTR
) return ret
;
1783 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB
" from \"%s\". errno %d (%s)\n",
1784 path
, errno
, strerror( errno
) );
1790 #if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
1791 static int futimens( int fd
, const struct timespec spec
[2] )
1793 return syscall( __NR_utimensat
, fd
, NULL
, spec
, 0 );
1795 #define HAVE_FUTIMENS
1796 #endif /* __ANDROID__ */
1799 #define UTIME_OMIT ((1 << 30) - 2)
1802 static BOOL
set_file_times_precise( int fd
, const LARGE_INTEGER
*mtime
,
1803 const LARGE_INTEGER
*atime
, NTSTATUS
*status
)
1805 #ifdef HAVE_FUTIMENS
1806 struct timespec tv
[2];
1808 tv
[0].tv_sec
= tv
[1].tv_sec
= 0;
1809 tv
[0].tv_nsec
= tv
[1].tv_nsec
= UTIME_OMIT
;
1810 if (atime
->QuadPart
)
1812 tv
[0].tv_sec
= atime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1813 tv
[0].tv_nsec
= (atime
->QuadPart
% 10000000) * 100;
1815 if (mtime
->QuadPart
)
1817 tv
[1].tv_sec
= mtime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1818 tv
[1].tv_nsec
= (mtime
->QuadPart
% 10000000) * 100;
1821 if (!&futimens
) return FALSE
;
1823 if (futimens( fd
, tv
) == -1) *status
= errno_to_status( errno
);
1824 else *status
= STATUS_SUCCESS
;
1832 static NTSTATUS
set_file_times( int fd
, const LARGE_INTEGER
*mtime
, const LARGE_INTEGER
*atime
)
1834 NTSTATUS status
= STATUS_SUCCESS
;
1835 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1836 struct timeval tv
[2];
1840 if (set_file_times_precise( fd
, mtime
, atime
, &status
))
1843 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1844 if (!atime
->QuadPart
|| !mtime
->QuadPart
)
1847 tv
[0].tv_sec
= tv
[0].tv_usec
= 0;
1848 tv
[1].tv_sec
= tv
[1].tv_usec
= 0;
1849 if (!fstat( fd
, &st
))
1851 tv
[0].tv_sec
= st
.st_atime
;
1852 tv
[1].tv_sec
= st
.st_mtime
;
1853 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1854 tv
[0].tv_usec
= st
.st_atim
.tv_nsec
/ 1000;
1855 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1856 tv
[0].tv_usec
= st
.st_atimespec
.tv_nsec
/ 1000;
1858 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1859 tv
[1].tv_usec
= st
.st_mtim
.tv_nsec
/ 1000;
1860 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1861 tv
[1].tv_usec
= st
.st_mtimespec
.tv_nsec
/ 1000;
1865 if (atime
->QuadPart
)
1867 tv
[0].tv_sec
= atime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1868 tv
[0].tv_usec
= (atime
->QuadPart
% 10000000) / 10;
1870 if (mtime
->QuadPart
)
1872 tv
[1].tv_sec
= mtime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1873 tv
[1].tv_usec
= (mtime
->QuadPart
% 10000000) / 10;
1876 if (futimes( fd
, tv
) == -1) status
= errno_to_status( errno
);
1877 #elif defined(HAVE_FUTIMESAT)
1878 if (futimesat( fd
, NULL
, tv
) == -1) status
= errno_to_status( errno
);
1881 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1882 FIXME( "setting file times not supported\n" );
1883 status
= STATUS_NOT_IMPLEMENTED
;
1889 static inline void get_file_times( const struct stat
*st
, LARGE_INTEGER
*mtime
, LARGE_INTEGER
*ctime
,
1890 LARGE_INTEGER
*atime
, LARGE_INTEGER
*creation
)
1892 mtime
->QuadPart
= ticks_from_time_t( st
->st_mtime
);
1893 ctime
->QuadPart
= ticks_from_time_t( st
->st_ctime
);
1894 atime
->QuadPart
= ticks_from_time_t( st
->st_atime
);
1895 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1896 mtime
->QuadPart
+= st
->st_mtim
.tv_nsec
/ 100;
1897 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1898 mtime
->QuadPart
+= st
->st_mtimespec
.tv_nsec
/ 100;
1900 #ifdef HAVE_STRUCT_STAT_ST_CTIM
1901 ctime
->QuadPart
+= st
->st_ctim
.tv_nsec
/ 100;
1902 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1903 ctime
->QuadPart
+= st
->st_ctimespec
.tv_nsec
/ 100;
1905 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1906 atime
->QuadPart
+= st
->st_atim
.tv_nsec
/ 100;
1907 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1908 atime
->QuadPart
+= st
->st_atimespec
.tv_nsec
/ 100;
1910 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1911 creation
->QuadPart
= ticks_from_time_t( st
->st_birthtime
);
1912 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
1913 creation
->QuadPart
+= st
->st_birthtim
.tv_nsec
/ 100;
1914 #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1915 creation
->QuadPart
+= st
->st_birthtimespec
.tv_nsec
/ 100;
1917 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
1918 creation
->QuadPart
= ticks_from_time_t( st
->__st_birthtime
);
1919 #ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
1920 creation
->QuadPart
+= st
->__st_birthtim
.tv_nsec
/ 100;
1928 /* fill in the file information that depends on the stat and attribute info */
1929 static NTSTATUS
fill_file_info( const struct stat
*st
, ULONG attr
, void *ptr
,
1930 FILE_INFORMATION_CLASS
class )
1934 case FileBasicInformation
:
1936 FILE_BASIC_INFORMATION
*info
= ptr
;
1938 get_file_times( st
, &info
->LastWriteTime
, &info
->ChangeTime
,
1939 &info
->LastAccessTime
, &info
->CreationTime
);
1940 info
->FileAttributes
= attr
;
1943 case FileStandardInformation
:
1945 FILE_STANDARD_INFORMATION
*info
= ptr
;
1947 if ((info
->Directory
= S_ISDIR(st
->st_mode
)))
1949 info
->AllocationSize
.QuadPart
= 0;
1950 info
->EndOfFile
.QuadPart
= 0;
1951 info
->NumberOfLinks
= 1;
1955 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1956 info
->EndOfFile
.QuadPart
= st
->st_size
;
1957 info
->NumberOfLinks
= st
->st_nlink
;
1961 case FileInternalInformation
:
1963 FILE_INTERNAL_INFORMATION
*info
= ptr
;
1964 info
->IndexNumber
.QuadPart
= st
->st_ino
;
1967 case FileEndOfFileInformation
:
1969 FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
1970 info
->EndOfFile
.QuadPart
= S_ISDIR(st
->st_mode
) ? 0 : st
->st_size
;
1973 case FileAllInformation
:
1975 FILE_ALL_INFORMATION
*info
= ptr
;
1976 fill_file_info( st
, attr
, &info
->BasicInformation
, FileBasicInformation
);
1977 fill_file_info( st
, attr
, &info
->StandardInformation
, FileStandardInformation
);
1978 fill_file_info( st
, attr
, &info
->InternalInformation
, FileInternalInformation
);
1981 /* all directory structures start with the FileDirectoryInformation layout */
1982 case FileBothDirectoryInformation
:
1983 case FileFullDirectoryInformation
:
1984 case FileDirectoryInformation
:
1986 FILE_DIRECTORY_INFORMATION
*info
= ptr
;
1988 get_file_times( st
, &info
->LastWriteTime
, &info
->ChangeTime
,
1989 &info
->LastAccessTime
, &info
->CreationTime
);
1990 if (S_ISDIR(st
->st_mode
))
1992 info
->AllocationSize
.QuadPart
= 0;
1993 info
->EndOfFile
.QuadPart
= 0;
1997 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1998 info
->EndOfFile
.QuadPart
= st
->st_size
;
2000 info
->FileAttributes
= attr
;
2003 case FileIdFullDirectoryInformation
:
2005 FILE_ID_FULL_DIRECTORY_INFORMATION
*info
= ptr
;
2006 info
->FileId
.QuadPart
= st
->st_ino
;
2007 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
2010 case FileIdBothDirectoryInformation
:
2012 FILE_ID_BOTH_DIRECTORY_INFORMATION
*info
= ptr
;
2013 info
->FileId
.QuadPart
= st
->st_ino
;
2014 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
2017 case FileIdGlobalTxDirectoryInformation
:
2019 FILE_ID_GLOBAL_TX_DIR_INFORMATION
*info
= ptr
;
2020 info
->FileId
.QuadPart
= st
->st_ino
;
2021 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
2026 return STATUS_INVALID_INFO_CLASS
;
2028 return STATUS_SUCCESS
;
2032 static unsigned int server_get_unix_name( HANDLE handle
, char **unix_name
)
2034 data_size_t size
= 1024;
2040 if (!(name
= malloc( size
+ 1 ))) return STATUS_NO_MEMORY
;
2042 SERVER_START_REQ( get_handle_unix_name
)
2044 req
->handle
= wine_server_obj_handle( handle
);
2045 wine_server_set_reply( req
, name
, size
);
2046 ret
= wine_server_call( req
);
2047 size
= reply
->name_len
;
2058 if (ret
!= STATUS_BUFFER_OVERFLOW
) break;
2063 static NTSTATUS
fill_name_info( const char *unix_name
, FILE_NAME_INFORMATION
*info
, LONG
*name_len
)
2068 if (!(status
= unix_to_nt_file_name( unix_name
, &nt_name
)))
2070 const WCHAR
*ptr
= nt_name
;
2071 const WCHAR
*end
= ptr
+ wcslen( nt_name
);
2073 /* Skip the volume mount point. */
2074 while (ptr
!= end
&& *ptr
== '\\') ++ptr
;
2075 while (ptr
!= end
&& *ptr
!= '\\') ++ptr
;
2076 while (ptr
!= end
&& *ptr
== '\\') ++ptr
;
2077 while (ptr
!= end
&& *ptr
!= '\\') ++ptr
;
2079 info
->FileNameLength
= (end
- ptr
) * sizeof(WCHAR
);
2080 if (*name_len
< info
->FileNameLength
) status
= STATUS_BUFFER_OVERFLOW
;
2081 else *name_len
= info
->FileNameLength
;
2083 memcpy( info
->FileName
, ptr
, *name_len
);
2091 static NTSTATUS
get_full_size_info(int fd
, FILE_FS_FULL_SIZE_INFORMATION
*info
) {
2095 #if !defined(linux) || !defined(HAVE_FSTATFS)
2096 struct statvfs stfs
;
2101 if (fstat( fd
, &st
) < 0) return errno_to_status( errno
);
2102 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
)) return STATUS_INVALID_DEVICE_REQUEST
;
2104 /* Linux's fstatvfs is buggy */
2105 #if !defined(linux) || !defined(HAVE_FSTATFS)
2106 if (fstatvfs( fd
, &stfs
) < 0) return errno_to_status( errno
);
2107 bsize
= stfs
.f_frsize
;
2109 if (fstatfs( fd
, &stfs
) < 0) return errno_to_status( errno
);
2110 bsize
= stfs
.f_bsize
;
2112 if (bsize
== 2048) /* assume CD-ROM */
2114 info
->BytesPerSector
= 2048;
2115 info
->SectorsPerAllocationUnit
= 1;
2119 info
->BytesPerSector
= 512;
2120 info
->SectorsPerAllocationUnit
= 8;
2122 info
->TotalAllocationUnits
.QuadPart
= bsize
* stfs
.f_blocks
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
2123 info
->CallerAvailableAllocationUnits
.QuadPart
= bsize
* stfs
.f_bavail
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
2124 info
->ActualAvailableAllocationUnits
.QuadPart
= bsize
* stfs
.f_bfree
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
2125 return STATUS_SUCCESS
;
2129 static NTSTATUS
server_get_file_info( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
,
2130 ULONG length
, FILE_INFORMATION_CLASS info_class
)
2132 SERVER_START_REQ( get_file_info
)
2134 req
->handle
= wine_server_obj_handle( handle
);
2135 req
->info_class
= info_class
;
2136 wine_server_set_reply( req
, buffer
, length
);
2137 io
->Status
= wine_server_call( req
);
2138 io
->Information
= wine_server_reply_size( reply
);
2141 if (io
->Status
== STATUS_NOT_IMPLEMENTED
)
2142 FIXME( "Unsupported info class %x\n", info_class
);
2148 static unsigned int server_open_file_object( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
2149 ULONG sharing
, ULONG options
)
2151 unsigned int status
;
2153 SERVER_START_REQ( open_file_object
)
2155 req
->access
= access
;
2156 req
->attributes
= attr
->Attributes
;
2157 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
2158 req
->sharing
= sharing
;
2159 req
->options
= options
;
2160 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
2161 status
= wine_server_call( req
);
2162 *handle
= wine_server_ptr_handle( reply
->handle
);
2169 /* retrieve device/inode number for all the drives */
2170 static unsigned int get_drives_info( struct file_identity info
[MAX_DOS_DRIVES
] )
2172 static pthread_mutex_t cache_mutex
= PTHREAD_MUTEX_INITIALIZER
;
2173 static struct file_identity cache
[MAX_DOS_DRIVES
];
2174 static time_t last_update
;
2175 static unsigned int nb_drives
;
2177 time_t now
= time(NULL
);
2179 mutex_lock( &cache_mutex
);
2180 if (now
!= last_update
)
2186 if (asprintf( &buffer
, "%s/dosdevices/a:", config_dir
) != -1)
2188 p
= buffer
+ strlen(buffer
) - 2;
2190 for (i
= nb_drives
= 0; i
< MAX_DOS_DRIVES
; i
++)
2193 if (!stat( buffer
, &st
))
2195 cache
[i
].dev
= st
.st_dev
;
2196 cache
[i
].ino
= st
.st_ino
;
2209 memcpy( info
, cache
, sizeof(cache
) );
2211 mutex_unlock( &cache_mutex
);
2216 /* Find a DOS device which can act as the root of "path".
2217 * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
2218 static int find_dos_device( const char *path
)
2220 int len
= strlen(path
);
2224 struct file_identity info
[MAX_DOS_DRIVES
];
2227 if (!get_drives_info( info
)) return -1;
2229 if (stat( path
, &st
) < 0) return -1;
2232 /* strip off trailing slashes */
2233 while (len
> 1 && path
[len
- 1] == '/') len
--;
2235 /* make a copy of the path */
2236 if (!(buffer
= malloc( len
+ 1 ))) return -1;
2237 memcpy( buffer
, path
, len
);
2242 if (!stat( buffer
, &st
) && S_ISDIR( st
.st_mode
))
2244 if (st
.st_dev
!= dev_id
) break;
2246 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
2248 if ((info
[drive
].dev
== st
.st_dev
) && (info
[drive
].ino
== st
.st_ino
))
2250 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2251 debugstr_a(path
), 'A' + drive
, debugstr_a(buffer
), debugstr_a(path
+ len
));
2257 if (len
<= 1) break; /* reached root */
2258 while (len
> 1 && path
[len
- 1] != '/') len
--;
2259 while (len
> 1 && path
[len
- 1] == '/') len
--;
2266 static NTSTATUS
get_mountmgr_fs_info( HANDLE handle
, int fd
, struct mountmgr_unix_drive
*drive
, ULONG size
)
2268 OBJECT_ATTRIBUTES attr
;
2269 UNICODE_STRING string
;
2272 unsigned int status
;
2275 if ((status
= server_get_unix_name( handle
, &unix_name
))) return status
;
2276 letter
= find_dos_device( unix_name
);
2279 memset( drive
, 0, sizeof(*drive
) );
2285 drive
->unix_dev
= st
.st_rdev
? st
.st_rdev
: st
.st_dev
;
2288 drive
->letter
= 'a' + letter
;
2290 init_unicode_string( &string
, MOUNTMGR_DEVICE_NAME
);
2291 InitializeObjectAttributes( &attr
, &string
, 0, NULL
, NULL
);
2292 status
= server_open_file_object( &mountmgr
, GENERIC_READ
| SYNCHRONIZE
, &attr
,
2293 FILE_SHARE_READ
| FILE_SHARE_WRITE
, FILE_SYNCHRONOUS_IO_NONALERT
);
2294 if (status
) return status
;
2296 status
= sync_ioctl( mountmgr
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
, drive
, sizeof(*drive
), drive
, size
);
2297 NtClose( mountmgr
);
2298 if (status
== STATUS_BUFFER_OVERFLOW
) status
= STATUS_SUCCESS
;
2299 else if (status
) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status
);
2304 /***********************************************************************
2305 * get_dir_data_entry
2307 * Return a directory entry from the cached data.
2309 static NTSTATUS
get_dir_data_entry( struct dir_data
*dir_data
, void *info_ptr
, IO_STATUS_BLOCK
*io
,
2310 ULONG max_length
, FILE_INFORMATION_CLASS
class,
2311 union file_directory_info
**last_info
)
2313 const struct dir_data_names
*names
= &dir_data
->names
[dir_data
->pos
];
2314 union file_directory_info
*info
;
2316 ULONG name_len
, start
, dir_size
, attributes
;
2318 if (get_file_info( names
->unix_name
, &st
, &attributes
) == -1)
2320 TRACE( "file no longer exists %s\n", names
->unix_name
);
2321 return STATUS_SUCCESS
;
2323 if (is_ignored_file( &st
))
2325 TRACE( "ignoring file %s\n", names
->unix_name
);
2326 return STATUS_SUCCESS
;
2328 start
= dir_info_align( io
->Information
);
2329 dir_size
= dir_info_size( class, 0 );
2330 if (start
+ dir_size
> max_length
) return STATUS_MORE_ENTRIES
;
2332 max_length
-= start
+ dir_size
;
2333 name_len
= wcslen( names
->long_name
) * sizeof(WCHAR
);
2334 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2335 if (*last_info
&& name_len
> max_length
) return STATUS_MORE_ENTRIES
;
2337 info
= (union file_directory_info
*)((char *)info_ptr
+ start
);
2338 info
->dir
.NextEntryOffset
= 0;
2339 info
->dir
.FileIndex
= 0; /* NTFS always has 0 here, so let's not bother with it */
2341 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2342 if (class != FileNamesInformation
)
2344 if (st
.st_dev
!= dir_data
->id
.dev
) st
.st_ino
= 0; /* ignore inode if on a different device */
2345 fill_file_info( &st
, attributes
, info
, class );
2350 case FileDirectoryInformation
:
2351 info
->dir
.FileNameLength
= name_len
;
2354 case FileFullDirectoryInformation
:
2355 info
->full
.EaSize
= 0; /* FIXME */
2356 info
->full
.FileNameLength
= name_len
;
2359 case FileIdFullDirectoryInformation
:
2360 info
->id_full
.EaSize
= 0; /* FIXME */
2361 info
->id_full
.FileNameLength
= name_len
;
2364 case FileBothDirectoryInformation
:
2365 info
->both
.EaSize
= 0; /* FIXME */
2366 info
->both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2367 memcpy( info
->both
.ShortName
, names
->short_name
, info
->both
.ShortNameLength
);
2368 info
->both
.FileNameLength
= name_len
;
2371 case FileIdBothDirectoryInformation
:
2372 info
->id_both
.EaSize
= 0; /* FIXME */
2373 info
->id_both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2374 memcpy( info
->id_both
.ShortName
, names
->short_name
, info
->id_both
.ShortNameLength
);
2375 info
->id_both
.FileNameLength
= name_len
;
2378 case FileIdGlobalTxDirectoryInformation
:
2379 info
->id_tx
.TxInfoFlags
= 0;
2380 info
->id_tx
.FileNameLength
= name_len
;
2383 case FileNamesInformation
:
2384 info
->names
.FileNameLength
= name_len
;
2392 memcpy( (char *)info
+ dir_size
, names
->long_name
, min( name_len
, max_length
) );
2393 io
->Information
= start
+ dir_size
+ min( name_len
, max_length
);
2394 if (*last_info
) (*last_info
)->next
= (char *)info
- (char *)*last_info
;
2396 return name_len
> max_length
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
2399 #ifdef VFAT_IOCTL_READDIR_BOTH
2401 /***********************************************************************
2402 * read_directory_vfat
2404 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2406 static NTSTATUS
read_directory_data_vfat( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2408 char *short_name
, *long_name
;
2409 KERNEL_DIRENT de
[2];
2410 NTSTATUS status
= STATUS_NO_MEMORY
;
2411 off_t old_pos
= lseek( fd
, 0, SEEK_CUR
);
2413 lseek( fd
, 0, SEEK_SET
);
2415 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1)
2417 if (errno
!= ENOENT
)
2419 status
= STATUS_NOT_SUPPORTED
;
2425 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2426 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2428 while (de
[0].d_reclen
)
2430 if (strcmp( de
[0].d_name
, "." ) && strcmp( de
[0].d_name
, ".." ))
2432 if (de
[1].d_name
[0])
2434 short_name
= de
[0].d_name
;
2435 long_name
= de
[1].d_name
;
2439 long_name
= de
[0].d_name
;
2442 if (!append_entry( data
, long_name
, short_name
, mask
)) goto done
;
2444 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1) break;
2446 status
= STATUS_SUCCESS
;
2448 lseek( fd
, old_pos
, SEEK_SET
);
2451 #endif /* VFAT_IOCTL_READDIR_BOTH */
2454 #ifdef HAVE_GETATTRLIST
2455 /***********************************************************************
2456 * read_directory_getattrlist
2458 * Read a single file from a directory by determining whether the file
2459 * identified by mask exists using getattrlist.
2461 static NTSTATUS
read_directory_data_getattrlist( struct dir_data
*data
, const char *unix_name
)
2463 struct attrlist attrlist
;
2464 #include "pshpack4.h"
2468 struct attrreference name_reference
;
2470 char name
[NAME_MAX
* 3 + 1];
2472 #include "poppack.h"
2474 memset( &attrlist
, 0, sizeof(attrlist
) );
2475 attrlist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
2476 attrlist
.commonattr
= ATTR_CMN_NAME
| ATTR_CMN_OBJTYPE
;
2477 if (getattrlist( unix_name
, &attrlist
, &buffer
, sizeof(buffer
), FSOPT_NOFOLLOW
) == -1)
2478 return STATUS_NO_SUCH_FILE
;
2479 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2480 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2481 if (buffer
.type
== VLNK
)
2484 attrlist
.commonattr
= 0;
2485 if (getattrlist( unix_name
, &attrlist
, &dummy
, sizeof(dummy
), 0 ) == -1)
2486 return STATUS_NO_SUCH_FILE
;
2489 TRACE( "found %s\n", buffer
.name
);
2491 if (!append_entry( data
, buffer
.name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2493 return STATUS_SUCCESS
;
2495 #endif /* HAVE_GETATTRLIST */
2498 /***********************************************************************
2499 * read_directory_stat
2501 * Read a single file from a directory by determining whether the file
2502 * identified by mask exists using stat.
2504 static NTSTATUS
read_directory_data_stat( struct dir_data
*data
, const char *unix_name
)
2508 /* if the file system is not case sensitive we can't find the actual name through stat() */
2509 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE
;
2510 if (stat( unix_name
, &st
) == -1) return STATUS_NO_SUCH_FILE
;
2512 TRACE( "found %s\n", unix_name
);
2514 if (!append_entry( data
, unix_name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2516 return STATUS_SUCCESS
;
2520 /***********************************************************************
2521 * read_directory_readdir
2523 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2525 static NTSTATUS
read_directory_data_readdir( struct dir_data
*data
, const UNICODE_STRING
*mask
)
2528 NTSTATUS status
= STATUS_NO_MEMORY
;
2529 DIR *dir
= opendir( "." );
2531 if (!dir
) return STATUS_NO_SUCH_FILE
;
2533 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2534 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2535 while ((de
= readdir( dir
)))
2537 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
2538 if (!append_entry( data
, de
->d_name
, NULL
, mask
)) goto done
;
2540 status
= STATUS_SUCCESS
;
2548 /***********************************************************************
2549 * read_directory_data
2551 * Read the full contents of a directory, using one of the above helper functions.
2553 static NTSTATUS
read_directory_data( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2557 #ifdef VFAT_IOCTL_READDIR_BOTH
2558 if (!(status
= read_directory_data_vfat( data
, fd
, mask
))) return status
;
2561 if (!has_wildcard( mask
))
2563 /* convert the mask to a Unix name and check for it */
2564 char unix_name
[MAX_DIR_ENTRY_LEN
* 3 + 1];
2565 int ret
= ntdll_wcstoumbs( mask
->Buffer
, mask
->Length
/ sizeof(WCHAR
),
2566 unix_name
, sizeof(unix_name
) - 1, TRUE
);
2570 #ifdef HAVE_GETATTRLIST
2571 if (!(status
= read_directory_data_getattrlist( data
, unix_name
))) return status
;
2573 if (!(status
= read_directory_data_stat( data
, unix_name
))) return status
;
2577 return read_directory_data_readdir( data
, mask
);
2581 /* compare file names for directory sorting */
2582 static int name_compare( const void *a
, const void *b
)
2584 const struct dir_data_names
*file_a
= (const struct dir_data_names
*)a
;
2585 const struct dir_data_names
*file_b
= (const struct dir_data_names
*)b
;
2586 int ret
= wcsicmp( file_a
->long_name
, file_b
->long_name
);
2587 if (!ret
) ret
= wcscmp( file_a
->long_name
, file_b
->long_name
);
2592 /***********************************************************************
2593 * init_cached_dir_data
2595 * Initialize the cached directory contents.
2597 static NTSTATUS
init_cached_dir_data( struct dir_data
**data_ret
, int fd
, const UNICODE_STRING
*mask
)
2599 struct dir_data
*data
;
2604 if (!(data
= calloc( 1, sizeof(*data
) ))) return STATUS_NO_MEMORY
;
2606 if ((status
= read_directory_data( data
, fd
, mask
)))
2608 free_dir_data( data
);
2612 /* sort filenames, but not "." and ".." */
2614 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, "." )) i
++;
2615 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, ".." )) i
++;
2616 if (i
< data
->count
) qsort( data
->names
+ i
, data
->count
- i
, sizeof(*data
->names
), name_compare
);
2621 data
->id
.dev
= st
.st_dev
;
2622 data
->id
.ino
= st
.st_ino
;
2625 TRACE( "mask %s found %u files\n", debugstr_us( mask
), data
->count
);
2626 for (i
= 0; i
< data
->count
; i
++)
2627 TRACE( "%s %s\n", debugstr_w(data
->names
[i
].long_name
), debugstr_w(data
->names
[i
].short_name
) );
2630 return data
->count
? STATUS_SUCCESS
: STATUS_NO_SUCH_FILE
;
2634 /***********************************************************************
2635 * get_cached_dir_data
2637 * Retrieve the cached directory data, or initialize it if necessary.
2639 static unsigned int get_cached_dir_data( HANDLE handle
, struct dir_data
**data_ret
, int fd
,
2640 const UNICODE_STRING
*mask
)
2643 int entry
= -1, free_entries
[16];
2644 unsigned int status
;
2646 SERVER_START_REQ( get_directory_cache_entry
)
2648 req
->handle
= wine_server_obj_handle( handle
);
2649 wine_server_set_reply( req
, free_entries
, sizeof(free_entries
) );
2650 if (!(status
= wine_server_call( req
))) entry
= reply
->entry
;
2652 for (i
= 0; i
< wine_server_reply_size( reply
) / sizeof(*free_entries
); i
++)
2654 int free_idx
= free_entries
[i
];
2655 if (free_idx
< dir_data_cache_size
)
2657 free_dir_data( dir_data_cache
[free_idx
] );
2658 dir_data_cache
[free_idx
] = NULL
;
2666 if (status
== STATUS_SHARING_VIOLATION
) FIXME( "shared directory handle not supported yet\n" );
2670 if (entry
>= dir_data_cache_size
)
2672 unsigned int size
= max( dir_data_cache_initial_size
, max( dir_data_cache_size
* 2, entry
+ 1 ) );
2673 struct dir_data
**new_cache
= realloc( dir_data_cache
, size
* sizeof(*new_cache
) );
2675 if (!new_cache
) return STATUS_NO_MEMORY
;
2676 memset( new_cache
+ dir_data_cache_size
, 0, (size
- dir_data_cache_size
) * sizeof(*new_cache
) );
2677 dir_data_cache
= new_cache
;
2678 dir_data_cache_size
= size
;
2681 if (!dir_data_cache
[entry
]) status
= init_cached_dir_data( &dir_data_cache
[entry
], fd
, mask
);
2683 *data_ret
= dir_data_cache
[entry
];
2688 /******************************************************************************
2689 * NtQueryDirectoryFile (NTDLL.@)
2691 NTSTATUS WINAPI
NtQueryDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc_routine
,
2692 void *apc_context
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
2693 FILE_INFORMATION_CLASS info_class
, BOOLEAN single_entry
,
2694 UNICODE_STRING
*mask
, BOOLEAN restart_scan
)
2696 int cwd
, fd
, needs_close
;
2697 enum server_fd_type type
;
2698 struct dir_data
*data
;
2699 unsigned int status
;
2701 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2702 handle
, event
, apc_routine
, apc_context
, io
, buffer
,
2703 (int)length
, info_class
, single_entry
, debugstr_us(mask
),
2706 if (event
|| apc_routine
)
2708 FIXME( "Unsupported yet option\n" );
2709 return STATUS_NOT_IMPLEMENTED
;
2713 case FileDirectoryInformation
:
2714 case FileBothDirectoryInformation
:
2715 case FileFullDirectoryInformation
:
2716 case FileIdBothDirectoryInformation
:
2717 case FileIdFullDirectoryInformation
:
2718 case FileIdGlobalTxDirectoryInformation
:
2719 case FileNamesInformation
:
2720 if (length
< dir_info_align( dir_info_size( info_class
, 1 ))) return STATUS_INFO_LENGTH_MISMATCH
;
2722 case FileObjectIdInformation
:
2723 if (length
!= sizeof(FILE_OBJECTID_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2724 return STATUS_INVALID_INFO_CLASS
;
2725 case FileQuotaInformation
:
2726 if (length
!= sizeof(FILE_QUOTA_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2727 return STATUS_INVALID_INFO_CLASS
;
2728 case FileReparsePointInformation
:
2729 if (length
!= sizeof(FILE_REPARSE_POINT_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2730 return STATUS_INVALID_INFO_CLASS
;
2732 return STATUS_INVALID_INFO_CLASS
;
2734 if (!buffer
) return STATUS_ACCESS_VIOLATION
;
2736 if ((status
= server_get_unix_fd( handle
, FILE_LIST_DIRECTORY
, &fd
, &needs_close
, &type
, NULL
)))
2739 if (type
!= FD_TYPE_DIR
)
2741 if (needs_close
) close( fd
);
2742 return STATUS_INVALID_PARAMETER
;
2745 io
->Information
= 0;
2747 mutex_lock( &dir_mutex
);
2749 cwd
= open( ".", O_RDONLY
);
2750 if (fchdir( fd
) != -1)
2752 if (!(status
= get_cached_dir_data( handle
, &data
, fd
, mask
)))
2754 union file_directory_info
*last_info
= NULL
;
2756 if (restart_scan
) data
->pos
= 0;
2758 while (!status
&& data
->pos
< data
->count
)
2760 status
= get_dir_data_entry( data
, buffer
, io
, length
, info_class
, &last_info
);
2761 if (!status
|| status
== STATUS_BUFFER_OVERFLOW
) data
->pos
++;
2762 if (single_entry
&& last_info
) break;
2765 if (!last_info
) status
= STATUS_NO_MORE_FILES
;
2766 else if (status
== STATUS_MORE_ENTRIES
) status
= STATUS_SUCCESS
;
2768 io
->Status
= status
;
2770 if (cwd
== -1 || fchdir( cwd
) == -1) chdir( "/" );
2772 else status
= errno_to_status( errno
);
2774 mutex_unlock( &dir_mutex
);
2776 if (needs_close
) close( fd
);
2777 if (cwd
!= -1) close( cwd
);
2778 TRACE( "=> %x (%ld)\n", status
, io
->Information
);
2783 /***********************************************************************
2786 * Find a file in a directory the hard way, by doing a case-insensitive search.
2787 * The file found is appended to unix_name at pos.
2788 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2790 static NTSTATUS
find_file_in_dir( char *unix_name
, int pos
, const WCHAR
*name
, int length
,
2791 BOOLEAN check_case
)
2793 WCHAR buffer
[MAX_DIR_ENTRY_LEN
];
2794 BOOLEAN is_name_8_dot_3
;
2800 /* try a shortcut for this directory */
2802 unix_name
[pos
++] = '/';
2803 ret
= ntdll_wcstoumbs( name
, length
, unix_name
+ pos
, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
2804 if (ret
>= 0 && ret
<= MAX_DIR_ENTRY_LEN
)
2806 unix_name
[pos
+ ret
] = 0;
2807 if (!stat( unix_name
, &st
)) return STATUS_SUCCESS
;
2809 if (check_case
) goto not_found
; /* we want an exact match */
2811 if (pos
> 1) unix_name
[pos
- 1] = 0;
2812 else unix_name
[1] = 0; /* keep the initial slash */
2814 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2816 is_name_8_dot_3
= is_legal_8dot3_name( name
, length
);
2817 #ifndef VFAT_IOCTL_READDIR_BOTH
2818 is_name_8_dot_3
= is_name_8_dot_3
&& length
>= 8 && name
[4] == '~';
2821 if (!is_name_8_dot_3
&& !get_dir_case_sensitivity( unix_name
)) goto not_found
;
2823 /* now look for it through the directory */
2825 #ifdef VFAT_IOCTL_READDIR_BOTH
2826 if (is_name_8_dot_3
)
2828 int fd
= open( unix_name
, O_RDONLY
| O_DIRECTORY
);
2831 KERNEL_DIRENT kde
[2];
2833 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) != -1)
2835 unix_name
[pos
- 1] = '/';
2836 while (kde
[0].d_reclen
)
2838 if (kde
[1].d_name
[0])
2840 ret
= ntdll_umbstowcs( kde
[1].d_name
, strlen(kde
[1].d_name
),
2841 buffer
, MAX_DIR_ENTRY_LEN
);
2842 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2844 strcpy( unix_name
+ pos
, kde
[1].d_name
);
2846 return STATUS_SUCCESS
;
2849 ret
= ntdll_umbstowcs( kde
[0].d_name
, strlen(kde
[0].d_name
),
2850 buffer
, MAX_DIR_ENTRY_LEN
);
2851 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2853 strcpy( unix_name
+ pos
,
2854 kde
[1].d_name
[0] ? kde
[1].d_name
: kde
[0].d_name
);
2856 return STATUS_SUCCESS
;
2858 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) == -1)
2864 /* if that did not work, restore previous state of unix_name */
2865 unix_name
[pos
- 1] = 0;
2869 /* fall through to normal handling */
2871 #endif /* VFAT_IOCTL_READDIR_BOTH */
2873 if (!(dir
= opendir( unix_name
))) return errno_to_status( errno
);
2875 unix_name
[pos
- 1] = '/';
2876 while ((de
= readdir( dir
)))
2878 ret
= ntdll_umbstowcs( de
->d_name
, strlen(de
->d_name
), buffer
, MAX_DIR_ENTRY_LEN
);
2879 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2881 strcpy( unix_name
+ pos
, de
->d_name
);
2883 return STATUS_SUCCESS
;
2886 if (!is_name_8_dot_3
) continue;
2888 if (!is_legal_8dot3_name( buffer
, ret
))
2890 WCHAR short_nameW
[12];
2891 ret
= hash_short_file_name( buffer
, ret
, short_nameW
);
2892 if (ret
== length
&& !wcsnicmp( short_nameW
, name
, length
))
2894 strcpy( unix_name
+ pos
, de
->d_name
);
2896 return STATUS_SUCCESS
;
2903 unix_name
[pos
- 1] = 0;
2904 return STATUS_OBJECT_NAME_NOT_FOUND
;
2910 static const WCHAR catrootW
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2911 static const WCHAR catroot2W
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2912 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};
2913 static const WCHAR driversetcW
[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2914 static const WCHAR logfilesW
[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2915 static const WCHAR spoolW
[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2916 static const WCHAR system32W
[] = {'s','y','s','t','e','m','3','2',0};
2917 static const WCHAR syswow64W
[] = {'s','y','s','w','o','w','6','4',0};
2918 static const WCHAR sysnativeW
[] = {'s','y','s','n','a','t','i','v','e',0};
2919 static const WCHAR regeditW
[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2920 static const WCHAR syswow64_regeditW
[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
2921 static const WCHAR windirW
[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\',0};
2922 static const WCHAR syswow64dirW
[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','w','o','w','6','4','\\'};
2924 static const WCHAR
* const no_redirect
[] =
2934 static struct file_identity windir
, sysdir
;
2936 static inline ULONG
starts_with_path( const WCHAR
*name
, ULONG name_len
, const WCHAR
*prefix
)
2938 ULONG len
= wcslen( prefix
);
2940 if (name_len
< len
) return 0;
2941 if (wcsnicmp( name
, prefix
, len
)) return 0;
2942 if (name_len
> len
&& name
[len
] != '\\') return 0;
2946 static BOOL
replace_path( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*str
, ULONG prefix_len
,
2947 const WCHAR
*match
, const WCHAR
*replace
)
2949 const WCHAR
*name
= attr
->ObjectName
->Buffer
;
2950 ULONG match_len
, replace_len
, len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
2953 if (!starts_with_path( name
+ prefix_len
, len
- prefix_len
, match
)) return FALSE
;
2955 match_len
= wcslen( match
);
2956 replace_len
= wcslen( replace
);
2957 str
->Length
= (len
+ replace_len
- match_len
) * sizeof(WCHAR
);
2958 str
->MaximumLength
= str
->Length
+ sizeof(WCHAR
);
2959 if (!(p
= str
->Buffer
= malloc( str
->MaximumLength
))) return FALSE
;
2961 memcpy( p
, name
, prefix_len
* sizeof(WCHAR
) );
2963 memcpy( p
, replace
, replace_len
* sizeof(WCHAR
) );
2965 name
+= prefix_len
+ match_len
;
2966 len
-= prefix_len
+ match_len
;
2967 memcpy( p
, name
, len
* sizeof(WCHAR
) );
2969 attr
->ObjectName
= str
;
2973 /***********************************************************************
2976 static void init_redirects(void)
2978 static const char system_dir
[] = "/dosdevices/c:/windows/system32";
2982 if (asprintf( &dir
, "%s%s", config_dir
, system_dir
) == -1) return;
2983 if (!stat( dir
, &st
))
2985 sysdir
.dev
= st
.st_dev
;
2986 sysdir
.ino
= st
.st_ino
;
2988 *strrchr( dir
, '/' ) = 0;
2989 if (!stat( dir
, &st
))
2991 windir
.dev
= st
.st_dev
;
2992 windir
.ino
= st
.st_ino
;
2994 else ERR( "%s: %s\n", dir
, strerror(errno
) );
2999 /***********************************************************************
3002 BOOL
get_redirect( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*redir
)
3004 const WCHAR
*name
= attr
->ObjectName
->Buffer
;
3005 unsigned int i
, prefix_len
= 0, len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
3007 redir
->Buffer
= NULL
;
3008 if (!NtCurrentTeb64()) return FALSE
;
3009 if (!len
) return FALSE
;
3011 if (!attr
->RootDirectory
)
3013 prefix_len
= wcslen( windirW
);
3014 if (len
< prefix_len
|| wcsnicmp( name
, windirW
, prefix_len
)) return FALSE
;
3018 int fd
, needs_close
;
3021 if (server_get_unix_fd( attr
->RootDirectory
, 0, &fd
, &needs_close
, NULL
, NULL
)) return FALSE
;
3023 if (needs_close
) close( fd
);
3024 if (!is_same_file( &windir
, &st
))
3026 if (!is_same_file( &sysdir
, &st
)) return FALSE
;
3027 if (NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
]) return FALSE
;
3028 if (name
[0] == '\\') return FALSE
;
3030 /* only check for paths that should NOT be redirected */
3031 for (i
= 0; i
< ARRAY_SIZE( no_redirect
); i
++)
3032 if (starts_with_path( name
, len
, no_redirect
[i
] + 9 /* "system32\\" */)) return FALSE
;
3034 /* redirect everything else */
3035 redir
->Length
= sizeof(syswow64dirW
) + len
* sizeof(WCHAR
);
3036 redir
->MaximumLength
= redir
->Length
+ sizeof(WCHAR
);
3037 if (!(redir
->Buffer
= malloc( redir
->MaximumLength
))) return FALSE
;
3038 memcpy( redir
->Buffer
, syswow64dirW
, sizeof(syswow64dirW
) );
3039 memcpy( redir
->Buffer
+ ARRAY_SIZE(syswow64dirW
), name
, len
* sizeof(WCHAR
) );
3040 redir
->Buffer
[redir
->Length
/ sizeof(WCHAR
)] = 0;
3041 attr
->RootDirectory
= 0;
3042 attr
->ObjectName
= redir
;
3047 /* sysnative is redirected even when redirection is disabled */
3049 if (replace_path( attr
, redir
, prefix_len
, sysnativeW
, system32W
)) return TRUE
;
3051 if (NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
]) return FALSE
;
3053 for (i
= 0; i
< ARRAY_SIZE( no_redirect
); i
++)
3054 if (starts_with_path( name
+ prefix_len
, len
- prefix_len
, no_redirect
[i
] )) return FALSE
;
3056 if (replace_path( attr
, redir
, prefix_len
, system32W
, syswow64W
)) return TRUE
;
3057 if (replace_path( attr
, redir
, prefix_len
, regeditW
, syswow64_regeditW
)) return TRUE
;
3063 /* there are no redirects on 64-bit */
3064 BOOL
get_redirect( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*redir
)
3066 redir
->Buffer
= NULL
;
3073 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
3075 /***********************************************************************
3078 void init_files(void)
3083 if (is_old_wow64()) init_redirects();
3085 /* a couple of directories that we don't want to return in directory searches */
3086 ignore_file( config_dir
);
3087 ignore_file( "/dev" );
3088 ignore_file( "/proc" );
3090 ignore_file( "/sys" );
3092 /* retrieve initial umask */
3093 start_umask
= umask( 0777 );
3094 umask( start_umask
);
3096 if (!open_hkcu_key( "Software\\Wine", &key
))
3098 static WCHAR showdotfilesW
[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
3101 UNICODE_STRING nameW
;
3103 init_unicode_string( &nameW
, showdotfilesW
);
3104 if (!NtQueryValueKey( key
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
3106 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
3107 show_dot_files
= IS_OPTION_TRUE( str
[0] );
3114 /******************************************************************************
3117 * Get the Unix path of a DOS device.
3119 static NTSTATUS
get_dos_device( char **unix_name
, int start_pos
)
3122 char *new_name
, *dev
= *unix_name
+ start_pos
;
3124 /* special case for drive devices */
3125 if (dev
[0] && dev
[1] == ':' && !dev
[2]) strcpy( dev
+ 1, "::" );
3127 if (strchr( dev
, '/' )) goto failed
;
3131 if (!stat( *unix_name
, &st
))
3133 TRACE( "-> %s\n", debugstr_a(*unix_name
));
3134 return STATUS_SUCCESS
;
3138 /* now try some defaults for it */
3139 if (!strcmp( dev
, "aux" ))
3141 strcpy( dev
, "com1" );
3144 if (!strcmp( dev
, "prn" ))
3146 strcpy( dev
, "lpt1" );
3151 if (dev
[1] == ':' && dev
[2] == ':') /* drive device */
3153 dev
[2] = 0; /* remove last ':' to get the drive mount point symlink */
3154 new_name
= get_default_drive_device( *unix_name
);
3157 *unix_name
= new_name
;
3158 if (!new_name
) return STATUS_BAD_DEVICE_TYPE
;
3159 dev
= NULL
; /* last try */
3164 return STATUS_BAD_DEVICE_TYPE
;
3168 /* return the length of the DOS namespace prefix if any */
3169 static inline int get_dos_prefix_len( const UNICODE_STRING
*name
)
3171 static const WCHAR nt_prefixW
[] = {'\\','?','?','\\'};
3172 static const WCHAR dosdev_prefixW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
3174 if (name
->Length
>= sizeof(nt_prefixW
) &&
3175 !memcmp( name
->Buffer
, nt_prefixW
, sizeof(nt_prefixW
) ))
3176 return ARRAY_SIZE( nt_prefixW
);
3178 if (name
->Length
>= sizeof(dosdev_prefixW
) &&
3179 !wcsnicmp( name
->Buffer
, dosdev_prefixW
, ARRAY_SIZE( dosdev_prefixW
)))
3180 return ARRAY_SIZE( dosdev_prefixW
);
3186 /***********************************************************************
3187 * remove_last_componentA
3189 * Remove the last component of the path. Helper for find_drive_rootA.
3191 static inline unsigned int remove_last_componentA( const char *path
, unsigned int len
)
3197 /* find start of the last path component */
3198 unsigned int prev
= len
;
3199 if (prev
<= 1) break; /* reached root */
3200 while (prev
> 1 && path
[prev
- 1] != '/') prev
--;
3201 /* does removing it take us up a level? */
3202 if (len
- prev
!= 1 || path
[prev
] != '.') /* not '.' */
3204 if (len
- prev
== 2 && path
[prev
] == '.' && path
[prev
+1] == '.') /* is it '..'? */
3209 /* strip off trailing slashes */
3210 while (prev
> 1 && path
[prev
- 1] == '/') prev
--;
3217 /***********************************************************************
3220 * Find a drive for which the root matches the beginning of the given path.
3221 * This can be used to translate a Unix path into a drive + DOS path.
3222 * Return value is the drive, or -1 on error. On success, ppath is modified
3223 * to point to the beginning of the DOS path.
3225 static NTSTATUS
find_drive_rootA( LPCSTR
*ppath
, unsigned int len
, int *drive_ret
)
3227 /* Starting with the full path, check if the device and inode match any of
3228 * the wine 'drives'. If not then remove the last path component and try
3229 * again. If the last component was a '..' then skip a normal component
3230 * since it's a directory that's ascended back out of.
3234 const char *path
= *ppath
;
3236 struct file_identity info
[MAX_DOS_DRIVES
];
3238 /* get device and inode of all drives */
3239 if (!get_drives_info( info
)) return STATUS_OBJECT_PATH_NOT_FOUND
;
3241 /* strip off trailing slashes */
3242 while (len
> 1 && path
[len
- 1] == '/') len
--;
3244 /* make a copy of the path */
3245 if (!(buffer
= malloc( len
+ 1 ))) return STATUS_NO_MEMORY
;
3246 memcpy( buffer
, path
, len
);
3251 if (!stat( buffer
, &st
) && S_ISDIR( st
.st_mode
))
3253 /* Find the drive */
3254 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
3256 if ((info
[drive
].dev
== st
.st_dev
) && (info
[drive
].ino
== st
.st_ino
))
3258 if (len
== 1) len
= 0; /* preserve root slash in returned path */
3259 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
3260 debugstr_a(path
), 'A' + drive
, debugstr_a(buffer
), debugstr_a(path
+ len
));
3264 return STATUS_SUCCESS
;
3268 if (len
<= 1) break; /* reached root */
3269 len
= remove_last_componentA( buffer
, len
);
3273 return STATUS_OBJECT_PATH_NOT_FOUND
;
3277 /******************************************************************************
3280 * Recursively search directories from the dir queue for a given inode.
3282 static NTSTATUS
find_file_id( char **unix_name
, ULONG
*len
, ULONGLONG file_id
, dev_t dev
)
3289 char *name
= *unix_name
;
3291 while (!(status
= next_dir_in_queue( name
)))
3293 if (!(dir
= opendir( name
))) continue;
3294 TRACE( "searching %s for %s\n", debugstr_a(name
), wine_dbgstr_longlong(file_id
) );
3295 pos
= strlen( name
);
3296 if (pos
+ MAX_DIR_ENTRY_LEN
>= *len
/ sizeof(WCHAR
))
3298 if (!(name
= realloc( name
, *len
* 2 )))
3301 return STATUS_NO_MEMORY
;
3307 while ((de
= readdir( dir
)))
3309 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
3310 strcpy( name
+ pos
, de
->d_name
);
3311 if (lstat( name
, &st
) == -1) continue;
3312 if (st
.st_dev
!= dev
) continue;
3313 if (st
.st_ino
== file_id
)
3316 return STATUS_SUCCESS
;
3318 if (!S_ISDIR( st
.st_mode
)) continue;
3319 if ((status
= add_dir_to_queue( name
)) != STATUS_SUCCESS
)
3331 /******************************************************************************
3332 * file_id_to_unix_file_name
3334 * Lookup a file from its file id instead of its name.
3336 static NTSTATUS
file_id_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char **unix_name_ret
,
3337 UNICODE_STRING
*nt_name
)
3339 enum server_fd_type type
;
3340 int old_cwd
, root_fd
, needs_close
;
3345 struct stat st
, root_st
;
3347 nt_name
->Buffer
= NULL
;
3348 if (attr
->ObjectName
->Length
!= sizeof(ULONGLONG
)) return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3349 if (!attr
->RootDirectory
) return STATUS_INVALID_PARAMETER
;
3350 memcpy( &file_id
, attr
->ObjectName
->Buffer
, sizeof(file_id
) );
3352 len
= 2 * MAX_DIR_ENTRY_LEN
+ 4;
3353 if (!(unix_name
= malloc( len
))) return STATUS_NO_MEMORY
;
3354 strcpy( unix_name
, "." );
3356 if ((status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3359 if (type
!= FD_TYPE_DIR
)
3361 status
= STATUS_OBJECT_TYPE_MISMATCH
;
3365 fstat( root_fd
, &root_st
);
3366 if (root_st
.st_ino
== file_id
) /* shortcut for "." */
3368 status
= STATUS_SUCCESS
;
3372 mutex_lock( &dir_mutex
);
3373 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3375 /* shortcut for ".." */
3376 if (!stat( "..", &st
) && st
.st_dev
== root_st
.st_dev
&& st
.st_ino
== file_id
)
3378 strcpy( unix_name
, ".." );
3379 status
= STATUS_SUCCESS
;
3383 status
= add_dir_to_queue( "." );
3385 status
= find_file_id( &unix_name
, &len
, file_id
, root_st
.st_dev
);
3386 if (!status
) /* get rid of "./" prefix */
3387 memmove( unix_name
, unix_name
+ 2, strlen(unix_name
) - 1 );
3390 if (fchdir( old_cwd
) == -1) chdir( "/" );
3392 else status
= errno_to_status( errno
);
3393 mutex_unlock( &dir_mutex
);
3394 if (old_cwd
!= -1) close( old_cwd
);
3397 if (status
== STATUS_SUCCESS
)
3399 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id
), debugstr_a(unix_name
) );
3400 *unix_name_ret
= unix_name
;
3402 nt_name
->MaximumLength
= (strlen(unix_name
) + 1) * sizeof(WCHAR
);
3403 if ((nt_name
->Buffer
= malloc( nt_name
->MaximumLength
)))
3405 DWORD i
, len
= ntdll_umbstowcs( unix_name
, strlen(unix_name
), nt_name
->Buffer
, strlen(unix_name
) );
3406 nt_name
->Buffer
[len
] = 0;
3407 nt_name
->Length
= len
* sizeof(WCHAR
);
3408 for (i
= 0; i
< len
; i
++) if (nt_name
->Buffer
[i
] == '/') nt_name
->Buffer
[i
] = '\\';
3413 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id
), attr
->RootDirectory
);
3416 if (needs_close
) close( root_fd
);
3421 /******************************************************************************
3424 * Helper for nt_to_unix_file_name
3426 static NTSTATUS
lookup_unix_name( const WCHAR
*name
, int name_len
, char **buffer
, int unix_len
, int pos
,
3427 UINT disposition
, BOOL is_unix
)
3429 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, '/', 0 };
3433 char *unix_name
= *buffer
;
3434 const WCHAR
*ptr
, *end
;
3436 /* check syntax of individual components */
3438 for (ptr
= name
, end
= name
+ name_len
; ptr
< end
; ptr
++)
3440 if (*ptr
== '\\') return STATUS_OBJECT_NAME_INVALID
; /* duplicate backslash */
3443 if (ptr
+ 1 == end
) return STATUS_OBJECT_NAME_INVALID
; /* "." element */
3444 if (ptr
[1] == '\\') return STATUS_OBJECT_NAME_INVALID
; /* "." element */
3447 if (ptr
+ 2 == end
) return STATUS_OBJECT_NAME_INVALID
; /* ".." element */
3448 if (ptr
[2] == '\\') return STATUS_OBJECT_NAME_INVALID
; /* ".." element */
3451 /* check for invalid characters (all chars except 0 are valid for unix) */
3452 for ( ; ptr
< end
&& *ptr
!= '\\'; ptr
++)
3454 if (!*ptr
) return STATUS_OBJECT_NAME_INVALID
;
3455 if (is_unix
) continue;
3456 if (*ptr
< 32 || wcschr( invalid_charsW
, *ptr
)) return STATUS_OBJECT_NAME_INVALID
;
3460 /* try a shortcut first */
3462 unix_name
[pos
] = '/';
3463 ret
= ntdll_wcstoumbs( name
, name_len
, unix_name
+ pos
+ 1, unix_len
- pos
- 1, TRUE
);
3464 if (ret
>= 0 && ret
< unix_len
- pos
- 1)
3467 unix_name
[pos
+ 1 + ret
] = 0;
3468 for (p
= unix_name
+ pos
; *p
; p
++) if (*p
== '\\') *p
= '/';
3469 if (!stat( unix_name
, &st
))
3471 if (disposition
== FILE_CREATE
) return STATUS_OBJECT_NAME_COLLISION
;
3472 return STATUS_SUCCESS
;
3476 if (!name_len
) /* empty name -> drive root doesn't exist */
3477 return STATUS_OBJECT_PATH_NOT_FOUND
;
3478 if (is_unix
&& (disposition
== FILE_OPEN
|| disposition
== FILE_OVERWRITE
))
3479 return STATUS_OBJECT_NAME_NOT_FOUND
;
3481 /* now do it component by component */
3485 const WCHAR
*end
, *next
;
3488 while (end
< name
+ name_len
&& *end
!= '\\') end
++;
3490 if (next
< name
+ name_len
) next
++;
3491 name_len
-= next
- name
;
3493 /* grow the buffer if needed */
3495 if (unix_len
- pos
< MAX_DIR_ENTRY_LEN
+ 3)
3498 unix_len
+= 2 * MAX_DIR_ENTRY_LEN
;
3499 if (!(new_name
= realloc( unix_name
, unix_len
))) return STATUS_NO_MEMORY
;
3500 unix_name
= *buffer
= new_name
;
3503 status
= find_file_in_dir( unix_name
, pos
, name
, end
- name
, is_unix
);
3505 /* if this is the last element, not finding it is not necessarily fatal */
3508 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
3510 if (disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
3512 ret
= ntdll_wcstoumbs( name
, end
- name
, unix_name
+ pos
+ 1, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
3513 if (ret
> 0 && ret
<= MAX_DIR_ENTRY_LEN
)
3515 unix_name
[pos
] = '/';
3517 if (end
< next
) unix_name
[pos
++] = '/';
3519 status
= STATUS_NO_SUCH_FILE
;
3524 else if (status
== STATUS_SUCCESS
&& disposition
== FILE_CREATE
)
3526 status
= STATUS_OBJECT_NAME_COLLISION
;
3528 if (end
< next
) strcat( unix_name
, "/" );
3530 else if (status
== STATUS_OBJECT_NAME_NOT_FOUND
) status
= STATUS_OBJECT_PATH_NOT_FOUND
;
3532 if (status
!= STATUS_SUCCESS
) break;
3534 pos
+= strlen( unix_name
+ pos
);
3542 /******************************************************************************
3543 * nt_to_unix_file_name_no_root
3545 static NTSTATUS
nt_to_unix_file_name_no_root( const UNICODE_STRING
*nameW
, char **unix_name_ret
,
3548 static const WCHAR unixW
[] = {'u','n','i','x'};
3549 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, 0 };
3551 NTSTATUS status
= STATUS_SUCCESS
;
3555 int pos
, ret
, name_len
, unix_len
, prefix_len
;
3556 WCHAR prefix
[MAX_DIR_ENTRY_LEN
+ 1];
3557 BOOLEAN is_unix
= FALSE
;
3559 name
= nameW
->Buffer
;
3560 name_len
= nameW
->Length
/ sizeof(WCHAR
);
3562 if (!name_len
|| name
[0] != '\\') return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3564 if (!(pos
= get_dos_prefix_len( nameW
)))
3565 return STATUS_BAD_DEVICE_TYPE
; /* no DOS prefix, assume NT native name */
3570 if (!name_len
) return STATUS_OBJECT_NAME_INVALID
;
3572 /* check for sub-directory */
3573 for (pos
= 0; pos
< name_len
&& pos
<= MAX_DIR_ENTRY_LEN
; pos
++)
3575 if (name
[pos
] == '\\') break;
3576 if (name
[pos
] < 32 || wcschr( invalid_charsW
, name
[pos
] ))
3577 return STATUS_OBJECT_NAME_INVALID
;
3578 prefix
[pos
] = (name
[pos
] >= 'A' && name
[pos
] <= 'Z') ? name
[pos
] + 'a' - 'A' : name
[pos
];
3580 if (pos
> MAX_DIR_ENTRY_LEN
) return STATUS_OBJECT_NAME_INVALID
;
3582 if (pos
>= 4 && !memcmp( prefix
, unixW
, sizeof(unixW
) ))
3584 /* allow slash for unix namespace */
3585 if (pos
> 4 && prefix
[4] == '/') pos
= 4;
3589 prefix
[prefix_len
] = 0;
3591 unix_len
= name_len
* 3 + MAX_DIR_ENTRY_LEN
+ 3;
3592 unix_len
+= strlen(config_dir
) + sizeof("/dosdevices/");
3593 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3594 strcpy( unix_name
, config_dir
);
3595 strcat( unix_name
, "/dosdevices/" );
3596 pos
= strlen(unix_name
);
3598 ret
= ntdll_wcstoumbs( prefix
, prefix_len
, unix_name
+ pos
, unix_len
- pos
- 1, TRUE
);
3602 return STATUS_OBJECT_NAME_INVALID
;
3605 if (prefix_len
== name_len
) /* no subdir, plain DOS device */
3607 unix_name
[pos
+ ret
] = 0;
3608 *unix_name_ret
= unix_name
;
3609 return get_dos_device( unix_name_ret
, pos
);
3613 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3615 if (wcschr( prefix
, '/' ))
3618 return STATUS_OBJECT_PATH_NOT_FOUND
;
3621 if (prefix_len
!= 2 || prefix
[1] != ':')
3624 if (lstat( unix_name
, &st
) == -1 && errno
== ENOENT
)
3629 return STATUS_BAD_DEVICE_TYPE
;
3631 pos
= 0; /* fall back to unix root */
3635 prefix_len
++; /* skip initial backslash */
3636 if (name_len
> prefix_len
&& name
[prefix_len
] == '\\') prefix_len
++; /* allow a second backslash */
3638 name_len
-= prefix_len
;
3640 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, pos
, disposition
, is_unix
);
3641 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3643 TRACE( "%s -> %s\n", debugstr_us(nameW
), debugstr_a(unix_name
) );
3644 *unix_name_ret
= unix_name
;
3648 TRACE( "%s not found in %s\n", debugstr_w(name
), debugstr_an(unix_name
, pos
) );
3655 /******************************************************************************
3656 * nt_to_unix_file_name
3658 * Convert a file name from NT namespace to Unix namespace.
3660 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3661 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3662 * returned, but the unix name is still filled in properly.
3664 NTSTATUS
nt_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char **name_ret
, UINT disposition
)
3666 enum server_fd_type type
;
3667 int old_cwd
, root_fd
, needs_close
;
3670 int name_len
, unix_len
;
3673 if (!attr
->RootDirectory
) /* without root dir fall back to normal lookup */
3674 return nt_to_unix_file_name_no_root( attr
->ObjectName
, name_ret
, disposition
);
3676 name
= attr
->ObjectName
->Buffer
;
3677 name_len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
3679 if (name_len
&& name
[0] == '\\') return STATUS_INVALID_PARAMETER
;
3681 unix_len
= name_len
* 3 + MAX_DIR_ENTRY_LEN
+ 3;
3682 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3685 if (!(status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3687 if (type
!= FD_TYPE_DIR
)
3689 if (needs_close
) close( root_fd
);
3690 status
= STATUS_BAD_DEVICE_TYPE
;
3694 mutex_lock( &dir_mutex
);
3695 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3697 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, 1, disposition
, FALSE
);
3698 if (fchdir( old_cwd
) == -1) chdir( "/" );
3700 else status
= errno_to_status( errno
);
3701 mutex_unlock( &dir_mutex
);
3702 if (old_cwd
!= -1) close( old_cwd
);
3703 if (needs_close
) close( root_fd
);
3706 else if (status
== STATUS_OBJECT_TYPE_MISMATCH
) status
= STATUS_BAD_DEVICE_TYPE
;
3708 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3710 TRACE( "%s -> %s\n", debugstr_us(attr
->ObjectName
), debugstr_a(unix_name
) );
3711 *name_ret
= unix_name
;
3715 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
3722 /******************************************************************************
3723 * wine_nt_to_unix_file_name
3725 * Convert a file name from NT namespace to Unix namespace.
3727 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3728 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3729 * returned, but the unix name is still filled in properly.
3731 NTSTATUS WINAPI
wine_nt_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char *nameA
, ULONG
*size
,
3734 char *buffer
= NULL
;
3736 UNICODE_STRING redir
;
3737 OBJECT_ATTRIBUTES new_attr
= *attr
;
3739 get_redirect( &new_attr
, &redir
);
3740 status
= nt_to_unix_file_name( &new_attr
, &buffer
, disposition
);
3744 struct stat st1
, st2
;
3745 char *name
= buffer
;
3747 /* remove dosdevices prefix for z: drive if it points to the Unix root */
3748 if (!strncmp( buffer
, config_dir
, strlen(config_dir
) ) &&
3749 !strncmp( buffer
+ strlen(config_dir
), "/dosdevices/z:/", 15 ))
3751 char *p
= buffer
+ strlen(config_dir
) + 14;
3753 if (!stat( buffer
, &st1
) && !stat( "/", &st2
) &&
3754 st1
.st_dev
== st2
.st_dev
&& st1
.st_ino
== st2
.st_ino
)
3759 if (*size
> strlen(name
)) strcpy( nameA
, name
);
3760 else status
= STATUS_BUFFER_TOO_SMALL
;
3761 *size
= strlen(name
) + 1;
3764 free( redir
.Buffer
);
3769 /******************************************************************
3772 * Get rid of . and .. components in the path.
3774 static void collapse_path( WCHAR
*path
)
3776 WCHAR
*p
, *start
, *next
;
3778 /* convert every / into a \ */
3779 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
3782 while (*p
&& *p
!= '\\') p
++;
3785 /* collapse duplicate backslashes */
3787 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
3797 case '\\': /* .\ component */
3799 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3801 case 0: /* final . */
3806 if (p
[2] == '\\') /* ..\ component */
3812 while (p
> start
&& p
[-1] != '\\') p
--;
3814 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3817 else if (!p
[2]) /* final .. */
3822 while (p
> start
&& p
[-1] != '\\') p
--;
3831 /* skip to the next component */
3832 while (*p
&& *p
!= '\\') p
++;
3835 /* remove last dot in previous dir name */
3836 if (p
> start
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
3841 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3842 while (p
> start
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
3847 /******************************************************************
3848 * unix_to_nt_file_name
3850 NTSTATUS
unix_to_nt_file_name( const char *name
, WCHAR
**nt
)
3852 static const WCHAR unix_prefixW
[] = {'\\','?','?','\\','u','n','i','x',0};
3853 WCHAR dos_prefixW
[] = {'\\','?','?','\\','A',':','\\',0};
3854 const WCHAR
*prefix
= unix_prefixW
;
3855 unsigned int lenW
, lenA
= strlen(name
);
3856 const char *path
= name
;
3861 status
= find_drive_rootA( &path
, lenA
, &drive
);
3862 lenA
-= path
- name
;
3864 if (status
== STATUS_SUCCESS
)
3866 while (lenA
&& path
[0] == '/') { lenA
--; path
++; }
3867 dos_prefixW
[4] += drive
;
3868 prefix
= dos_prefixW
;
3870 else if (status
!= STATUS_OBJECT_PATH_NOT_FOUND
) return status
;
3872 lenW
= wcslen( prefix
);
3873 if (!(buffer
= malloc( (lenA
+ lenW
+ 1) * sizeof(WCHAR
) ))) return STATUS_NO_MEMORY
;
3874 memcpy( buffer
, prefix
, lenW
* sizeof(WCHAR
) );
3875 lenW
+= ntdll_umbstowcs( path
, lenA
, buffer
+ lenW
, lenA
);
3877 collapse_path( buffer
);
3879 return STATUS_SUCCESS
;
3883 /******************************************************************
3884 * wine_unix_to_nt_file_name
3886 NTSTATUS WINAPI
wine_unix_to_nt_file_name( const char *name
, WCHAR
*buffer
, ULONG
*size
)
3888 WCHAR
*nt_name
= NULL
;
3891 if (name
[0] != '/') return STATUS_INVALID_PARAMETER
; /* relative paths are not supported */
3893 status
= unix_to_nt_file_name( name
, &nt_name
);
3896 if (*size
> wcslen(nt_name
)) wcscpy( buffer
, nt_name
);
3897 else status
= STATUS_BUFFER_TOO_SMALL
;
3898 *size
= wcslen(nt_name
) + 1;
3905 /***********************************************************************
3908 * Simplified version of RtlGetFullPathName_U.
3910 NTSTATUS
get_full_path( const WCHAR
*name
, const WCHAR
*curdir
, WCHAR
**path
)
3912 static const WCHAR uncW
[] = {'\\','?','?','\\','U','N','C','\\',0};
3913 static const WCHAR devW
[] = {'\\','?','?','\\',0};
3914 static const WCHAR unixW
[] = {'u','n','i','x'};
3915 WCHAR
*ret
, root
[] = {'\\','?','?','\\','C',':','\\',0};
3916 NTSTATUS status
= STATUS_SUCCESS
;
3917 const WCHAR
*prefix
;
3919 if (IS_SEPARATOR(name
[0]) && IS_SEPARATOR(name
[1])) /* \\ prefix */
3921 if ((name
[2] == '.' || name
[2] == '?') && IS_SEPARATOR(name
[3])) /* \\?\ device */
3924 if (!wcsnicmp( name
, unixW
, 4 ) && IS_SEPARATOR(name
[4])) /* \\?\unix special name */
3928 unix_name
= malloc( wcslen(name
) * 3 + 1 );
3929 ntdll_wcstoumbs( name
, wcslen(name
) + 1, unix_name
, wcslen(name
) * 3 + 1, FALSE
);
3930 status
= unix_to_nt_file_name( unix_name
, path
);
3936 else prefix
= uncW
; /* UNC path */
3938 else if (IS_SEPARATOR(name
[0])) /* absolute path */
3940 root
[4] = curdir
[4];
3943 else if (name
[0] && name
[1] == ':') /* drive letter */
3945 root
[4] = towupper(name
[0]);
3949 else prefix
= curdir
; /* relative path */
3951 ret
= malloc( (wcslen(prefix
) + wcslen(name
) + 1) * sizeof(WCHAR
) );
3952 wcscpy( ret
, prefix
);
3953 wcscat( ret
, name
);
3954 collapse_path( ret
);
3956 return STATUS_SUCCESS
;
3960 /***********************************************************************
3963 * Unmount the specified device.
3965 static NTSTATUS
unmount_device( HANDLE handle
)
3968 int unix_fd
, needs_close
;
3970 if (!(status
= server_get_unix_fd( handle
, 0, &unix_fd
, &needs_close
, NULL
, NULL
)))
3973 char *mount_point
= NULL
;
3975 if (fstat( unix_fd
, &st
) == -1 || !is_valid_mounted_device( &st
))
3976 status
= STATUS_INVALID_PARAMETER
;
3979 if ((mount_point
= get_device_mount_point( st
.st_rdev
)))
3982 static const char umount
[] = "diskutil unmount >/dev/null 2>&1 ";
3984 static const char umount
[] = "umount >/dev/null 2>&1 ";
3987 if (asprintf( &cmd
, "%s%s", umount
, mount_point
) != -1)
3992 /* umount will fail to release the loop device since we still have
3993 a handle to it, so we release it here */
3994 if (major(st
.st_rdev
) == LOOP_MAJOR
) ioctl( unix_fd
, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3997 free( mount_point
);
4000 if (needs_close
) close( unix_fd
);
4006 /******************************************************************************
4009 * Helper for NtCreateFile that takes a Unix path.
4011 NTSTATUS
open_unix_file( HANDLE
*handle
, const char *unix_name
, ACCESS_MASK access
,
4012 OBJECT_ATTRIBUTES
*attr
, ULONG attributes
, ULONG sharing
, ULONG disposition
,
4013 ULONG options
, void *ea_buffer
, ULONG ea_length
)
4015 struct object_attributes
*objattr
;
4016 unsigned int status
;
4019 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
4021 SERVER_START_REQ( create_file
)
4023 req
->access
= access
;
4024 req
->sharing
= sharing
;
4025 req
->create
= disposition
;
4026 req
->options
= options
;
4027 req
->attrs
= attributes
;
4028 wine_server_add_data( req
, objattr
, len
);
4029 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4030 status
= wine_server_call( req
);
4031 *handle
= wine_server_ptr_handle( reply
->handle
);
4039 /******************************************************************************
4040 * NtCreateFile (NTDLL.@)
4042 NTSTATUS WINAPI
NtCreateFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
4043 IO_STATUS_BLOCK
*io
, LARGE_INTEGER
*alloc_size
,
4044 ULONG attributes
, ULONG sharing
, ULONG disposition
,
4045 ULONG options
, void *ea_buffer
, ULONG ea_length
)
4047 OBJECT_ATTRIBUTES new_attr
;
4048 UNICODE_STRING nt_name
;
4050 BOOL name_hidden
= FALSE
;
4051 BOOL created
= FALSE
;
4052 unsigned int status
;
4054 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
4055 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
4056 handle
, (int)access
, debugstr_us(attr
->ObjectName
), (int)attr
->Attributes
,
4057 attr
->RootDirectory
, attr
->SecurityDescriptor
, io
, alloc_size
,
4058 (int)attributes
, (int)sharing
, (int)disposition
, (int)options
, ea_buffer
, (int)ea_length
);
4061 if (!attr
|| !attr
->ObjectName
) return STATUS_INVALID_PARAMETER
;
4063 if (alloc_size
) FIXME( "alloc_size not supported\n" );
4066 if (options
& FILE_OPEN_BY_FILE_ID
)
4068 status
= file_id_to_unix_file_name( &new_attr
, &unix_name
, &nt_name
);
4069 if (!status
) new_attr
.ObjectName
= &nt_name
;
4073 get_redirect( &new_attr
, &nt_name
);
4074 status
= nt_to_unix_file_name( &new_attr
, &unix_name
, disposition
);
4077 if (status
== STATUS_BAD_DEVICE_TYPE
)
4079 status
= server_open_file_object( handle
, access
, &new_attr
, sharing
, options
);
4080 if (status
== STATUS_SUCCESS
) io
->Information
= FILE_OPENED
;
4081 free( nt_name
.Buffer
);
4082 return io
->Status
= status
;
4085 if (status
== STATUS_NO_SUCH_FILE
&& disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
4088 status
= STATUS_SUCCESS
;
4091 if (status
== STATUS_SUCCESS
)
4093 name_hidden
= is_hidden_file( unix_name
);
4094 status
= open_unix_file( handle
, unix_name
, access
, &new_attr
, attributes
,
4095 sharing
, disposition
, options
, ea_buffer
, ea_length
);
4098 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
4100 if (status
== STATUS_SUCCESS
)
4102 if (created
) io
->Information
= FILE_CREATED
;
4103 else switch(disposition
)
4105 case FILE_SUPERSEDE
:
4106 io
->Information
= FILE_SUPERSEDED
;
4109 io
->Information
= FILE_CREATED
;
4113 io
->Information
= FILE_OPENED
;
4115 case FILE_OVERWRITE
:
4116 case FILE_OVERWRITE_IF
:
4117 io
->Information
= FILE_OVERWRITTEN
;
4121 if (io
->Information
== FILE_CREATED
&&
4122 ((attributes
& XATTR_ATTRIBS_MASK
) || name_hidden
))
4124 int fd
, needs_close
;
4126 /* set any DOS extended attributes */
4127 if (!server_get_unix_fd( *handle
, 0, &fd
, &needs_close
, NULL
, NULL
))
4129 if (fd_set_dos_attrib( fd
, attributes
, TRUE
) == -1 && errno
!= ENOTSUP
)
4130 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB
". errno %d (%s)",
4131 errno
, strerror( errno
) );
4132 if (needs_close
) close( fd
);
4136 else if (status
== STATUS_TOO_MANY_OPENED_FILES
)
4139 if (!once
++) ERR_(winediag
)( "Too many open files, ulimit -n probably needs to be increased\n" );
4142 free( nt_name
.Buffer
);
4143 return io
->Status
= status
;
4147 /******************************************************************************
4148 * NtOpenFile (NTDLL.@)
4150 NTSTATUS WINAPI
NtOpenFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
4151 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG options
)
4153 return NtCreateFile( handle
, access
, attr
, io
, NULL
, 0, sharing
, FILE_OPEN
, options
, NULL
, 0 );
4157 /******************************************************************************
4158 * NtCreateMailslotFile (NTDLL.@)
4160 NTSTATUS WINAPI
NtCreateMailslotFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
4161 IO_STATUS_BLOCK
*io
, ULONG options
, ULONG quota
, ULONG msg_size
,
4162 LARGE_INTEGER
*timeout
)
4164 unsigned int status
;
4166 struct object_attributes
*objattr
;
4168 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
4169 handle
, (int)access
, attr
, io
, (int)options
, (int)quota
, (int)msg_size
, timeout
);
4172 if (!attr
) return STATUS_INVALID_PARAMETER
;
4173 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
4175 SERVER_START_REQ( create_mailslot
)
4177 req
->access
= access
;
4178 req
->max_msgsize
= msg_size
;
4179 req
->read_timeout
= timeout
? timeout
->QuadPart
: -1;
4180 wine_server_add_data( req
, objattr
, len
);
4181 if (!(status
= wine_server_call( req
))) *handle
= wine_server_ptr_handle( reply
->handle
);
4190 /******************************************************************
4191 * NtCreateNamedPipeFile (NTDLL.@)
4193 NTSTATUS WINAPI
NtCreateNamedPipeFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
4194 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG dispo
, ULONG options
,
4195 ULONG pipe_type
, ULONG read_mode
, ULONG completion_mode
,
4196 ULONG max_inst
, ULONG inbound_quota
, ULONG outbound_quota
,
4197 LARGE_INTEGER
*timeout
)
4199 unsigned int status
;
4201 struct object_attributes
*objattr
;
4204 if (!attr
) return STATUS_INVALID_PARAMETER
;
4206 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
4207 handle
, (int)access
, debugstr_us(attr
->ObjectName
), io
, (int)sharing
, (int)dispo
,
4208 (int)options
, (int)pipe_type
, (int)read_mode
, (int)completion_mode
, (int)max_inst
,
4209 (int)inbound_quota
, (int)outbound_quota
, timeout
);
4211 /* assume we only get relative timeout */
4212 if (timeout
&& timeout
->QuadPart
> 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout
->QuadPart
) );
4214 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
4216 SERVER_START_REQ( create_named_pipe
)
4218 req
->access
= access
;
4219 req
->options
= options
;
4220 req
->sharing
= sharing
;
4222 (pipe_type
? NAMED_PIPE_MESSAGE_STREAM_WRITE
: 0) |
4223 (read_mode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0) |
4224 (completion_mode
? NAMED_PIPE_NONBLOCKING_MODE
: 0);
4225 req
->disposition
= dispo
;
4226 req
->maxinstances
= max_inst
;
4227 req
->outsize
= outbound_quota
;
4228 req
->insize
= inbound_quota
;
4229 req
->timeout
= timeout
? timeout
->QuadPart
: 0ULL;
4230 wine_server_add_data( req
, objattr
, len
);
4231 if (!(status
= wine_server_call( req
)))
4233 *handle
= wine_server_ptr_handle( reply
->handle
);
4234 io
->Information
= reply
->created
? FILE_CREATED
: FILE_OPENED
;
4240 return io
->Status
= status
;
4244 /******************************************************************
4245 * NtDeleteFile (NTDLL.@)
4247 NTSTATUS WINAPI
NtDeleteFile( OBJECT_ATTRIBUTES
*attr
)
4252 UNICODE_STRING nt_name
;
4253 OBJECT_ATTRIBUTES new_attr
= *attr
;
4255 get_redirect( &new_attr
, &nt_name
);
4256 if (!(status
= nt_to_unix_file_name( &new_attr
, &unix_name
, FILE_OPEN
)))
4258 if (!(status
= open_unix_file( &handle
, unix_name
, GENERIC_READ
| GENERIC_WRITE
| DELETE
, &new_attr
,
4259 0, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, FILE_OPEN
,
4260 FILE_DELETE_ON_CLOSE
, NULL
, 0 )))
4264 free( nt_name
.Buffer
);
4269 /******************************************************************************
4270 * NtQueryFullAttributesFile (NTDLL.@)
4272 NTSTATUS WINAPI
NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES
*attr
,
4273 FILE_NETWORK_OPEN_INFORMATION
*info
)
4276 unsigned int status
;
4277 UNICODE_STRING redir
;
4278 OBJECT_ATTRIBUTES new_attr
= *attr
;
4280 get_redirect( &new_attr
, &redir
);
4281 if (!(status
= nt_to_unix_file_name( &new_attr
, &unix_name
, FILE_OPEN
)))
4286 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4287 status
= errno_to_status( errno
);
4288 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4289 status
= STATUS_INVALID_INFO_CLASS
;
4292 FILE_BASIC_INFORMATION basic
;
4293 FILE_STANDARD_INFORMATION std
;
4295 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
4296 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
4298 info
->CreationTime
= basic
.CreationTime
;
4299 info
->LastAccessTime
= basic
.LastAccessTime
;
4300 info
->LastWriteTime
= basic
.LastWriteTime
;
4301 info
->ChangeTime
= basic
.ChangeTime
;
4302 info
->AllocationSize
= std
.AllocationSize
;
4303 info
->EndOfFile
= std
.EndOfFile
;
4304 info
->FileAttributes
= basic
.FileAttributes
;
4308 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
4309 free( redir
.Buffer
);
4314 /******************************************************************************
4315 * NtQueryAttributesFile (NTDLL.@)
4317 NTSTATUS WINAPI
NtQueryAttributesFile( const OBJECT_ATTRIBUTES
*attr
, FILE_BASIC_INFORMATION
*info
)
4320 unsigned int status
;
4321 UNICODE_STRING redir
;
4322 OBJECT_ATTRIBUTES new_attr
= *attr
;
4324 get_redirect( &new_attr
, &redir
);
4325 if (!(status
= nt_to_unix_file_name( &new_attr
, &unix_name
, FILE_OPEN
)))
4330 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4331 status
= errno_to_status( errno
);
4332 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4333 status
= STATUS_INVALID_INFO_CLASS
;
4335 status
= fill_file_info( &st
, attributes
, info
, FileBasicInformation
);
4338 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
4339 free( redir
.Buffer
);
4344 /******************************************************************************
4345 * NtQueryInformationFile (NTDLL.@)
4347 NTSTATUS WINAPI
NtQueryInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
4348 void *ptr
, ULONG len
, FILE_INFORMATION_CLASS
class )
4350 static const size_t info_sizes
[FileMaximumInformation
] =
4353 sizeof(FILE_DIRECTORY_INFORMATION
), /* FileDirectoryInformation */
4354 sizeof(FILE_FULL_DIRECTORY_INFORMATION
), /* FileFullDirectoryInformation */
4355 sizeof(FILE_BOTH_DIRECTORY_INFORMATION
), /* FileBothDirectoryInformation */
4356 sizeof(FILE_BASIC_INFORMATION
), /* FileBasicInformation */
4357 sizeof(FILE_STANDARD_INFORMATION
), /* FileStandardInformation */
4358 sizeof(FILE_INTERNAL_INFORMATION
), /* FileInternalInformation */
4359 sizeof(FILE_EA_INFORMATION
), /* FileEaInformation */
4360 0, /* FileAccessInformation */
4361 sizeof(FILE_NAME_INFORMATION
), /* FileNameInformation */
4362 sizeof(FILE_RENAME_INFORMATION
)-sizeof(WCHAR
), /* FileRenameInformation */
4363 0, /* FileLinkInformation */
4364 sizeof(FILE_NAMES_INFORMATION
)-sizeof(WCHAR
), /* FileNamesInformation */
4365 sizeof(FILE_DISPOSITION_INFORMATION
), /* FileDispositionInformation */
4366 sizeof(FILE_POSITION_INFORMATION
), /* FilePositionInformation */
4367 sizeof(FILE_FULL_EA_INFORMATION
), /* FileFullEaInformation */
4368 0, /* FileModeInformation */
4369 sizeof(FILE_ALIGNMENT_INFORMATION
), /* FileAlignmentInformation */
4370 sizeof(FILE_ALL_INFORMATION
), /* FileAllInformation */
4371 sizeof(FILE_ALLOCATION_INFORMATION
), /* FileAllocationInformation */
4372 sizeof(FILE_END_OF_FILE_INFORMATION
), /* FileEndOfFileInformation */
4373 0, /* FileAlternateNameInformation */
4374 sizeof(FILE_STREAM_INFORMATION
)-sizeof(WCHAR
), /* FileStreamInformation */
4375 sizeof(FILE_PIPE_INFORMATION
), /* FilePipeInformation */
4376 sizeof(FILE_PIPE_LOCAL_INFORMATION
), /* FilePipeLocalInformation */
4377 0, /* FilePipeRemoteInformation */
4378 sizeof(FILE_MAILSLOT_QUERY_INFORMATION
), /* FileMailslotQueryInformation */
4379 0, /* FileMailslotSetInformation */
4380 0, /* FileCompressionInformation */
4381 0, /* FileObjectIdInformation */
4382 0, /* FileCompletionInformation */
4383 0, /* FileMoveClusterInformation */
4384 0, /* FileQuotaInformation */
4385 0, /* FileReparsePointInformation */
4386 sizeof(FILE_NETWORK_OPEN_INFORMATION
), /* FileNetworkOpenInformation */
4387 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
), /* FileAttributeTagInformation */
4388 0, /* FileTrackingInformation */
4389 0, /* FileIdBothDirectoryInformation */
4390 0, /* FileIdFullDirectoryInformation */
4391 0, /* FileValidDataLengthInformation */
4392 0, /* FileShortNameInformation */
4393 0, /* FileIoCompletionNotificationInformation, */
4394 0, /* FileIoStatusBlockRangeInformation */
4395 0, /* FileIoPriorityHintInformation */
4396 0, /* FileSfioReserveInformation */
4397 0, /* FileSfioVolumeInformation */
4398 0, /* FileHardLinkInformation */
4399 0, /* FileProcessIdsUsingFileInformation */
4400 0, /* FileNormalizedNameInformation */
4401 0, /* FileNetworkPhysicalNameInformation */
4402 0, /* FileIdGlobalTxDirectoryInformation */
4403 0, /* FileIsRemoteDeviceInformation */
4404 0, /* FileAttributeCacheInformation */
4405 0, /* FileNumaNodeInformation */
4406 0, /* FileStandardLinkInformation */
4407 0, /* FileRemoteProtocolInformation */
4408 0, /* FileRenameInformationBypassAccessCheck */
4409 0, /* FileLinkInformationBypassAccessCheck */
4410 0, /* FileVolumeNameInformation */
4411 sizeof(FILE_ID_INFORMATION
), /* FileIdInformation */
4412 0, /* FileIdExtdDirectoryInformation */
4413 0, /* FileReplaceCompletionInformation */
4414 0, /* FileHardLinkFullIdInformation */
4415 0, /* FileIdExtdBothDirectoryInformation */
4416 0, /* FileDispositionInformationEx */
4417 0, /* FileRenameInformationEx */
4418 0, /* FileRenameInformationExBypassAccessCheck */
4419 0, /* FileDesiredStorageClassInformation */
4420 sizeof(FILE_STAT_INFORMATION
), /* FileStatInformation */
4421 0, /* FileMemoryPartitionInformation */
4422 0, /* FileStatLxInformation */
4423 0, /* FileCaseSensitiveInformation */
4424 0, /* FileLinkInformationEx */
4425 0, /* FileLinkInformationExBypassAccessCheck */
4426 0, /* FileStorageReserveIdInformation */
4427 0, /* FileCaseSensitiveInformationForceAccessCheck */
4428 0, /* FileKnownFolderInformation */
4432 int fd
, needs_close
= FALSE
;
4434 unsigned int options
;
4435 unsigned int status
;
4437 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, (int)len
, class);
4439 io
->Information
= 0;
4441 if (class <= 0 || class >= FileMaximumInformation
)
4442 return io
->Status
= STATUS_INVALID_INFO_CLASS
;
4443 if (!info_sizes
[class])
4444 return server_get_file_info( handle
, io
, ptr
, len
, class );
4445 if (len
< info_sizes
[class])
4446 return io
->Status
= STATUS_INFO_LENGTH_MISMATCH
;
4448 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, &options
)))
4450 if (status
!= STATUS_BAD_DEVICE_TYPE
) return io
->Status
= status
;
4451 return server_get_file_info( handle
, io
, ptr
, len
, class );
4456 case FileBasicInformation
:
4457 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1)
4458 status
= errno_to_status( errno
);
4459 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4460 status
= STATUS_INVALID_INFO_CLASS
;
4462 fill_file_info( &st
, attr
, ptr
, class );
4464 case FileStandardInformation
:
4466 FILE_STANDARD_INFORMATION
*info
= ptr
;
4468 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4471 fill_file_info( &st
, attr
, info
, class );
4472 info
->DeletePending
= FALSE
; /* FIXME */
4476 case FilePositionInformation
:
4478 FILE_POSITION_INFORMATION
*info
= ptr
;
4479 off_t res
= lseek( fd
, 0, SEEK_CUR
);
4480 if (res
== (off_t
)-1) status
= errno_to_status( errno
);
4481 else info
->CurrentByteOffset
.QuadPart
= res
;
4484 case FileInternalInformation
:
4485 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4486 else fill_file_info( &st
, attr
, ptr
, class );
4488 case FileEaInformation
:
4490 FILE_EA_INFORMATION
*info
= ptr
;
4494 case FileEndOfFileInformation
:
4495 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4496 else fill_file_info( &st
, attr
, ptr
, class );
4498 case FileAllInformation
:
4500 FILE_ALL_INFORMATION
*info
= ptr
;
4503 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4504 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4505 status
= STATUS_INVALID_INFO_CLASS
;
4506 else if (!(status
= server_get_unix_name( handle
, &unix_name
)))
4508 LONG name_len
= len
- FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
);
4510 fill_file_info( &st
, attr
, info
, FileAllInformation
);
4511 info
->StandardInformation
.DeletePending
= FALSE
; /* FIXME */
4512 info
->EaInformation
.EaSize
= 0;
4513 info
->AccessInformation
.AccessFlags
= 0; /* FIXME */
4514 info
->PositionInformation
.CurrentByteOffset
.QuadPart
= lseek( fd
, 0, SEEK_CUR
);
4515 info
->ModeInformation
.Mode
= 0; /* FIXME */
4516 info
->AlignmentInformation
.AlignmentRequirement
= 1; /* FIXME */
4518 status
= fill_name_info( unix_name
, &info
->NameInformation
, &name_len
);
4520 io
->Information
= FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
) + name_len
;
4524 case FileMailslotQueryInformation
:
4526 FILE_MAILSLOT_QUERY_INFORMATION
*info
= ptr
;
4528 SERVER_START_REQ( set_mailslot_info
)
4530 req
->handle
= wine_server_obj_handle( handle
);
4532 status
= wine_server_call( req
);
4533 if (status
== STATUS_SUCCESS
)
4535 info
->MaximumMessageSize
= reply
->max_msgsize
;
4536 info
->MailslotQuota
= 0;
4537 info
->NextMessageSize
= 0;
4538 info
->MessagesAvailable
= 0;
4539 info
->ReadTimeout
.QuadPart
= reply
->read_timeout
;
4546 ULONG size
= info
->MaximumMessageSize
? info
->MaximumMessageSize
: 0x10000;
4547 if (size
> 0x10000) size
= 0x10000;
4548 if ((tmpbuf
= malloc( size
)))
4550 if (needs_close
) close( fd
);
4551 if (!server_get_unix_fd( handle
, FILE_READ_DATA
, &fd
, &needs_close
, NULL
, NULL
))
4553 int res
= recv( fd
, tmpbuf
, size
, MSG_PEEK
);
4554 info
->MessagesAvailable
= (res
> 0);
4555 info
->NextMessageSize
= (res
>= 0) ? res
: MAILSLOT_NO_MESSAGE
;
4562 case FileNameInformation
:
4564 FILE_NAME_INFORMATION
*info
= ptr
;
4567 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
4569 LONG name_len
= len
- FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
);
4570 status
= fill_name_info( unix_name
, info
, &name_len
);
4572 io
->Information
= FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
) + name_len
;
4576 case FileNetworkOpenInformation
:
4578 FILE_NETWORK_OPEN_INFORMATION
*info
= ptr
;
4581 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
4586 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4587 status
= errno_to_status( errno
);
4588 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4589 status
= STATUS_INVALID_INFO_CLASS
;
4592 FILE_BASIC_INFORMATION basic
;
4593 FILE_STANDARD_INFORMATION std
;
4595 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
4596 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
4598 info
->CreationTime
= basic
.CreationTime
;
4599 info
->LastAccessTime
= basic
.LastAccessTime
;
4600 info
->LastWriteTime
= basic
.LastWriteTime
;
4601 info
->ChangeTime
= basic
.ChangeTime
;
4602 info
->AllocationSize
= std
.AllocationSize
;
4603 info
->EndOfFile
= std
.EndOfFile
;
4604 info
->FileAttributes
= basic
.FileAttributes
;
4610 case FileIdInformation
:
4611 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4614 struct mountmgr_unix_drive drive
;
4615 FILE_ID_INFORMATION
*info
= ptr
;
4617 info
->VolumeSerialNumber
= 0;
4618 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) ))
4619 info
->VolumeSerialNumber
= drive
.serial
;
4620 memset( &info
->FileId
, 0, sizeof(info
->FileId
) );
4621 *(ULONGLONG
*)&info
->FileId
= st
.st_ino
;
4624 case FileAttributeTagInformation
:
4625 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4628 FILE_ATTRIBUTE_TAG_INFORMATION
*info
= ptr
;
4629 info
->FileAttributes
= attr
;
4630 info
->ReparseTag
= 0; /* FIXME */
4631 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, &st
))
4632 info
->ReparseTag
= IO_REPARSE_TAG_MOUNT_POINT
;
4635 case FileStatInformation
:
4636 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4637 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4638 status
= STATUS_INVALID_INFO_CLASS
;
4641 FILE_STAT_INFORMATION
*info
= ptr
;
4642 FILE_BASIC_INFORMATION basic
;
4643 FILE_STANDARD_INFORMATION std
;
4645 fill_file_info( &st
, attr
, &basic
, FileBasicInformation
);
4646 fill_file_info( &st
, attr
, &std
, FileStandardInformation
);
4648 info
->FileId
.QuadPart
= st
.st_ino
;
4649 info
->CreationTime
= basic
.CreationTime
;
4650 info
->LastAccessTime
= basic
.LastAccessTime
;
4651 info
->LastWriteTime
= basic
.LastWriteTime
;
4652 info
->ChangeTime
= basic
.ChangeTime
;
4653 info
->AllocationSize
= std
.AllocationSize
;
4654 info
->EndOfFile
= std
.EndOfFile
;
4655 info
->FileAttributes
= attr
;
4656 info
->ReparseTag
= 0; /* FIXME */
4657 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, &st
))
4658 info
->ReparseTag
= IO_REPARSE_TAG_MOUNT_POINT
;
4659 info
->NumberOfLinks
= std
.NumberOfLinks
;
4660 info
->EffectiveAccess
= FILE_ALL_ACCESS
; /* FIXME */
4664 FIXME("Unsupported class (%d)\n", class);
4665 status
= STATUS_NOT_IMPLEMENTED
;
4668 if (needs_close
) close( fd
);
4669 if (status
== STATUS_SUCCESS
&& !io
->Information
) io
->Information
= info_sizes
[class];
4670 return io
->Status
= status
;
4674 /******************************************************************************
4675 * NtSetInformationFile (NTDLL.@)
4677 NTSTATUS WINAPI
NtSetInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
4678 void *ptr
, ULONG len
, FILE_INFORMATION_CLASS
class )
4680 int fd
, needs_close
;
4681 unsigned int status
= STATUS_SUCCESS
;
4683 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, (int)len
, class );
4687 case FileBasicInformation
:
4688 if (len
>= sizeof(FILE_BASIC_INFORMATION
))
4690 const FILE_BASIC_INFORMATION
*info
= ptr
;
4691 LARGE_INTEGER mtime
, atime
;
4694 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4695 return io
->Status
= status
;
4697 if (server_get_unix_name( handle
, &unix_name
)) unix_name
= NULL
;
4699 mtime
.QuadPart
= info
->LastWriteTime
.QuadPart
== -1 ? 0 : info
->LastWriteTime
.QuadPart
;
4700 atime
.QuadPart
= info
->LastAccessTime
.QuadPart
== -1 ? 0 : info
->LastAccessTime
.QuadPart
;
4702 if (atime
.QuadPart
|| mtime
.QuadPart
)
4703 status
= set_file_times( fd
, &mtime
, &atime
);
4705 if (status
== STATUS_SUCCESS
)
4706 status
= fd_set_file_info( fd
, info
->FileAttributes
,
4707 unix_name
&& is_hidden_file( unix_name
));
4709 if (needs_close
) close( fd
);
4712 else status
= STATUS_INVALID_PARAMETER_3
;
4715 case FilePositionInformation
:
4716 if (len
>= sizeof(FILE_POSITION_INFORMATION
))
4718 const FILE_POSITION_INFORMATION
*info
= ptr
;
4720 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4721 return io
->Status
= status
;
4723 if (lseek( fd
, info
->CurrentByteOffset
.QuadPart
, SEEK_SET
) == (off_t
)-1)
4724 status
= errno_to_status( errno
);
4726 if (needs_close
) close( fd
);
4728 else status
= STATUS_INVALID_PARAMETER_3
;
4731 case FileEndOfFileInformation
:
4732 if (len
>= sizeof(FILE_END_OF_FILE_INFORMATION
))
4734 const FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
4736 SERVER_START_REQ( set_fd_eof_info
)
4738 req
->handle
= wine_server_obj_handle( handle
);
4739 req
->eof
= info
->EndOfFile
.QuadPart
;
4740 status
= wine_server_call( req
);
4744 else status
= STATUS_INVALID_PARAMETER_3
;
4747 case FilePipeInformation
:
4748 if (len
>= sizeof(FILE_PIPE_INFORMATION
))
4750 FILE_PIPE_INFORMATION
*info
= ptr
;
4752 if ((info
->CompletionMode
| info
->ReadMode
) & ~1)
4754 status
= STATUS_INVALID_PARAMETER
;
4758 SERVER_START_REQ( set_named_pipe_info
)
4760 req
->handle
= wine_server_obj_handle( handle
);
4761 req
->flags
= (info
->CompletionMode
? NAMED_PIPE_NONBLOCKING_MODE
: 0) |
4762 (info
->ReadMode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0);
4763 status
= wine_server_call( req
);
4767 else status
= STATUS_INVALID_PARAMETER_3
;
4770 case FileMailslotSetInformation
:
4772 FILE_MAILSLOT_SET_INFORMATION
*info
= ptr
;
4774 SERVER_START_REQ( set_mailslot_info
)
4776 req
->handle
= wine_server_obj_handle( handle
);
4777 req
->flags
= MAILSLOT_SET_READ_TIMEOUT
;
4778 req
->read_timeout
= info
->ReadTimeout
.QuadPart
;
4779 status
= wine_server_call( req
);
4785 case FileCompletionInformation
:
4786 if (len
>= sizeof(FILE_COMPLETION_INFORMATION
))
4788 FILE_COMPLETION_INFORMATION
*info
= ptr
;
4790 SERVER_START_REQ( set_completion_info
)
4792 req
->handle
= wine_server_obj_handle( handle
);
4793 req
->chandle
= wine_server_obj_handle( info
->CompletionPort
);
4794 req
->ckey
= info
->CompletionKey
;
4795 status
= wine_server_call( req
);
4799 else status
= STATUS_INVALID_PARAMETER_3
;
4802 case FileIoCompletionNotificationInformation
:
4803 if (len
>= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
))
4805 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
*info
= ptr
;
4807 if (info
->Flags
& FILE_SKIP_SET_USER_EVENT_ON_FAST_IO
)
4808 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4810 SERVER_START_REQ( set_fd_completion_mode
)
4812 req
->handle
= wine_server_obj_handle( handle
);
4813 req
->flags
= info
->Flags
;
4814 status
= wine_server_call( req
);
4818 else status
= STATUS_INFO_LENGTH_MISMATCH
;
4821 case FileIoPriorityHintInformation
:
4822 if (len
>= sizeof(FILE_IO_PRIORITY_HINT_INFO
))
4824 FILE_IO_PRIORITY_HINT_INFO
*info
= ptr
;
4825 if (info
->PriorityHint
< MaximumIoPriorityHintType
)
4826 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info
->PriorityHint
);
4828 status
= STATUS_INVALID_PARAMETER
;
4830 else status
= STATUS_INFO_LENGTH_MISMATCH
;
4833 case FileAllInformation
:
4834 status
= STATUS_INVALID_INFO_CLASS
;
4837 case FileValidDataLengthInformation
:
4838 if (len
>= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
))
4841 const FILE_VALID_DATA_LENGTH_INFORMATION
*info
= ptr
;
4843 if ((status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, NULL
, NULL
)))
4844 return io
->Status
= status
;
4846 if (fstat( fd
, &st
) == -1) status
= errno_to_status( errno
);
4847 else if (info
->ValidDataLength
.QuadPart
<= 0 || (off_t
)info
->ValidDataLength
.QuadPart
> st
.st_size
)
4848 status
= STATUS_INVALID_PARAMETER
;
4851 #ifdef HAVE_POSIX_FALLOCATE
4853 if ((err
= posix_fallocate( fd
, 0, (off_t
)info
->ValidDataLength
.QuadPart
)) != 0)
4855 if (err
== EOPNOTSUPP
) WARN( "posix_fallocate not supported on this filesystem\n" );
4856 else status
= errno_to_status( err
);
4859 WARN( "setting valid data length not supported\n" );
4862 if (needs_close
) close( fd
);
4864 else status
= STATUS_INVALID_PARAMETER_3
;
4867 case FileDispositionInformation
:
4868 if (len
>= sizeof(FILE_DISPOSITION_INFORMATION
))
4870 FILE_DISPOSITION_INFORMATION
*info
= ptr
;
4872 SERVER_START_REQ( set_fd_disp_info
)
4874 req
->handle
= wine_server_obj_handle( handle
);
4875 req
->flags
= info
->DoDeleteFile
? FILE_DISPOSITION_DELETE
: FILE_DISPOSITION_DO_NOT_DELETE
;
4876 status
= wine_server_call( req
);
4880 else status
= STATUS_INVALID_PARAMETER_3
;
4883 case FileDispositionInformationEx
:
4884 if (len
>= sizeof(FILE_DISPOSITION_INFORMATION_EX
))
4886 FILE_DISPOSITION_INFORMATION_EX
*info
= ptr
;
4888 if (info
->Flags
& FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK
)
4889 FIXME( "FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK not supported\n" );
4891 SERVER_START_REQ( set_fd_disp_info
)
4893 req
->handle
= wine_server_obj_handle( handle
);
4894 req
->flags
= info
->Flags
;
4895 status
= wine_server_call( req
);
4899 else status
= STATUS_INVALID_PARAMETER_3
;
4902 case FileRenameInformation
:
4903 case FileRenameInformationEx
:
4904 if (len
>= sizeof(FILE_RENAME_INFORMATION
))
4906 FILE_RENAME_INFORMATION
*info
= ptr
;
4908 UNICODE_STRING name_str
, redir
;
4909 OBJECT_ATTRIBUTES attr
;
4912 if (class == FileRenameInformation
)
4913 flags
= info
->ReplaceIfExists
? FILE_RENAME_REPLACE_IF_EXISTS
: 0;
4915 flags
= info
->Flags
;
4917 if (flags
& ~(FILE_RENAME_REPLACE_IF_EXISTS
| FILE_RENAME_IGNORE_READONLY_ATTRIBUTE
))
4918 FIXME( "unsupported flags: %#x\n", flags
);
4920 name_str
.Buffer
= info
->FileName
;
4921 name_str
.Length
= info
->FileNameLength
;
4922 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4923 InitializeObjectAttributes( &attr
, &name_str
, OBJ_CASE_INSENSITIVE
, info
->RootDirectory
, NULL
);
4924 get_redirect( &attr
, &redir
);
4926 status
= nt_to_unix_file_name( &attr
, &unix_name
, FILE_OPEN_IF
);
4927 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
4929 SERVER_START_REQ( set_fd_name_info
)
4931 req
->handle
= wine_server_obj_handle( handle
);
4932 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4933 req
->namelen
= attr
.ObjectName
->Length
;
4936 wine_server_add_data( req
, attr
.ObjectName
->Buffer
, attr
.ObjectName
->Length
);
4937 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4938 status
= wine_server_call( req
);
4944 free( redir
.Buffer
);
4946 else status
= STATUS_INVALID_PARAMETER_3
;
4949 case FileLinkInformation
:
4950 case FileLinkInformationEx
:
4951 if (len
>= sizeof(FILE_LINK_INFORMATION
))
4953 FILE_LINK_INFORMATION
*info
= ptr
;
4955 UNICODE_STRING name_str
, redir
;
4956 OBJECT_ATTRIBUTES attr
;
4959 if (class == FileLinkInformation
)
4960 flags
= info
->ReplaceIfExists
? FILE_LINK_REPLACE_IF_EXISTS
: 0;
4962 flags
= info
->Flags
;
4964 if (flags
& ~(FILE_LINK_REPLACE_IF_EXISTS
| FILE_LINK_IGNORE_READONLY_ATTRIBUTE
))
4965 FIXME( "unsupported flags: %#x\n", flags
);
4967 name_str
.Buffer
= info
->FileName
;
4968 name_str
.Length
= info
->FileNameLength
;
4969 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4970 InitializeObjectAttributes( &attr
, &name_str
, OBJ_CASE_INSENSITIVE
, info
->RootDirectory
, NULL
);
4971 get_redirect( &attr
, &redir
);
4973 status
= nt_to_unix_file_name( &attr
, &unix_name
, FILE_OPEN_IF
);
4974 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
4976 SERVER_START_REQ( set_fd_name_info
)
4978 req
->handle
= wine_server_obj_handle( handle
);
4979 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4980 req
->namelen
= attr
.ObjectName
->Length
;
4983 wine_server_add_data( req
, attr
.ObjectName
->Buffer
, attr
.ObjectName
->Length
);
4984 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4985 status
= wine_server_call( req
);
4991 free( redir
.Buffer
);
4993 else status
= STATUS_INVALID_PARAMETER_3
;
4997 FIXME("Unsupported class (%d)\n", class);
4998 status
= STATUS_NOT_IMPLEMENTED
;
5001 io
->Information
= 0;
5002 return io
->Status
= status
;
5006 /***********************************************************************
5007 * Asynchronous file I/O *
5010 struct async_fileio_read
5012 struct async_fileio io
;
5014 unsigned int already
;
5019 struct async_fileio_write
5021 struct async_fileio io
;
5023 unsigned int already
;
5027 struct async_fileio_read_changes
5029 struct async_fileio io
;
5038 struct async_fileio io
;
5039 void *buffer
; /* buffer for output */
5040 ULONG size
; /* size of buffer */
5043 static struct async_fileio
*fileio_freelist
;
5045 void release_fileio( struct async_fileio
*io
)
5049 struct async_fileio
*next
= fileio_freelist
;
5051 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist
, io
, next
) == next
) return;
5055 struct async_fileio
*alloc_fileio( DWORD size
, async_callback_t callback
, HANDLE handle
)
5057 /* first free remaining previous fileinfos */
5058 struct async_fileio
*io
= InterlockedExchangePointer( (void **)&fileio_freelist
, NULL
);
5062 struct async_fileio
*next
= io
->next
;
5067 if ((io
= malloc( size
)))
5069 io
->callback
= callback
;
5070 io
->handle
= handle
;
5075 /* callback for irp async I/O completion */
5076 static BOOL
irp_completion( void *user
, ULONG_PTR
*info
, unsigned int *status
)
5078 struct async_irp
*async
= user
;
5080 if (*status
== STATUS_ALERTED
)
5082 SERVER_START_REQ( get_async_result
)
5084 req
->user_arg
= wine_server_client_ptr( async
);
5085 wine_server_set_reply( req
, async
->buffer
, async
->size
);
5086 *status
= virtual_locked_server_call( req
);
5090 release_fileio( &async
->io
);
5094 static BOOL
async_read_proc( void *user
, ULONG_PTR
*info
, unsigned int *status
)
5096 struct async_fileio_read
*fileio
= user
;
5097 int fd
, needs_close
, result
;
5101 case STATUS_ALERTED
: /* got some new data */
5102 /* check to see if the data is ready (non-blocking) */
5103 if ((*status
= server_get_unix_fd( fileio
->io
.handle
, FILE_READ_DATA
, &fd
,
5104 &needs_close
, NULL
, NULL
)))
5107 result
= virtual_locked_read(fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
-fileio
->already
);
5108 if (needs_close
) close( fd
);
5112 if (errno
== EAGAIN
|| errno
== EINTR
)
5115 /* check to see if the transfer is complete */
5116 *status
= errno_to_status( errno
);
5118 else if (result
== 0)
5120 *status
= fileio
->already
? STATUS_SUCCESS
: STATUS_PIPE_BROKEN
;
5124 fileio
->already
+= result
;
5126 if (fileio
->already
< fileio
->count
&& !fileio
->avail_mode
)
5129 *status
= STATUS_SUCCESS
;
5133 case STATUS_TIMEOUT
:
5134 case STATUS_IO_TIMEOUT
:
5135 if (fileio
->already
) *status
= STATUS_SUCCESS
;
5139 *info
= fileio
->already
;
5140 release_fileio( &fileio
->io
);
5144 static BOOL
async_write_proc( void *user
, ULONG_PTR
*info
, unsigned int *status
)
5146 struct async_fileio_write
*fileio
= user
;
5147 int result
, fd
, needs_close
;
5148 enum server_fd_type type
;
5152 case STATUS_ALERTED
:
5153 /* write some data (non-blocking) */
5154 if ((*status
= server_get_unix_fd( fileio
->io
.handle
, FILE_WRITE_DATA
, &fd
,
5155 &needs_close
, &type
, NULL
)))
5158 if (!fileio
->count
&& type
== FD_TYPE_MAILSLOT
)
5159 result
= send( fd
, fileio
->buffer
, 0, 0 );
5161 result
= write( fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
- fileio
->already
);
5163 if (needs_close
) close( fd
);
5167 if (errno
== EAGAIN
|| errno
== EINTR
) return FALSE
;
5168 *status
= errno_to_status( errno
);
5172 fileio
->already
+= result
;
5173 if (fileio
->already
< fileio
->count
) return FALSE
;
5174 *status
= STATUS_SUCCESS
;
5178 case STATUS_TIMEOUT
:
5179 case STATUS_IO_TIMEOUT
:
5180 if (fileio
->already
) *status
= STATUS_SUCCESS
;
5184 *info
= fileio
->already
;
5185 release_fileio( &fileio
->io
);
5189 static void set_sync_iosb( IO_STATUS_BLOCK
*io
, NTSTATUS status
, ULONG_PTR info
, unsigned int options
)
5191 if (in_wow64_call() && !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)))
5193 IO_STATUS_BLOCK32
*io32
= io
->Pointer
;
5195 io32
->Status
= status
;
5196 io32
->Information
= info
;
5200 io
->Status
= status
;
5201 io
->Information
= info
;
5205 /* do a read call through the server */
5206 static unsigned int server_read_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5207 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG size
,
5208 LARGE_INTEGER
*offset
, ULONG
*key
)
5210 struct async_irp
*async
;
5211 unsigned int status
;
5215 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
5216 return STATUS_NO_MEMORY
;
5218 async
->buffer
= buffer
;
5221 SERVER_START_REQ( read
)
5223 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, iosb_client_ptr(io
) );
5224 req
->pos
= offset
? offset
->QuadPart
: 0;
5225 wine_server_set_reply( req
, buffer
, size
);
5226 status
= virtual_locked_server_call( req
);
5227 wait_handle
= wine_server_ptr_handle( reply
->wait
);
5228 options
= reply
->options
;
5229 if (wait_handle
&& status
!= STATUS_PENDING
)
5230 set_sync_iosb( io
, status
, wine_server_reply_size( reply
), options
);
5234 if (status
!= STATUS_PENDING
) free( async
);
5236 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
5240 /* do a write call through the server */
5241 static unsigned int server_write_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5242 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG size
,
5243 LARGE_INTEGER
*offset
, ULONG
*key
)
5245 struct async_irp
*async
;
5246 unsigned int status
;
5250 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
5251 return STATUS_NO_MEMORY
;
5253 async
->buffer
= NULL
;
5256 SERVER_START_REQ( write
)
5258 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, iosb_client_ptr(io
) );
5259 req
->pos
= offset
? offset
->QuadPart
: 0;
5260 wine_server_add_data( req
, buffer
, size
);
5261 status
= wine_server_call( req
);
5262 wait_handle
= wine_server_ptr_handle( reply
->wait
);
5263 options
= reply
->options
;
5264 if (wait_handle
&& status
!= STATUS_PENDING
)
5265 set_sync_iosb( io
, status
, reply
->size
, options
);
5269 if (status
!= STATUS_PENDING
) free( async
);
5271 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
5275 /* do an ioctl call through the server */
5276 static NTSTATUS
server_ioctl_file( HANDLE handle
, HANDLE event
,
5277 PIO_APC_ROUTINE apc
, PVOID apc_context
,
5278 IO_STATUS_BLOCK
*io
, UINT code
,
5279 const void *in_buffer
, UINT in_size
,
5280 PVOID out_buffer
, UINT out_size
)
5282 struct async_irp
*async
;
5283 unsigned int status
;
5287 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
5288 return STATUS_NO_MEMORY
;
5289 async
->buffer
= out_buffer
;
5290 async
->size
= out_size
;
5292 SERVER_START_REQ( ioctl
)
5295 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, iosb_client_ptr(io
) );
5296 wine_server_add_data( req
, in_buffer
, in_size
);
5297 if ((code
& 3) != METHOD_BUFFERED
) wine_server_add_data( req
, out_buffer
, out_size
);
5298 wine_server_set_reply( req
, out_buffer
, out_size
);
5299 status
= virtual_locked_server_call( req
);
5300 wait_handle
= wine_server_ptr_handle( reply
->wait
);
5301 options
= reply
->options
;
5302 if (wait_handle
&& status
!= STATUS_PENDING
)
5303 set_sync_iosb( io
, status
, wine_server_reply_size( reply
), options
);
5307 if (status
== STATUS_NOT_SUPPORTED
)
5308 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
5309 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
5311 if (status
!= STATUS_PENDING
) free( async
);
5313 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
5320 int interval
; /* max interval between two bytes */
5321 int total
; /* total timeout for the whole operation */
5322 int end_time
; /* absolute time of end of operation */
5325 /* retrieve the I/O timeouts to use for a given handle */
5326 static unsigned int get_io_timeouts( HANDLE handle
, enum server_fd_type type
, ULONG count
, BOOL is_read
,
5327 struct io_timeouts
*timeouts
)
5329 timeouts
->interval
= timeouts
->total
= -1;
5333 case FD_TYPE_SERIAL
:
5335 /* GetCommTimeouts */
5338 if (sync_ioctl( handle
, IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) ))
5343 if (st
.ReadIntervalTimeout
)
5344 timeouts
->interval
= st
.ReadIntervalTimeout
;
5346 if (st
.ReadTotalTimeoutMultiplier
|| st
.ReadTotalTimeoutConstant
)
5348 timeouts
->total
= st
.ReadTotalTimeoutConstant
;
5349 if (st
.ReadTotalTimeoutMultiplier
!= MAXDWORD
)
5350 timeouts
->total
+= count
* st
.ReadTotalTimeoutMultiplier
;
5352 else if (st
.ReadIntervalTimeout
== MAXDWORD
)
5353 timeouts
->interval
= timeouts
->total
= 0;
5357 if (st
.WriteTotalTimeoutMultiplier
|| st
.WriteTotalTimeoutConstant
)
5359 timeouts
->total
= st
.WriteTotalTimeoutConstant
;
5360 if (st
.WriteTotalTimeoutMultiplier
!= MAXDWORD
)
5361 timeouts
->total
+= count
* st
.WriteTotalTimeoutMultiplier
;
5366 case FD_TYPE_MAILSLOT
:
5369 timeouts
->interval
= 0; /* return as soon as we got something */
5370 SERVER_START_REQ( set_mailslot_info
)
5372 req
->handle
= wine_server_obj_handle( handle
);
5374 if (!wine_server_call( req
) && reply
->read_timeout
!= TIMEOUT_INFINITE
)
5375 timeouts
->total
= reply
->read_timeout
/ -10000;
5380 case FD_TYPE_SOCKET
:
5382 if (is_read
) timeouts
->interval
= 0; /* return as soon as we got something */
5387 if (timeouts
->total
!= -1) timeouts
->end_time
= NtGetTickCount() + timeouts
->total
;
5388 return STATUS_SUCCESS
;
5392 /* retrieve the timeout for the next wait, in milliseconds */
5393 static inline int get_next_io_timeout( const struct io_timeouts
*timeouts
, ULONG already
)
5397 if (timeouts
->total
!= -1)
5399 ret
= timeouts
->end_time
- NtGetTickCount();
5400 if (ret
< 0) ret
= 0;
5402 if (already
&& timeouts
->interval
!= -1)
5404 if (ret
== -1 || ret
> timeouts
->interval
) ret
= timeouts
->interval
;
5410 /* retrieve the avail_mode flag for async reads */
5411 static NTSTATUS
get_io_avail_mode( HANDLE handle
, enum server_fd_type type
, BOOL
*avail_mode
)
5413 NTSTATUS status
= STATUS_SUCCESS
;
5417 case FD_TYPE_SERIAL
:
5419 /* GetCommTimeouts */
5422 if (!(status
= sync_ioctl( handle
, IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) )))
5424 *avail_mode
= (!st
.ReadTotalTimeoutMultiplier
&&
5425 !st
.ReadTotalTimeoutConstant
&&
5426 st
.ReadIntervalTimeout
== MAXDWORD
);
5430 case FD_TYPE_MAILSLOT
:
5431 case FD_TYPE_SOCKET
:
5436 *avail_mode
= FALSE
;
5442 /* register an async I/O for a file read; helper for NtReadFile */
5443 static unsigned int register_async_file_read( HANDLE handle
, HANDLE event
,
5444 PIO_APC_ROUTINE apc
, void *apc_user
,
5445 client_ptr_t iosb
, void *buffer
,
5446 ULONG already
, ULONG length
, BOOL avail_mode
)
5448 struct async_fileio_read
*fileio
;
5449 unsigned int status
;
5451 if (!(fileio
= (struct async_fileio_read
*)alloc_fileio( sizeof(*fileio
), async_read_proc
, handle
)))
5452 return STATUS_NO_MEMORY
;
5454 fileio
->already
= already
;
5455 fileio
->count
= length
;
5456 fileio
->buffer
= buffer
;
5457 fileio
->avail_mode
= avail_mode
;
5459 SERVER_START_REQ( register_async
)
5461 req
->type
= ASYNC_TYPE_READ
;
5462 req
->count
= length
;
5463 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, iosb
);
5464 status
= wine_server_call( req
);
5468 if (status
!= STATUS_PENDING
) free( fileio
);
5472 static void add_completion( HANDLE handle
, ULONG_PTR value
, NTSTATUS status
, ULONG info
, BOOL async
)
5474 SERVER_START_REQ( add_fd_completion
)
5476 req
->handle
= wine_server_obj_handle( handle
);
5477 req
->cvalue
= value
;
5478 req
->status
= status
;
5479 req
->information
= info
;
5481 wine_server_call( req
);
5486 /* notify direct completion of async and close the wait handle if it is no longer needed */
5487 void set_async_direct_result( HANDLE
*async_handle
, unsigned int options
, IO_STATUS_BLOCK
*io
,
5488 NTSTATUS status
, ULONG_PTR information
, BOOL mark_pending
)
5492 /* if we got STATUS_ALERTED, we must have a valid async handle */
5493 assert( *async_handle
);
5495 if (!NT_ERROR(status
) && status
!= STATUS_PENDING
)
5496 set_sync_iosb( io
, status
, information
, options
);
5498 SERVER_START_REQ( set_async_direct_result
)
5500 req
->handle
= wine_server_obj_handle( *async_handle
);
5501 req
->status
= status
;
5502 req
->information
= information
;
5503 req
->mark_pending
= mark_pending
;
5504 ret
= wine_server_call( req
);
5505 if (ret
== STATUS_SUCCESS
)
5506 *async_handle
= wine_server_ptr_handle( reply
->handle
);
5510 if (ret
!= STATUS_SUCCESS
)
5511 ERR( "cannot report I/O result back to server: %#x\n", ret
);
5514 /* complete async file I/O, signaling completion in all ways necessary */
5515 void file_complete_async( HANDLE handle
, unsigned int options
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5516 IO_STATUS_BLOCK
*io
, NTSTATUS status
, ULONG_PTR information
)
5518 ULONG_PTR iosb_ptr
= iosb_client_ptr(io
);
5520 set_sync_iosb( io
, status
, information
, options
);
5521 if (event
) NtSetEvent( event
, NULL
);
5522 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
, (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
5523 else if (apc_user
) add_completion( handle
, (ULONG_PTR
)apc_user
, status
, information
, FALSE
);
5527 static unsigned int set_pending_write( HANDLE device
)
5529 unsigned int status
;
5531 SERVER_START_REQ( set_serial_info
)
5533 req
->handle
= wine_server_obj_handle( device
);
5534 req
->flags
= SERIALINFO_PENDING_WRITE
;
5535 status
= wine_server_call( req
);
5542 /******************************************************************************
5543 * NtReadFile (NTDLL.@)
5545 NTSTATUS WINAPI
NtReadFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5546 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
5547 LARGE_INTEGER
*offset
, ULONG
*key
)
5549 int result
, unix_handle
, needs_close
;
5550 unsigned int options
;
5551 struct io_timeouts timeouts
;
5552 unsigned int status
, ret_status
;
5554 client_ptr_t iosb_ptr
= iosb_client_ptr(io
);
5555 enum server_fd_type type
;
5556 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5557 BOOL send_completion
= FALSE
, async_read
, timeout_init_done
= FALSE
;
5559 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5560 handle
, event
, apc
, apc_user
, io
, buffer
, (int)length
, offset
, key
);
5562 if (!io
) return STATUS_ACCESS_VIOLATION
;
5564 status
= server_get_unix_fd( handle
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5565 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5567 if (!virtual_check_buffer_for_write( buffer
, length
)) return STATUS_ACCESS_VIOLATION
;
5569 if (status
== STATUS_BAD_DEVICE_TYPE
)
5570 return server_read_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5572 async_read
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5574 if (type
== FD_TYPE_FILE
)
5576 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5578 status
= STATUS_INVALID_PARAMETER
;
5582 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5584 /* async I/O doesn't make sense on regular files */
5585 while ((result
= virtual_locked_pread( unix_handle
, buffer
, length
, offset
->QuadPart
)) == -1)
5589 status
= errno_to_status( errno
);
5593 if (!async_read
) /* update file pointer position */
5594 lseek( unix_handle
, offset
->QuadPart
+ result
, SEEK_SET
);
5597 status
= (total
|| !length
) ? STATUS_SUCCESS
: STATUS_END_OF_FILE
;
5601 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5603 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5605 status
= STATUS_INVALID_PARAMETER
;
5609 else if (type
== FD_TYPE_SOCKET
)
5611 status
= sock_read( handle
, unix_handle
, event
, apc
, apc_user
, io
, buffer
, length
);
5612 if (needs_close
) close( unix_handle
);
5616 if (type
== FD_TYPE_SERIAL
&& async_read
&& length
)
5618 /* an asynchronous serial port read with a read interval timeout needs to
5619 skip the synchronous read to make sure that the server starts the read
5620 interval timer after the first read */
5621 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5622 if (timeouts
.interval
)
5624 status
= register_async_file_read( handle
, event
, apc
, apc_user
, iosb_ptr
,
5625 buffer
, total
, length
, FALSE
);
5632 if ((result
= virtual_locked_read( unix_handle
, (char *)buffer
+ total
, length
- total
)) >= 0)
5635 if (!result
|| total
== length
)
5639 status
= STATUS_SUCCESS
;
5646 case FD_TYPE_DEVICE
:
5647 status
= length
? STATUS_END_OF_FILE
: STATUS_SUCCESS
;
5649 case FD_TYPE_SERIAL
:
5652 status
= STATUS_SUCCESS
;
5657 status
= STATUS_PIPE_BROKEN
;
5661 else if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5663 else if (errno
!= EAGAIN
)
5665 if (errno
== EINTR
) continue;
5666 if (!total
) status
= errno_to_status( errno
);
5674 if ((status
= get_io_avail_mode( handle
, type
, &avail_mode
))) goto err
;
5675 if (total
&& avail_mode
)
5677 status
= STATUS_SUCCESS
;
5680 status
= register_async_file_read( handle
, event
, apc
, apc_user
, iosb_ptr
,
5681 buffer
, total
, length
, avail_mode
);
5684 else /* synchronous read, wait for the fd to become ready */
5689 if (!timeout_init_done
)
5691 timeout_init_done
= TRUE
;
5692 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5693 if (event
) NtResetEvent( event
, NULL
);
5695 timeout
= get_next_io_timeout( &timeouts
, total
);
5697 pfd
.fd
= unix_handle
;
5698 pfd
.events
= POLLIN
;
5700 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
5702 if (total
) /* return with what we got so far */
5703 status
= STATUS_SUCCESS
;
5705 status
= (type
== FD_TYPE_MAILSLOT
) ? STATUS_IO_TIMEOUT
: STATUS_TIMEOUT
;
5708 if (ret
== -1 && errno
!= EINTR
)
5710 status
= errno_to_status( errno
);
5713 /* will now restart the read */
5718 send_completion
= cvalue
!= 0;
5721 if (needs_close
) close( unix_handle
);
5722 if (status
== STATUS_SUCCESS
|| (status
== STATUS_END_OF_FILE
&& (!async_read
|| type
== FD_TYPE_FILE
)))
5724 set_sync_iosb( io
, status
, total
, options
);
5725 TRACE("= SUCCESS (%u)\n", total
);
5726 if (event
) NtSetEvent( event
, NULL
);
5727 if (apc
&& (!status
|| async_read
)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5728 (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
5732 TRACE("= 0x%08x\n", status
);
5733 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5736 ret_status
= async_read
&& type
== FD_TYPE_FILE
&& (status
== STATUS_SUCCESS
|| status
== STATUS_END_OF_FILE
)
5737 ? STATUS_PENDING
: status
;
5739 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
5744 /******************************************************************************
5745 * NtReadFileScatter (NTDLL.@)
5747 NTSTATUS WINAPI
NtReadFileScatter( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5748 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
5749 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
5751 int result
, unix_handle
, needs_close
;
5752 unsigned int options
, status
;
5753 UINT pos
= 0, total
= 0;
5754 client_ptr_t iosb_ptr
= iosb_client_ptr(io
);
5755 enum server_fd_type type
;
5756 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5757 BOOL send_completion
= FALSE
;
5759 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5760 file
, event
, apc
, apc_user
, io
, segments
, (int)length
, offset
, key
);
5762 if (!io
) return STATUS_ACCESS_VIOLATION
;
5764 status
= server_get_unix_fd( file
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5765 if (status
) return status
;
5767 if ((type
!= FD_TYPE_FILE
) ||
5768 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
5769 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
5771 status
= STATUS_INVALID_PARAMETER
;
5777 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5778 result
= pread( unix_handle
, (char *)segments
->Buffer
+ pos
,
5779 min( length
- pos
, page_size
- pos
), offset
->QuadPart
+ total
);
5781 result
= read( unix_handle
, (char *)segments
->Buffer
+ pos
, min( length
- pos
, page_size
- pos
) );
5785 if (errno
== EINTR
) continue;
5786 status
= errno_to_status( errno
);
5792 if ((pos
+= result
) == page_size
)
5799 if (total
== 0) status
= STATUS_END_OF_FILE
;
5801 send_completion
= cvalue
!= 0;
5803 if (needs_close
) close( unix_handle
);
5804 set_sync_iosb( io
, status
, total
, options
);
5805 TRACE("= 0x%08x (%u)\n", status
, total
);
5806 if (event
) NtSetEvent( event
, NULL
);
5807 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
, (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
5808 if (send_completion
) add_completion( file
, cvalue
, status
, total
, TRUE
);
5810 return STATUS_PENDING
;
5813 if (needs_close
) close( unix_handle
);
5814 if (event
) NtResetEvent( event
, NULL
);
5815 TRACE("= 0x%08x\n", status
);
5820 /******************************************************************************
5821 * NtWriteFile (NTDLL.@)
5823 NTSTATUS WINAPI
NtWriteFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5824 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG length
,
5825 LARGE_INTEGER
*offset
, ULONG
*key
)
5827 int result
, unix_handle
, needs_close
;
5828 unsigned int options
;
5829 struct io_timeouts timeouts
;
5830 unsigned int status
, ret_status
;
5832 client_ptr_t iosb_ptr
= iosb_client_ptr(io
);
5833 enum server_fd_type type
;
5834 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5835 BOOL send_completion
= FALSE
, async_write
, append_write
= FALSE
, timeout_init_done
= FALSE
;
5836 LARGE_INTEGER offset_eof
;
5838 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5839 handle
, event
, apc
, apc_user
, io
, buffer
, (int)length
, offset
, key
);
5841 if (!io
) return STATUS_ACCESS_VIOLATION
;
5843 status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5844 if (status
== STATUS_ACCESS_DENIED
)
5846 status
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &unix_handle
,
5847 &needs_close
, &type
, &options
);
5848 append_write
= TRUE
;
5850 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5852 async_write
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5854 if (!virtual_check_buffer_for_read( buffer
, length
))
5856 status
= STATUS_INVALID_USER_BUFFER
;
5860 if (status
== STATUS_BAD_DEVICE_TYPE
)
5861 return server_write_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5863 if (type
== FD_TYPE_FILE
)
5866 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5868 status
= STATUS_INVALID_PARAMETER
;
5874 offset_eof
.QuadPart
= FILE_WRITE_TO_END_OF_FILE
;
5875 offset
= &offset_eof
;
5878 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5880 off_t off
= offset
->QuadPart
;
5882 if (offset
->QuadPart
== FILE_WRITE_TO_END_OF_FILE
)
5886 if (fstat( unix_handle
, &st
) == -1)
5888 status
= errno_to_status( errno
);
5893 else if (offset
->QuadPart
< 0)
5895 status
= STATUS_INVALID_PARAMETER
;
5899 /* async I/O doesn't make sense on regular files */
5900 while ((result
= pwrite( unix_handle
, buffer
, length
, off
)) == -1)
5904 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5905 else status
= errno_to_status( errno
);
5910 if (!async_write
) /* update file pointer position */
5911 lseek( unix_handle
, off
+ result
, SEEK_SET
);
5914 status
= STATUS_SUCCESS
;
5918 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5921 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5923 status
= STATUS_INVALID_PARAMETER
;
5927 else if (type
== FD_TYPE_SOCKET
)
5929 status
= sock_write( handle
, unix_handle
, event
, apc
, apc_user
, io
, buffer
, length
);
5930 if (needs_close
) close( unix_handle
);
5936 /* zero-length writes on sockets may not work with plain write(2) */
5937 if (!length
&& type
== FD_TYPE_MAILSLOT
)
5938 result
= send( unix_handle
, buffer
, 0, 0 );
5940 result
= write( unix_handle
, (const char *)buffer
+ total
, length
- total
);
5945 if (total
== length
)
5947 status
= STATUS_SUCCESS
;
5950 if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5952 else if (errno
!= EAGAIN
)
5954 if (errno
== EINTR
) continue;
5957 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5958 else status
= errno_to_status( errno
);
5965 struct async_fileio_write
*fileio
;
5967 fileio
= (struct async_fileio_write
*)alloc_fileio( sizeof(*fileio
), async_write_proc
, handle
);
5970 status
= STATUS_NO_MEMORY
;
5973 fileio
->already
= total
;
5974 fileio
->count
= length
;
5975 fileio
->buffer
= buffer
;
5977 SERVER_START_REQ( register_async
)
5979 req
->type
= ASYNC_TYPE_WRITE
;
5980 req
->count
= length
;
5981 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, iosb_ptr
);
5982 status
= wine_server_call( req
);
5986 if (status
!= STATUS_PENDING
) free( fileio
);
5989 else /* synchronous write, wait for the fd to become ready */
5994 if (!timeout_init_done
)
5996 timeout_init_done
= TRUE
;
5997 if ((status
= get_io_timeouts( handle
, type
, length
, FALSE
, &timeouts
)))
5999 if (event
) NtResetEvent( event
, NULL
);
6001 timeout
= get_next_io_timeout( &timeouts
, total
);
6003 pfd
.fd
= unix_handle
;
6004 pfd
.events
= POLLOUT
;
6006 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
6008 /* return with what we got so far */
6009 status
= total
? STATUS_SUCCESS
: STATUS_TIMEOUT
;
6012 if (ret
== -1 && errno
!= EINTR
)
6014 status
= errno_to_status( errno
);
6017 /* will now restart the write */
6022 send_completion
= cvalue
!= 0;
6025 if (needs_close
) close( unix_handle
);
6027 if (type
== FD_TYPE_SERIAL
&& (status
== STATUS_SUCCESS
|| status
== STATUS_PENDING
))
6028 set_pending_write( handle
);
6030 if (status
== STATUS_SUCCESS
)
6032 set_sync_iosb( io
, status
, total
, options
);
6033 TRACE("= SUCCESS (%u)\n", total
);
6034 if (event
) NtSetEvent( event
, NULL
);
6035 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
, (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
6039 TRACE("= 0x%08x\n", status
);
6040 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
6043 ret_status
= async_write
&& type
== FD_TYPE_FILE
&& status
== STATUS_SUCCESS
? STATUS_PENDING
: status
;
6044 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
6049 /******************************************************************************
6050 * NtWriteFileGather (NTDLL.@)
6052 NTSTATUS WINAPI
NtWriteFileGather( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
6053 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
6054 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
6056 int result
, unix_handle
, needs_close
;
6057 unsigned int options
, status
;
6058 UINT pos
= 0, total
= 0;
6059 enum server_fd_type type
;
6061 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
6062 file
, event
, apc
, apc_user
, io
, segments
, (int)length
, offset
, key
);
6064 if (length
% page_size
) return STATUS_INVALID_PARAMETER
;
6065 if (!io
) return STATUS_ACCESS_VIOLATION
;
6067 status
= server_get_unix_fd( file
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
6068 if (status
) return status
;
6070 if ((type
!= FD_TYPE_FILE
) ||
6071 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
6072 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
6074 status
= STATUS_INVALID_PARAMETER
;
6080 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
6081 result
= pwrite( unix_handle
, (char *)segments
->Buffer
+ pos
,
6082 page_size
- pos
, offset
->QuadPart
+ total
);
6084 result
= write( unix_handle
, (char *)segments
->Buffer
+ pos
, page_size
- pos
);
6088 if (errno
== EINTR
) continue;
6089 if (errno
== EFAULT
)
6091 status
= STATUS_INVALID_USER_BUFFER
;
6094 status
= errno_to_status( errno
);
6099 status
= STATUS_DISK_FULL
;
6104 if ((pos
+= result
) == page_size
)
6112 if (needs_close
) close( unix_handle
);
6113 if (status
== STATUS_SUCCESS
)
6115 file_complete_async( file
, options
, event
, apc
, apc_user
, io
, status
, total
);
6116 TRACE("= SUCCESS (%u)\n", total
);
6120 TRACE("= 0x%08x\n", status
);
6121 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
6127 /******************************************************************************
6128 * NtDeviceIoControlFile (NTDLL.@)
6130 NTSTATUS WINAPI
NtDeviceIoControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
6131 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
6132 void *out_buffer
, ULONG out_size
)
6134 ULONG device
= (code
>> 16);
6135 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
6137 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
6138 handle
, event
, apc
, apc_context
, io
, (int)code
,
6139 in_buffer
, (int)in_size
, out_buffer
, (int)out_size
);
6141 /* some broken applications call this frequently with INVALID_HANDLE_VALUE,
6142 * and run slowly if we make a server call every time */
6143 if (HandleToLong( handle
) == ~0)
6144 return STATUS_INVALID_HANDLE
;
6148 case FILE_DEVICE_BEEP
:
6149 case FILE_DEVICE_NETWORK
:
6150 status
= sock_ioctl( handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
6152 case FILE_DEVICE_DISK
:
6153 case FILE_DEVICE_CD_ROM
:
6154 case FILE_DEVICE_DVD
:
6155 case FILE_DEVICE_CONTROLLER
:
6156 case FILE_DEVICE_MASS_STORAGE
:
6157 status
= cdrom_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
6158 in_buffer
, in_size
, out_buffer
, out_size
);
6160 case FILE_DEVICE_SERIAL_PORT
:
6161 status
= serial_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
6162 in_buffer
, in_size
, out_buffer
, out_size
);
6164 case FILE_DEVICE_TAPE
:
6165 status
= tape_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
6166 in_buffer
, in_size
, out_buffer
, out_size
);
6170 if (status
== STATUS_NOT_SUPPORTED
|| status
== STATUS_BAD_DEVICE_TYPE
)
6171 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
6172 in_buffer
, in_size
, out_buffer
, out_size
);
6177 /* helper for internal ioctl calls */
6178 NTSTATUS
sync_ioctl( HANDLE file
, ULONG code
, void *in_buffer
, ULONG in_size
, void *out_buffer
, ULONG out_size
)
6180 IO_STATUS_BLOCK32 io32
;
6183 /* the 32-bit iosb is filled for overlapped file handles */
6185 return NtDeviceIoControlFile( file
, NULL
, NULL
, NULL
, &io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
6189 /* Tell Valgrind to ignore any holes in structs we will be passing to the
6191 static void ignore_server_ioctl_struct_holes( ULONG code
, const void *in_buffer
, ULONG in_size
)
6193 #ifdef VALGRIND_MAKE_MEM_DEFINED
6194 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
6196 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
6197 if ((size) >= FIELD_OFFSET(t, f2)) \
6198 VALGRIND_MAKE_MEM_DEFINED( \
6199 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
6200 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
6205 case FSCTL_PIPE_WAIT
:
6206 IGNORE_STRUCT_HOLE(in_buffer
, in_size
, FILE_PIPE_WAIT_FOR_BUFFER
, TimeoutSpecified
, Name
);
6213 /******************************************************************************
6214 * NtFsControlFile (NTDLL.@)
6216 NTSTATUS WINAPI
NtFsControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
6217 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
6218 void *out_buffer
, ULONG out_size
)
6220 unsigned int options
;
6221 int fd
, needs_close
;
6225 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
6226 handle
, event
, apc
, apc_context
, io
, (int)code
,
6227 in_buffer
, (int)in_size
, out_buffer
, (int)out_size
);
6229 if (!io
) return STATUS_INVALID_PARAMETER
;
6231 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, &options
);
6232 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
)
6234 if (needs_close
) close( fd
);
6236 ignore_server_ioctl_struct_holes( code
, in_buffer
, in_size
);
6240 case FSCTL_DISMOUNT_VOLUME
:
6241 status
= server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
6242 in_buffer
, in_size
, out_buffer
, out_size
);
6243 if (!status
) status
= unmount_device( handle
);
6246 case FSCTL_PIPE_IMPERSONATE
:
6247 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
6248 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
6249 in_buffer
, in_size
, out_buffer
, out_size
);
6251 case FSCTL_IS_VOLUME_MOUNTED
:
6252 case FSCTL_LOCK_VOLUME
:
6253 case FSCTL_UNLOCK_VOLUME
:
6254 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
6255 (int)code
, (int)code
>> 16, ((int)code
>> 14) & 3, ((int)code
>> 2) & 0xfff, (int)code
& 3);
6256 status
= STATUS_SUCCESS
;
6259 case FSCTL_GET_RETRIEVAL_POINTERS
:
6261 RETRIEVAL_POINTERS_BUFFER
*buffer
= (RETRIEVAL_POINTERS_BUFFER
*)out_buffer
;
6263 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
6265 if (out_size
>= sizeof(RETRIEVAL_POINTERS_BUFFER
))
6267 buffer
->ExtentCount
= 1;
6268 buffer
->StartingVcn
.QuadPart
= 1;
6269 buffer
->Extents
[0].NextVcn
.QuadPart
= 0;
6270 buffer
->Extents
[0].Lcn
.QuadPart
= 0;
6271 size
= sizeof(RETRIEVAL_POINTERS_BUFFER
);
6272 status
= STATUS_SUCCESS
;
6276 status
= STATUS_BUFFER_TOO_SMALL
;
6281 case FSCTL_GET_REPARSE_POINT
:
6282 if (out_buffer
&& out_size
)
6284 FIXME("FSCTL_GET_REPARSE_POINT semi-stub\n");
6285 status
= STATUS_NOT_A_REPARSE_POINT
;
6287 else status
= STATUS_INVALID_USER_BUFFER
;
6290 case FSCTL_GET_OBJECT_ID
:
6292 FILE_OBJECTID_BUFFER
*info
= out_buffer
;
6293 int fd
, needs_close
;
6296 if (out_size
>= sizeof(*info
))
6298 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
6301 if (needs_close
) close( fd
);
6302 memset( info
, 0, sizeof(*info
) );
6303 memcpy( info
->ObjectId
, &st
.st_dev
, sizeof(st
.st_dev
) );
6304 memcpy( info
->ObjectId
+ 8, &st
.st_ino
, sizeof(st
.st_ino
) );
6305 size
= sizeof(*info
);
6307 else status
= STATUS_BUFFER_TOO_SMALL
;
6311 case FSCTL_SET_SPARSE
:
6312 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
6313 status
= STATUS_SUCCESS
;
6316 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
6317 in_buffer
, in_size
, out_buffer
, out_size
);
6320 if (!NT_ERROR(status
) && status
!= STATUS_PENDING
)
6321 file_complete_async( handle
, options
, event
, apc
, apc_context
, io
, status
, size
);
6326 /******************************************************************************
6327 * NtFlushBuffersFile (NTDLL.@)
6329 NTSTATUS WINAPI
NtFlushBuffersFile( HANDLE handle
, IO_STATUS_BLOCK
*io
)
6333 enum server_fd_type type
;
6334 int fd
, needs_close
;
6336 if (!io
|| !virtual_check_buffer_for_write( io
, sizeof(*io
) )) return STATUS_ACCESS_VIOLATION
;
6338 ret
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, &type
, NULL
);
6339 if (ret
== STATUS_ACCESS_DENIED
)
6340 ret
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &fd
, &needs_close
, &type
, NULL
);
6342 if (!ret
&& (type
== FD_TYPE_FILE
|| type
== FD_TYPE_DIR
|| type
== FD_TYPE_CHAR
))
6344 if (fsync(fd
)) ret
= errno_to_status( errno
);
6346 io
->Information
= 0;
6348 else if (!ret
&& type
== FD_TYPE_SERIAL
)
6350 ret
= serial_FlushBuffersFile( fd
);
6352 else if (ret
!= STATUS_ACCESS_DENIED
)
6354 struct async_irp
*async
;
6356 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
6357 return STATUS_NO_MEMORY
;
6358 async
->buffer
= NULL
;
6361 SERVER_START_REQ( flush
)
6363 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, iosb_client_ptr(io
) );
6364 ret
= wine_server_call( req
);
6365 wait_handle
= wine_server_ptr_handle( reply
->event
);
6366 if (wait_handle
&& ret
!= STATUS_PENDING
)
6369 io
->Information
= 0;
6374 if (ret
!= STATUS_PENDING
) free( async
);
6376 if (wait_handle
) ret
= wait_async( wait_handle
, FALSE
);
6379 if (needs_close
) close( fd
);
6384 /**************************************************************************
6385 * NtCancelIoFile (NTDLL.@)
6387 NTSTATUS WINAPI
NtCancelIoFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
)
6389 unsigned int status
;
6391 TRACE( "%p %p\n", handle
, io_status
);
6393 SERVER_START_REQ( cancel_async
)
6395 req
->handle
= wine_server_obj_handle( handle
);
6396 req
->only_thread
= TRUE
;
6397 if (!(status
= wine_server_call( req
)))
6399 io_status
->Status
= status
;
6400 io_status
->Information
= 0;
6409 /**************************************************************************
6410 * NtCancelIoFileEx (NTDLL.@)
6412 NTSTATUS WINAPI
NtCancelIoFileEx( HANDLE handle
, IO_STATUS_BLOCK
*io
, IO_STATUS_BLOCK
*io_status
)
6414 unsigned int status
;
6416 TRACE( "%p %p %p\n", handle
, io
, io_status
);
6418 SERVER_START_REQ( cancel_async
)
6420 req
->handle
= wine_server_obj_handle( handle
);
6421 req
->iosb
= wine_server_client_ptr( io
);
6422 if (!(status
= wine_server_call( req
)))
6424 io_status
->Status
= status
;
6425 io_status
->Information
= 0;
6434 /**************************************************************************
6435 * NtCancelSynchronousIoFile (NTDLL.@)
6437 NTSTATUS WINAPI
NtCancelSynchronousIoFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, IO_STATUS_BLOCK
*io_status
)
6439 unsigned int status
;
6441 TRACE( "(%p %p %p)\n", handle
, io
, io_status
);
6443 SERVER_START_REQ( cancel_sync
)
6445 req
->handle
= wine_server_obj_handle( handle
);
6446 req
->iosb
= wine_server_client_ptr( io
);
6447 status
= wine_server_call( req
);
6451 io_status
->Status
= status
;
6452 io_status
->Information
= 0;
6456 /******************************************************************
6457 * NtLockFile (NTDLL.@)
6459 NTSTATUS WINAPI
NtLockFile( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void* apc_user
,
6460 IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
6461 LARGE_INTEGER
*count
, ULONG
*key
, BOOLEAN dont_wait
, BOOLEAN exclusive
)
6468 if (apc
|| io_status
|| key
)
6470 FIXME("Unimplemented yet parameter\n");
6471 return STATUS_NOT_IMPLEMENTED
;
6473 if (apc_user
&& !warn
++) FIXME("I/O completion on lock not implemented yet\n");
6477 SERVER_START_REQ( lock_file
)
6479 req
->handle
= wine_server_obj_handle( file
);
6480 req
->offset
= offset
->QuadPart
;
6481 req
->count
= count
->QuadPart
;
6482 req
->shared
= !exclusive
;
6483 req
->wait
= !dont_wait
;
6484 ret
= wine_server_call( req
);
6485 handle
= wine_server_ptr_handle( reply
->handle
);
6486 async
= reply
->overlapped
;
6489 if (ret
!= STATUS_PENDING
)
6491 if (!ret
&& event
) NtSetEvent( event
, NULL
);
6496 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6497 if (handle
) NtClose( handle
);
6498 return STATUS_PENDING
;
6502 NtWaitForSingleObject( handle
, FALSE
, NULL
);
6505 else /* Unix lock conflict, sleep a bit and retry */
6508 time
.QuadPart
= -100 * (ULONGLONG
)10000;
6509 NtDelayExecution( FALSE
, &time
);
6515 /******************************************************************
6516 * NtUnlockFile (NTDLL.@)
6518 NTSTATUS WINAPI
NtUnlockFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
6519 LARGE_INTEGER
*count
, ULONG
*key
)
6521 unsigned int status
;
6523 TRACE( "%p %s %s\n",
6524 handle
, wine_dbgstr_longlong(offset
->QuadPart
), wine_dbgstr_longlong(count
->QuadPart
) );
6526 if (io_status
|| key
)
6528 FIXME("Unimplemented yet parameter\n");
6529 return STATUS_NOT_IMPLEMENTED
;
6532 SERVER_START_REQ( unlock_file
)
6534 req
->handle
= wine_server_obj_handle( handle
);
6535 req
->offset
= offset
->QuadPart
;
6536 req
->count
= count
->QuadPart
;
6537 status
= wine_server_call( req
);
6544 static BOOL
read_changes_apc( void *user
, ULONG_PTR
*info
, unsigned int *status
)
6546 struct async_fileio_read_changes
*fileio
= user
;
6549 if (*status
== STATUS_ALERTED
)
6551 SERVER_START_REQ( read_change
)
6553 req
->handle
= wine_server_obj_handle( fileio
->io
.handle
);
6554 wine_server_set_reply( req
, fileio
->data
, fileio
->data_size
);
6555 *status
= wine_server_call( req
);
6556 size
= wine_server_reply_size( reply
);
6560 if (*status
== STATUS_SUCCESS
&& fileio
->buffer
)
6562 FILE_NOTIFY_INFORMATION
*pfni
= fileio
->buffer
;
6563 int i
, left
= fileio
->buffer_size
;
6564 DWORD
*last_entry_offset
= NULL
;
6565 struct filesystem_event
*event
= (struct filesystem_event
*)fileio
->data
;
6567 while (size
&& left
>= sizeof(*pfni
))
6569 DWORD len
= (left
- offsetof(FILE_NOTIFY_INFORMATION
, FileName
)) / sizeof(WCHAR
);
6571 /* convert to an NT style path */
6572 for (i
= 0; i
< event
->len
; i
++)
6573 if (event
->name
[i
] == '/') event
->name
[i
] = '\\';
6575 pfni
->Action
= event
->action
;
6576 pfni
->FileNameLength
= ntdll_umbstowcs( event
->name
, event
->len
, pfni
->FileName
, len
);
6577 last_entry_offset
= &pfni
->NextEntryOffset
;
6579 if (pfni
->FileNameLength
== len
) break;
6581 i
= offsetof(FILE_NOTIFY_INFORMATION
, FileName
[pfni
->FileNameLength
]);
6582 pfni
->FileNameLength
*= sizeof(WCHAR
);
6583 pfni
->NextEntryOffset
= i
;
6584 pfni
= (FILE_NOTIFY_INFORMATION
*)((char*)pfni
+ i
);
6587 i
= (offsetof(struct filesystem_event
, name
[event
->len
])
6588 + sizeof(int)-1) / sizeof(int) * sizeof(int);
6589 event
= (struct filesystem_event
*)((char*)event
+ i
);
6595 *status
= STATUS_NOTIFY_ENUM_DIR
;
6600 if (last_entry_offset
) *last_entry_offset
= 0;
6601 size
= fileio
->buffer_size
- left
;
6606 *status
= STATUS_NOTIFY_ENUM_DIR
;
6612 release_fileio( &fileio
->io
);
6616 #define FILE_NOTIFY_ALL ( \
6617 FILE_NOTIFY_CHANGE_FILE_NAME | \
6618 FILE_NOTIFY_CHANGE_DIR_NAME | \
6619 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
6620 FILE_NOTIFY_CHANGE_SIZE | \
6621 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6622 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6623 FILE_NOTIFY_CHANGE_CREATION | \
6624 FILE_NOTIFY_CHANGE_SECURITY )
6626 /******************************************************************************
6627 * NtNotifyChangeDirectoryFile (NTDLL.@)
6629 NTSTATUS WINAPI
NtNotifyChangeDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
,
6630 void *apc_context
, IO_STATUS_BLOCK
*iosb
, void *buffer
,
6631 ULONG buffer_size
, ULONG filter
, BOOLEAN subtree
)
6633 struct async_fileio_read_changes
*fileio
;
6634 unsigned int status
;
6635 ULONG size
= max( 4096, buffer_size
);
6637 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6638 handle
, event
, apc
, apc_context
, iosb
, buffer
, (int)buffer_size
, (int)filter
, subtree
);
6640 if (!iosb
) return STATUS_ACCESS_VIOLATION
;
6641 if (filter
== 0 || (filter
& ~FILE_NOTIFY_ALL
)) return STATUS_INVALID_PARAMETER
;
6643 fileio
= (struct async_fileio_read_changes
*)alloc_fileio(
6644 offsetof(struct async_fileio_read_changes
, data
[size
]), read_changes_apc
, handle
);
6645 if (!fileio
) return STATUS_NO_MEMORY
;
6647 fileio
->buffer
= buffer
;
6648 fileio
->buffer_size
= buffer_size
;
6649 fileio
->data_size
= size
;
6651 SERVER_START_REQ( read_directory_changes
)
6653 req
->filter
= filter
;
6654 req
->want_data
= (buffer
!= NULL
);
6655 req
->subtree
= subtree
;
6656 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_context
, iosb_client_ptr(iosb
) );
6657 status
= wine_server_call( req
);
6661 if (status
!= STATUS_PENDING
) free( fileio
);
6666 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6667 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6668 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION
*info
, const char *fstypename
,
6669 unsigned int flags
)
6671 if (!strcmp("cd9660", fstypename
) || !strcmp("udf", fstypename
))
6673 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6674 /* Don't assume read-only, let the mount options set it below */
6675 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6677 else if (!strcmp("nfs", fstypename
) || !strcmp("nwfs", fstypename
) ||
6678 !strcmp("smbfs", fstypename
) || !strcmp("afpfs", fstypename
))
6680 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6681 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6683 else if (!strcmp("procfs", fstypename
))
6684 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6686 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6688 if (flags
& MNT_RDONLY
)
6689 info
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
6691 if (!(flags
& MNT_LOCAL
))
6693 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6694 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6699 static inline BOOL
is_device_placeholder( int fd
)
6701 static const char wine_placeholder
[] = "Wine device placeholder";
6702 char buffer
[sizeof(wine_placeholder
)-1];
6704 if (pread( fd
, buffer
, sizeof(wine_placeholder
) - 1, 0 ) != sizeof(wine_placeholder
) - 1)
6706 return !memcmp( buffer
, wine_placeholder
, sizeof(wine_placeholder
) - 1 );
6709 NTSTATUS
get_device_info( int fd
, FILE_FS_DEVICE_INFORMATION
*info
)
6713 info
->Characteristics
= 0;
6714 if (fstat( fd
, &st
) < 0) return errno_to_status( errno
);
6715 if (S_ISCHR( st
.st_mode
))
6717 info
->DeviceType
= FILE_DEVICE_UNKNOWN
;
6719 switch(major(st
.st_rdev
))
6722 info
->DeviceType
= FILE_DEVICE_NULL
;
6725 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6728 info
->DeviceType
= FILE_DEVICE_PARALLEL_PORT
;
6730 case SCSI_TAPE_MAJOR
:
6731 info
->DeviceType
= FILE_DEVICE_TAPE
;
6734 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6737 if (ioctl(fd
, FIODTYPE
, &d_type
) == 0)
6742 info
->DeviceType
= FILE_DEVICE_TAPE
;
6745 info
->DeviceType
= FILE_DEVICE_DISK
;
6748 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6750 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6752 info
->DeviceType
= FILE_DEVICE_NULL
;
6756 /* no special d_type for parallel ports */
6761 else if (S_ISBLK( st
.st_mode
))
6763 info
->DeviceType
= FILE_DEVICE_DISK
;
6765 else if (S_ISFIFO( st
.st_mode
) || S_ISSOCK( st
.st_mode
))
6767 info
->DeviceType
= FILE_DEVICE_NAMED_PIPE
;
6769 else if (is_device_placeholder( fd
))
6771 info
->DeviceType
= FILE_DEVICE_DISK
;
6773 else /* regular file or directory */
6775 #if defined(linux) && defined(HAVE_FSTATFS)
6778 /* check for floppy disk */
6779 if (major(st
.st_dev
) == FLOPPY_MAJOR
)
6780 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6782 if (fstatfs( fd
, &stfs
) < 0) stfs
.f_type
= 0;
6783 switch (stfs
.f_type
)
6785 case 0x9660: /* iso9660 */
6786 case 0x9fa1: /* supermount */
6787 case 0x15013346: /* udf */
6788 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6789 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6791 case 0x6969: /* nfs */
6792 case 0xff534d42: /* cifs */
6793 case 0xfe534d42: /* smb2 */
6794 case 0x517b: /* smbfs */
6795 case 0x564c: /* ncpfs */
6796 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6797 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6799 case 0x1373: /* devfs */
6800 case 0x9fa0: /* procfs */
6801 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6803 case 0x01021994: /* tmpfs */
6804 case 0x28cd3d45: /* cramfs */
6805 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6806 * filesystems are rare on Windows, and some programs refuse to
6807 * recognize them as valid. */
6809 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6812 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6815 if (fstatfs( fd
, &stfs
) < 0)
6816 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6818 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flags
);
6819 #elif defined(__NetBSD__)
6820 struct statvfs stfs
;
6822 if (fstatvfs( fd
, &stfs
) < 0)
6823 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6825 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flag
);
6827 /* Use dkio to work out device types */
6829 # include <sys/dkio.h>
6830 # include <sys/vtoc.h>
6831 struct dk_cinfo dkinf
;
6832 int retval
= ioctl(fd
, DKIOCINFO
, &dkinf
);
6834 WARN("Unable to get disk device type information - assuming a disk like device\n");
6835 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6837 switch (dkinf
.dki_ctype
)
6840 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6841 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6845 case DKC_INTEL82072
:
6846 case DKC_INTEL82077
:
6847 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6848 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6851 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6852 * filesystems are rare on Windows, and some programs refuse to
6853 * recognize them as valid. */
6855 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6860 if (!warned
++) FIXME( "device info not properly supported on this platform\n" );
6861 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6863 info
->Characteristics
|= FILE_DEVICE_IS_MOUNTED
;
6865 return STATUS_SUCCESS
;
6869 /******************************************************************************
6870 * NtQueryVolumeInformationFile (NTDLL.@)
6872 NTSTATUS WINAPI
NtQueryVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
6873 void *buffer
, ULONG length
,
6874 FS_INFORMATION_CLASS info_class
)
6876 int fd
, needs_close
;
6877 unsigned int status
;
6879 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
6880 if (status
== STATUS_BAD_DEVICE_TYPE
)
6882 struct async_irp
*async
;
6885 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
6886 return STATUS_NO_MEMORY
;
6887 async
->buffer
= buffer
;
6888 async
->size
= length
;
6890 SERVER_START_REQ( get_volume_info
)
6892 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, iosb_client_ptr(io
) );
6893 req
->handle
= wine_server_obj_handle( handle
);
6894 req
->info_class
= info_class
;
6895 wine_server_set_reply( req
, buffer
, length
);
6896 status
= wine_server_call( req
);
6897 if (status
!= STATUS_PENDING
)
6899 io
->Status
= status
;
6900 io
->Information
= wine_server_reply_size( reply
);
6902 wait_handle
= wine_server_ptr_handle( reply
->wait
);
6905 if (status
!= STATUS_PENDING
) free( async
);
6906 if (wait_handle
) status
= wait_async( wait_handle
, FALSE
);
6909 else if (status
) return io
->Status
= status
;
6911 io
->Information
= 0;
6913 switch( info_class
)
6915 case FileFsLabelInformation
:
6916 FIXME( "%p: label info not supported\n", handle
);
6917 status
= STATUS_NOT_IMPLEMENTED
;
6920 case FileFsSizeInformation
:
6921 if (length
< sizeof(FILE_FS_SIZE_INFORMATION
))
6922 status
= STATUS_BUFFER_TOO_SMALL
;
6925 FILE_FS_SIZE_INFORMATION
*info
= buffer
;
6926 FILE_FS_FULL_SIZE_INFORMATION full_info
;
6928 if ((status
= get_full_size_info(fd
, &full_info
)) == STATUS_SUCCESS
)
6930 info
->TotalAllocationUnits
= full_info
.TotalAllocationUnits
;
6931 info
->AvailableAllocationUnits
= full_info
.CallerAvailableAllocationUnits
;
6932 info
->SectorsPerAllocationUnit
= full_info
.SectorsPerAllocationUnit
;
6933 info
->BytesPerSector
= full_info
.BytesPerSector
;
6934 io
->Information
= sizeof(*info
);
6939 case FileFsDeviceInformation
:
6940 if (length
< sizeof(FILE_FS_DEVICE_INFORMATION
))
6941 status
= STATUS_BUFFER_TOO_SMALL
;
6944 FILE_FS_DEVICE_INFORMATION
*info
= buffer
;
6946 if ((status
= get_device_info( fd
, info
)) == STATUS_SUCCESS
)
6947 io
->Information
= sizeof(*info
);
6951 case FileFsAttributeInformation
:
6953 static const WCHAR fatW
[] = {'F','A','T'};
6954 static const WCHAR fat32W
[] = {'F','A','T','3','2'};
6955 static const WCHAR ntfsW
[] = {'N','T','F','S'};
6956 static const WCHAR cdfsW
[] = {'C','D','F','S'};
6957 static const WCHAR udfW
[] = {'U','D','F'};
6959 FILE_FS_ATTRIBUTE_INFORMATION
*info
= buffer
;
6960 struct mountmgr_unix_drive drive
;
6961 enum mountmgr_fs_type fs_type
= MOUNTMGR_FS_TYPE_NTFS
;
6963 if (length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
))
6965 status
= STATUS_INFO_LENGTH_MISMATCH
;
6969 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) )) fs_type
= drive
.fs_type
;
6974 if (!fstatfs( fd
, &stfs
))
6976 #if defined(linux) && defined(HAVE_FSTATFS)
6977 switch (stfs
.f_type
)
6980 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6983 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6986 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6989 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6990 if (!strcmp( stfs
.f_fstypename
, "cd9660" ))
6991 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6992 else if (!strcmp( stfs
.f_fstypename
, "udf" ))
6993 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6994 else if (!strcmp( stfs
.f_fstypename
, "msdos" )) /* FreeBSD < 5, Apple */
6995 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6996 else if (!strcmp( stfs
.f_fstypename
, "msdosfs" )) /* FreeBSD >= 5 */
6997 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
7004 case MOUNTMGR_FS_TYPE_ISO9660
:
7005 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
;
7006 info
->MaximumComponentNameLength
= 221;
7007 info
->FileSystemNameLength
= min( sizeof(cdfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
7008 memcpy(info
->FileSystemName
, cdfsW
, info
->FileSystemNameLength
);
7010 case MOUNTMGR_FS_TYPE_UDF
:
7011 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
| FILE_UNICODE_ON_DISK
| FILE_CASE_SENSITIVE_SEARCH
;
7012 info
->MaximumComponentNameLength
= 255;
7013 info
->FileSystemNameLength
= min( sizeof(udfW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
7014 memcpy(info
->FileSystemName
, udfW
, info
->FileSystemNameLength
);
7016 case MOUNTMGR_FS_TYPE_FAT
:
7017 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
7018 info
->MaximumComponentNameLength
= 255;
7019 info
->FileSystemNameLength
= min( sizeof(fatW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
7020 memcpy(info
->FileSystemName
, fatW
, info
->FileSystemNameLength
);
7022 case MOUNTMGR_FS_TYPE_FAT32
:
7023 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
7024 info
->MaximumComponentNameLength
= 255;
7025 info
->FileSystemNameLength
= min( sizeof(fat32W
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
7026 memcpy(info
->FileSystemName
, fat32W
, info
->FileSystemNameLength
);
7029 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_PERSISTENT_ACLS
;
7030 info
->MaximumComponentNameLength
= 255;
7031 info
->FileSystemNameLength
= min( sizeof(ntfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
7032 memcpy(info
->FileSystemName
, ntfsW
, info
->FileSystemNameLength
);
7036 io
->Information
= offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) + info
->FileSystemNameLength
;
7037 status
= STATUS_SUCCESS
;
7041 case FileFsVolumeInformation
:
7043 FILE_FS_VOLUME_INFORMATION
*info
= buffer
;
7045 struct mountmgr_unix_drive
*drive
= (struct mountmgr_unix_drive
*)data
;
7048 if (length
< sizeof(FILE_FS_VOLUME_INFORMATION
))
7050 status
= STATUS_INFO_LENGTH_MISMATCH
;
7054 if (get_mountmgr_fs_info( handle
, fd
, drive
, sizeof(data
) ))
7056 status
= STATUS_NOT_IMPLEMENTED
;
7060 label
= (WCHAR
*)((char *)drive
+ drive
->label_offset
);
7061 info
->VolumeCreationTime
.QuadPart
= 0; /* FIXME */
7062 info
->VolumeSerialNumber
= drive
->serial
;
7063 info
->VolumeLabelLength
= min( wcslen( label
) * sizeof(WCHAR
),
7064 length
- offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) );
7065 info
->SupportsObjects
= (drive
->fs_type
== MOUNTMGR_FS_TYPE_NTFS
);
7066 memcpy( info
->VolumeLabel
, label
, info
->VolumeLabelLength
);
7067 io
->Information
= offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) + info
->VolumeLabelLength
;
7068 status
= STATUS_SUCCESS
;
7072 case FileFsControlInformation
:
7073 FIXME( "%p: control info not supported\n", handle
);
7074 status
= STATUS_NOT_IMPLEMENTED
;
7077 case FileFsFullSizeInformation
:
7078 if (length
< sizeof(FILE_FS_FULL_SIZE_INFORMATION
))
7079 status
= STATUS_BUFFER_TOO_SMALL
;
7082 FILE_FS_FULL_SIZE_INFORMATION
*info
= buffer
;
7083 if ((status
= get_full_size_info(fd
, info
)) == STATUS_SUCCESS
)
7084 io
->Information
= sizeof(*info
);
7088 case FileFsObjectIdInformation
:
7089 FIXME( "%p: object id info not supported\n", handle
);
7090 status
= STATUS_NOT_IMPLEMENTED
;
7093 case FileFsMaximumInformation
:
7094 FIXME( "%p: maximum info not supported\n", handle
);
7095 status
= STATUS_NOT_IMPLEMENTED
;
7099 status
= STATUS_INVALID_PARAMETER
;
7102 if (needs_close
) close( fd
);
7103 return io
->Status
= status
;
7107 /******************************************************************************
7108 * NtSetVolumeInformationFile (NTDLL.@)
7110 NTSTATUS WINAPI
NtSetVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *info
,
7111 ULONG length
, FS_INFORMATION_CLASS
class )
7113 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle
, io
, info
, (int)length
, class );
7114 return STATUS_SUCCESS
;
7118 /******************************************************************
7119 * NtQueryEaFile (NTDLL.@)
7121 NTSTATUS WINAPI
NtQueryEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
7122 BOOLEAN single_entry
, void *list
, ULONG list_len
,
7123 ULONG
*index
, BOOLEAN restart
)
7125 int fd
, needs_close
;
7128 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) semi-stub\n",
7129 handle
, io
, buffer
, (int)length
, single_entry
, list
, (int)list_len
, index
, restart
);
7131 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
7134 if (buffer
&& length
)
7135 memset( buffer
, 0, length
);
7137 if (needs_close
) close( fd
);
7138 return STATUS_NO_EAS_ON_FILE
;
7142 /******************************************************************
7143 * NtSetEaFile (NTDLL.@)
7145 NTSTATUS WINAPI
NtSetEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
)
7147 FIXME( "(%p,%p,%p,%d) stub\n", handle
, io
, buffer
, (int)length
);
7148 return STATUS_ACCESS_DENIED
;
7152 /* convert type information from server format; helper for NtQueryObject */
7153 static void *put_object_type_info( OBJECT_TYPE_INFORMATION
*p
, struct object_type_info
*info
)
7155 const ULONG align
= sizeof(DWORD_PTR
) - 1;
7157 memset( p
, 0, sizeof(*p
) );
7158 p
->TypeName
.Buffer
= (WCHAR
*)(p
+ 1);
7159 p
->TypeName
.Length
= info
->name_len
;
7160 p
->TypeName
.MaximumLength
= info
->name_len
+ sizeof(WCHAR
);
7161 p
->TotalNumberOfObjects
= info
->obj_count
;
7162 p
->TotalNumberOfHandles
= info
->handle_count
;
7163 p
->HighWaterNumberOfObjects
= info
->obj_max
;
7164 p
->HighWaterNumberOfHandles
= info
->handle_max
;
7165 p
->TypeIndex
= info
->index
+ 2;
7166 p
->GenericMapping
.GenericRead
= info
->mapping
.read
;
7167 p
->GenericMapping
.GenericWrite
= info
->mapping
.write
;
7168 p
->GenericMapping
.GenericExecute
= info
->mapping
.exec
;
7169 p
->GenericMapping
.GenericAll
= info
->mapping
.all
;
7170 p
->ValidAccessMask
= info
->valid_access
;
7171 memcpy( p
->TypeName
.Buffer
, info
+ 1, info
->name_len
);
7172 p
->TypeName
.Buffer
[info
->name_len
/ sizeof(WCHAR
)] = 0;
7173 return (char *)(p
+ 1) + ((p
->TypeName
.MaximumLength
+ align
) & ~align
);
7176 /**************************************************************************
7177 * NtQueryObject (NTDLL.@)
7179 NTSTATUS WINAPI
NtQueryObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
7180 void *ptr
, ULONG len
, ULONG
*used_len
)
7182 unsigned int status
;
7184 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle
, info_class
, ptr
, (int)len
, used_len
);
7186 if (used_len
) *used_len
= 0;
7190 case ObjectBasicInformation
:
7192 OBJECT_BASIC_INFORMATION
*p
= ptr
;
7194 if (len
< sizeof(*p
)) return STATUS_INFO_LENGTH_MISMATCH
;
7196 SERVER_START_REQ( get_object_info
)
7198 req
->handle
= wine_server_obj_handle( handle
);
7199 status
= wine_server_call( req
);
7200 if (status
== STATUS_SUCCESS
)
7202 memset( p
, 0, sizeof(*p
) );
7203 p
->GrantedAccess
= reply
->access
;
7204 p
->PointerCount
= reply
->ref_count
;
7205 p
->HandleCount
= reply
->handle_count
;
7206 if (used_len
) *used_len
= sizeof(*p
);
7213 case ObjectNameInformation
:
7215 OBJECT_NAME_INFORMATION
*p
= ptr
;
7219 /* first try as a file object */
7221 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
7223 if (!(status
= unix_to_nt_file_name( unix_name
, &nt_name
)))
7225 ULONG size
= (wcslen(nt_name
) + 1) * sizeof(WCHAR
);
7226 if (len
< sizeof(*p
)) status
= STATUS_INFO_LENGTH_MISMATCH
;
7227 else if (len
< sizeof(*p
) + size
) status
= STATUS_BUFFER_OVERFLOW
;
7230 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
7231 p
->Name
.Length
= size
- sizeof(WCHAR
);
7232 p
->Name
.MaximumLength
= size
;
7233 wcscpy( p
->Name
.Buffer
, nt_name
);
7235 if (used_len
) *used_len
= sizeof(*p
) + size
;
7241 else if (status
!= STATUS_OBJECT_TYPE_MISMATCH
) break;
7243 /* not a file, treat as a generic object */
7245 SERVER_START_REQ( get_object_name
)
7247 req
->handle
= wine_server_obj_handle( handle
);
7248 if (len
> sizeof(*p
)) wine_server_set_reply( req
, p
+ 1, len
- sizeof(*p
) );
7249 status
= wine_server_call( req
);
7250 if (status
== STATUS_SUCCESS
)
7252 if (!reply
->total
) /* no name */
7254 if (sizeof(*p
) > len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
7255 else memset( p
, 0, sizeof(*p
) );
7256 if (used_len
) *used_len
= sizeof(*p
);
7258 else if (sizeof(*p
) + reply
->total
+ sizeof(WCHAR
) > len
)
7260 if (used_len
) *used_len
= sizeof(*p
) + reply
->total
+ sizeof(WCHAR
);
7261 status
= STATUS_INFO_LENGTH_MISMATCH
;
7265 ULONG res
= wine_server_reply_size( reply
);
7266 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
7267 p
->Name
.Length
= res
;
7268 p
->Name
.MaximumLength
= res
+ sizeof(WCHAR
);
7269 p
->Name
.Buffer
[res
/ sizeof(WCHAR
)] = 0;
7270 if (used_len
) *used_len
= sizeof(*p
) + p
->Name
.MaximumLength
;
7278 case ObjectTypeInformation
:
7280 OBJECT_TYPE_INFORMATION
*p
= ptr
;
7281 char buffer
[sizeof(struct object_type_info
) + 64];
7282 struct object_type_info
*info
= (struct object_type_info
*)buffer
;
7284 SERVER_START_REQ( get_object_type
)
7286 req
->handle
= wine_server_obj_handle( handle
);
7287 wine_server_set_reply( req
, buffer
, sizeof(buffer
) );
7288 status
= wine_server_call( req
);
7292 if (sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
) <= len
)
7294 put_object_type_info( p
, info
);
7295 if (used_len
) *used_len
= sizeof(*p
) + p
->TypeName
.MaximumLength
;
7299 if (used_len
) *used_len
= sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
);
7300 status
= STATUS_INFO_LENGTH_MISMATCH
;
7305 case ObjectTypesInformation
:
7307 OBJECT_TYPES_INFORMATION
*types
= ptr
;
7308 OBJECT_TYPE_INFORMATION
*p
;
7309 struct object_type_info
*buffer
;
7310 /* assume at most 32 types, with an average 16-char name */
7311 UINT size
= 32 * (sizeof(struct object_type_info
) + 16 * sizeof(WCHAR
));
7312 UINT i
, count
, pos
, total
, align
= sizeof(DWORD_PTR
) - 1;
7314 buffer
= malloc( size
);
7315 SERVER_START_REQ( get_object_types
)
7317 wine_server_set_reply( req
, buffer
, size
);
7318 status
= wine_server_call( req
);
7319 count
= reply
->count
;
7324 if (len
>= sizeof(*types
)) types
->NumberOfTypes
= count
;
7325 total
= (sizeof(*types
) + align
) & ~align
;
7326 p
= (OBJECT_TYPE_INFORMATION
*)((char *)ptr
+ total
);
7327 for (i
= pos
= 0; i
< count
; i
++)
7329 struct object_type_info
*info
= (struct object_type_info
*)((char *)buffer
+ pos
);
7330 pos
+= sizeof(*info
) + ((info
->name_len
+ 3) & ~3);
7331 total
+= sizeof(*p
) + ((info
->name_len
+ sizeof(WCHAR
) + align
) & ~align
);
7332 if (total
<= len
) p
= put_object_type_info( p
, info
);
7334 if (used_len
) *used_len
= total
;
7335 if (total
> len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
7337 else if (status
== STATUS_BUFFER_OVERFLOW
) FIXME( "size %u too small\n", size
);
7343 case ObjectHandleFlagInformation
:
7345 OBJECT_HANDLE_FLAG_INFORMATION
* p
= ptr
;
7347 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
7349 SERVER_START_REQ( set_handle_info
)
7351 req
->handle
= wine_server_obj_handle( handle
);
7354 status
= wine_server_call( req
);
7355 if (status
== STATUS_SUCCESS
)
7357 p
->Inherit
= (reply
->old_flags
& HANDLE_FLAG_INHERIT
) != 0;
7358 p
->ProtectFromClose
= (reply
->old_flags
& HANDLE_FLAG_PROTECT_FROM_CLOSE
) != 0;
7359 if (used_len
) *used_len
= sizeof(*p
);
7367 FIXME("Unsupported information class %u\n", info_class
);
7368 status
= STATUS_NOT_IMPLEMENTED
;
7375 /**************************************************************************
7376 * NtSetInformationObject (NTDLL.@)
7378 NTSTATUS WINAPI
NtSetInformationObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
7379 void *ptr
, ULONG len
)
7381 unsigned int status
;
7383 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle
, info_class
, ptr
, (int)len
);
7387 case ObjectHandleFlagInformation
:
7389 OBJECT_HANDLE_FLAG_INFORMATION
* p
= ptr
;
7391 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
7393 SERVER_START_REQ( set_handle_info
)
7395 req
->handle
= wine_server_obj_handle( handle
);
7396 req
->mask
= HANDLE_FLAG_INHERIT
| HANDLE_FLAG_PROTECT_FROM_CLOSE
;
7397 if (p
->Inherit
) req
->flags
|= HANDLE_FLAG_INHERIT
;
7398 if (p
->ProtectFromClose
) req
->flags
|= HANDLE_FLAG_PROTECT_FROM_CLOSE
;
7399 status
= wine_server_call( req
);
7406 FIXME("Unsupported information class %u\n", info_class
);
7407 status
= STATUS_NOT_IMPLEMENTED
;