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>
108 #include "ntstatus.h"
109 #define WIN32_NO_STATUS
110 #define NONAMELESSUNION
113 #include "winioctl.h"
114 #include "winternl.h"
115 #include "ddk/ntddk.h"
116 #include "ddk/ntddser.h"
118 #define WINE_MOUNTMGR_EXTENSIONS
119 #include "ddk/mountmgr.h"
120 #include "wine/server.h"
121 #include "wine/list.h"
122 #include "wine/debug.h"
123 #include "unix_private.h"
125 WINE_DEFAULT_DEBUG_CHANNEL(file
);
126 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
128 #define MAX_DOS_DRIVES 26
130 /* just in case... */
131 #undef VFAT_IOCTL_READDIR_BOTH
132 #undef EXT2_IOC_GETFLAGS
133 #undef EXT4_CASEFOLD_FL
137 /* We want the real kernel dirent structure, not the libc one */
142 unsigned short d_reclen
;
146 /* Define the VFAT ioctl to get both short and long file names */
147 #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, KERNEL_DIRENT [2] )
149 /* Define the ext2 ioctl for handling extra attributes */
150 #define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
152 /* Case-insensitivity attribute */
153 #define EXT4_CASEFOLD_FL 0x40000000
156 # define O_DIRECTORY 0200000 /* must be directory */
159 #ifndef AT_NO_AUTOMOUNT
160 #define AT_NO_AUTOMOUNT 0x800
165 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
167 #define INVALID_NT_CHARS '*','?','<','>','|','"'
168 #define INVALID_DOS_CHARS INVALID_NT_CHARS,'+','=',',',';','[',']',' ','\345'
170 #define MAX_DIR_ENTRY_LEN 255 /* max length of a directory entry in chars */
172 #define MAX_IGNORED_FILES 4
174 #define SAMBA_XATTR_DOS_ATTRIB "user.DOSATTRIB"
175 #define XATTR_ATTRIBS_MASK (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)
183 static struct file_identity ignored_files
[MAX_IGNORED_FILES
];
184 static unsigned int ignored_files_count
;
186 union file_directory_info
189 FILE_DIRECTORY_INFORMATION dir
;
190 FILE_BOTH_DIRECTORY_INFORMATION both
;
191 FILE_FULL_DIRECTORY_INFORMATION full
;
192 FILE_ID_BOTH_DIRECTORY_INFORMATION id_both
;
193 FILE_ID_FULL_DIRECTORY_INFORMATION id_full
;
194 FILE_ID_GLOBAL_TX_DIR_INFORMATION id_tx
;
195 FILE_NAMES_INFORMATION names
;
198 struct dir_data_buffer
200 struct dir_data_buffer
*next
; /* next buffer in the list */
201 unsigned int size
; /* total size of the buffer */
202 unsigned int pos
; /* current position in the buffer */
206 struct dir_data_names
208 const WCHAR
*long_name
; /* long file name in Unicode */
209 const WCHAR
*short_name
; /* short file name in Unicode */
210 const char *unix_name
; /* Unix file name in host encoding */
215 unsigned int size
; /* size of the names array */
216 unsigned int count
; /* count of used entries in the names array */
217 unsigned int pos
; /* current reading position in the names array */
218 struct file_identity id
; /* directory file identity */
219 struct dir_data_names
*names
; /* directory file names */
220 struct dir_data_buffer
*buffer
; /* head of data buffers list */
223 static const unsigned int dir_data_buffer_initial_size
= 4096;
224 static const unsigned int dir_data_cache_initial_size
= 256;
225 static const unsigned int dir_data_names_initial_size
= 64;
227 static struct dir_data
**dir_data_cache
;
228 static unsigned int dir_data_cache_size
;
230 static BOOL show_dot_files
;
231 static mode_t start_umask
;
233 /* at some point we may want to allow Winelib apps to set this */
234 static const BOOL is_case_sensitive
= FALSE
;
236 static pthread_mutex_t dir_mutex
= PTHREAD_MUTEX_INITIALIZER
;
237 static pthread_mutex_t mnt_mutex
= PTHREAD_MUTEX_INITIALIZER
;
239 /* check if a given Unicode char is OK in a DOS short name */
240 static inline BOOL
is_invalid_dos_char( WCHAR ch
)
242 static const WCHAR invalid_chars
[] = { INVALID_DOS_CHARS
,'~','.',0 };
243 if (ch
> 0x7f) return TRUE
;
244 return wcschr( invalid_chars
, ch
) != NULL
;
247 /* check if the device can be a mounted volume */
248 static inline BOOL
is_valid_mounted_device( const struct stat
*st
)
250 #if defined(linux) || defined(__sun__)
251 return S_ISBLK( st
->st_mode
);
253 /* disks are char devices on *BSD */
254 return S_ISCHR( st
->st_mode
);
258 static inline void ignore_file( const char *name
)
261 assert( ignored_files_count
< MAX_IGNORED_FILES
);
262 if (!stat( name
, &st
))
264 ignored_files
[ignored_files_count
].dev
= st
.st_dev
;
265 ignored_files
[ignored_files_count
].ino
= st
.st_ino
;
266 ignored_files_count
++;
270 static inline BOOL
is_same_file( const struct file_identity
*file
, const struct stat
*st
)
272 return st
->st_dev
== file
->dev
&& st
->st_ino
== file
->ino
;
275 static inline BOOL
is_ignored_file( const struct stat
*st
)
279 for (i
= 0; i
< ignored_files_count
; i
++)
280 if (is_same_file( &ignored_files
[i
], st
)) return TRUE
;
284 static inline unsigned int dir_info_align( unsigned int len
)
286 return (len
+ 7) & ~7;
289 static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS
class, unsigned int len
)
293 case FileDirectoryInformation
:
294 return offsetof( FILE_DIRECTORY_INFORMATION
, FileName
[len
] );
295 case FileBothDirectoryInformation
:
296 return offsetof( FILE_BOTH_DIRECTORY_INFORMATION
, FileName
[len
] );
297 case FileFullDirectoryInformation
:
298 return offsetof( FILE_FULL_DIRECTORY_INFORMATION
, FileName
[len
] );
299 case FileIdBothDirectoryInformation
:
300 return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION
, FileName
[len
] );
301 case FileIdFullDirectoryInformation
:
302 return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION
, FileName
[len
] );
303 case FileIdGlobalTxDirectoryInformation
:
304 return offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION
, FileName
[len
] );
305 case FileNamesInformation
:
306 return offsetof( FILE_NAMES_INFORMATION
, FileName
[len
] );
313 static inline BOOL
has_wildcard( const UNICODE_STRING
*mask
)
317 if (!mask
) return TRUE
;
318 for (i
= 0; i
< mask
->Length
/ sizeof(WCHAR
); i
++)
319 if (mask
->Buffer
[i
] == '*' || mask
->Buffer
[i
] == '?') return TRUE
;
323 NTSTATUS
errno_to_status( int err
)
325 TRACE( "errno = %d\n", err
);
328 case EAGAIN
: return STATUS_SHARING_VIOLATION
;
329 case EBADF
: return STATUS_INVALID_HANDLE
;
330 case EBUSY
: return STATUS_DEVICE_BUSY
;
331 case ENOSPC
: return STATUS_DISK_FULL
;
334 case EACCES
: return STATUS_ACCESS_DENIED
;
335 case ENOTDIR
: return STATUS_OBJECT_PATH_NOT_FOUND
;
336 case ENOENT
: return STATUS_OBJECT_NAME_NOT_FOUND
;
337 case EISDIR
: return STATUS_INVALID_DEVICE_REQUEST
;
339 case ENFILE
: return STATUS_TOO_MANY_OPENED_FILES
;
340 case EINVAL
: return STATUS_INVALID_PARAMETER
;
341 case ENOTEMPTY
: return STATUS_DIRECTORY_NOT_EMPTY
;
342 case EPIPE
: return STATUS_PIPE_DISCONNECTED
;
343 case EIO
: return STATUS_DEVICE_NOT_READY
;
345 case ENOMEDIUM
: return STATUS_NO_MEDIA_IN_DEVICE
;
347 case ENXIO
: return STATUS_NO_SUCH_DEVICE
;
349 case EOPNOTSUPP
:return STATUS_NOT_SUPPORTED
;
350 case ECONNRESET
:return STATUS_PIPE_DISCONNECTED
;
351 case EFAULT
: return STATUS_ACCESS_VIOLATION
;
352 case ESPIPE
: return STATUS_ILLEGAL_FUNCTION
;
353 case ELOOP
: return STATUS_REPARSE_POINT_NOT_RESOLVED
;
354 #ifdef ETIME /* Missing on FreeBSD */
355 case ETIME
: return STATUS_IO_TIMEOUT
;
357 case ENOEXEC
: /* ?? */
358 case EEXIST
: /* ?? */
360 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err
);
361 return STATUS_UNSUCCESSFUL
;
366 static int xattr_fremove( int filedes
, const char *name
)
368 #ifdef HAVE_SYS_XATTR_H
369 # ifdef XATTR_ADDITIONAL_OPTIONS
370 return fremovexattr( filedes
, name
, 0 );
372 return fremovexattr( filedes
, name
);
381 static int xattr_fset( int filedes
, const char *name
, const void *value
, size_t size
)
383 #ifdef HAVE_SYS_XATTR_H
384 # ifdef XATTR_ADDITIONAL_OPTIONS
385 return fsetxattr( filedes
, name
, value
, size
, 0, 0 );
387 return fsetxattr( filedes
, name
, value
, size
, 0 );
396 static int xattr_get( const char *path
, const char *name
, void *value
, size_t size
)
398 #ifdef HAVE_SYS_XATTR_H
399 # ifdef XATTR_ADDITIONAL_OPTIONS
400 return getxattr( path
, name
, value
, size
, 0, 0 );
402 return getxattr( path
, name
, value
, size
);
411 static int xattr_fget( int filedes
, const char *name
, void *value
, size_t size
)
413 #ifdef HAVE_SYS_XATTR_H
414 # ifdef XATTR_ADDITIONAL_OPTIONS
415 return fgetxattr( filedes
, name
, value
, size
, 0, 0 );
417 return fgetxattr( filedes
, name
, value
, size
);
426 /* get space from the current directory data buffer, allocating a new one if necessary */
427 static void *get_dir_data_space( struct dir_data
*data
, unsigned int size
)
429 struct dir_data_buffer
*buffer
= data
->buffer
;
432 if (!buffer
|| size
> buffer
->size
- buffer
->pos
)
434 unsigned int new_size
= buffer
? buffer
->size
* 2 : dir_data_buffer_initial_size
;
435 if (new_size
< size
) new_size
= size
;
436 if (!(buffer
= malloc( offsetof( struct dir_data_buffer
, data
[new_size
] ) ))) return NULL
;
438 buffer
->size
= new_size
;
439 buffer
->next
= data
->buffer
;
440 data
->buffer
= buffer
;
442 ret
= buffer
->data
+ buffer
->pos
;
447 /* add a string to the directory data buffer */
448 static const char *add_dir_data_nameA( struct dir_data
*data
, const char *name
)
450 /* keep buffer data WCHAR-aligned */
451 char *ptr
= get_dir_data_space( data
, (strlen( name
) + sizeof(WCHAR
)) & ~(sizeof(WCHAR
) - 1) );
452 if (ptr
) strcpy( ptr
, name
);
456 /* add a Unicode string to the directory data buffer */
457 static const WCHAR
*add_dir_data_nameW( struct dir_data
*data
, const WCHAR
*name
)
459 WCHAR
*ptr
= get_dir_data_space( data
, (wcslen( name
) + 1) * sizeof(WCHAR
) );
460 if (ptr
) wcscpy( ptr
, name
);
464 /* add an entry to the directory names array */
465 static BOOL
add_dir_data_names( struct dir_data
*data
, const WCHAR
*long_name
,
466 const WCHAR
*short_name
, const char *unix_name
)
468 static const WCHAR empty
[1];
469 struct dir_data_names
*names
= data
->names
;
471 if (data
->count
>= data
->size
)
473 unsigned int new_size
= max( data
->size
* 2, dir_data_names_initial_size
);
475 if (!(names
= realloc( names
, new_size
* sizeof(*names
) ))) return FALSE
;
476 data
->size
= new_size
;
482 if (!(names
[data
->count
].short_name
= add_dir_data_nameW( data
, short_name
))) return FALSE
;
484 else names
[data
->count
].short_name
= empty
;
486 if (!(names
[data
->count
].long_name
= add_dir_data_nameW( data
, long_name
))) return FALSE
;
487 if (!(names
[data
->count
].unix_name
= add_dir_data_nameA( data
, unix_name
))) return FALSE
;
492 /* free the complete directory data structure */
493 static void free_dir_data( struct dir_data
*data
)
495 struct dir_data_buffer
*buffer
, *next
;
499 for (buffer
= data
->buffer
; buffer
; buffer
= next
)
509 /* support for a directory queue for filesystem searches */
517 static struct list dir_queue
= LIST_INIT( dir_queue
);
519 static NTSTATUS
add_dir_to_queue( const char *name
)
521 int len
= strlen( name
) + 1;
522 struct dir_name
*dir
= malloc( offsetof( struct dir_name
, name
[len
] ));
523 if (!dir
) return STATUS_NO_MEMORY
;
524 strcpy( dir
->name
, name
);
525 list_add_tail( &dir_queue
, &dir
->entry
);
526 return STATUS_SUCCESS
;
529 static NTSTATUS
next_dir_in_queue( char *name
)
531 struct list
*head
= list_head( &dir_queue
);
534 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
535 strcpy( name
, dir
->name
);
536 list_remove( &dir
->entry
);
538 return STATUS_SUCCESS
;
540 return STATUS_OBJECT_NAME_NOT_FOUND
;
543 static void flush_dir_queue(void)
547 while ((head
= list_head( &dir_queue
)))
549 struct dir_name
*dir
= LIST_ENTRY( head
, struct dir_name
, entry
);
550 list_remove( &dir
->entry
);
558 static char *unescape_field( char *str
)
562 for (in
= out
= str
; *in
; in
++, out
++)
572 else if (in
[1] == '0' && in
[2] == '4' && in
[3] == '0')
577 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '1')
582 else if (in
[1] == '0' && in
[2] == '1' && in
[3] == '2')
587 else if (in
[1] == '1' && in
[2] == '3' && in
[3] == '4')
599 static inline char *get_field( char **str
)
603 ret
= strsep( str
, " \t" );
604 if (*str
) *str
+= strspn( *str
, " \t" );
608 /************************************************************************
609 * getmntent_replacement
611 * getmntent replacement for Android.
613 * NB returned static buffer is not thread safe; protect with mnt_mutex.
615 static struct mntent
*getmntent_replacement( FILE *f
)
617 static struct mntent entry
;
618 static char buf
[4096];
623 if (!fgets( buf
, sizeof(buf
), f
)) return NULL
;
624 p
= strchr( buf
, '\n' );
626 else /* Partially unread line, move file ptr to end */
629 while (fgets( tmp
, sizeof(tmp
), f
))
630 if (strchr( tmp
, '\n' )) break;
632 start
= buf
+ strspn( buf
, " \t" );
633 } while (start
[0] == '\0' || start
[0] == '#');
635 p
= get_field( &start
);
636 entry
.mnt_fsname
= p
? unescape_field( p
) : (char *)"";
638 p
= get_field( &start
);
639 entry
.mnt_dir
= p
? unescape_field( p
) : (char *)"";
641 p
= get_field( &start
);
642 entry
.mnt_type
= p
? unescape_field( p
) : (char *)"";
644 p
= get_field( &start
);
645 entry
.mnt_opts
= p
? unescape_field( p
) : (char *)"";
647 p
= get_field( &start
);
648 entry
.mnt_freq
= p
? atoi(p
) : 0;
650 p
= get_field( &start
);
651 entry
.mnt_passno
= p
? atoi(p
) : 0;
655 #define getmntent getmntent_replacement
658 /***********************************************************************
659 * parse_mount_entries
661 * Parse mount entries looking for a given device. Helper for get_default_drive_device.
665 #include <sys/vfstab.h>
666 static char *parse_vfstab_entries( FILE *f
, dev_t dev
, ino_t ino
)
672 while (! getvfsent( f
, &entry
))
674 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
675 if (!strcmp( entry
.vfs_fstype
, "nfs" ) ||
676 !strcmp( entry
.vfs_fstype
, "smbfs" ) ||
677 !strcmp( entry
.vfs_fstype
, "ncpfs" )) continue;
679 if (stat( entry
.vfs_mountp
, &st
) == -1) continue;
680 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
681 if (!strcmp( entry
.vfs_fstype
, "fd" ))
683 if ((device
= strstr( entry
.vfs_mntopts
, "dev=" )))
685 char *p
= strchr( device
+ 4, ',' );
691 return entry
.vfs_special
;
698 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
700 struct mntent
*entry
;
704 while ((entry
= getmntent( f
)))
706 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
707 if (!strcmp( entry
->mnt_type
, "nfs" ) ||
708 !strcmp( entry
->mnt_type
, "cifs" ) ||
709 !strcmp( entry
->mnt_type
, "smbfs" ) ||
710 !strcmp( entry
->mnt_type
, "ncpfs" )) continue;
712 if (stat( entry
->mnt_dir
, &st
) == -1) continue;
713 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
714 if (!strcmp( entry
->mnt_type
, "supermount" ))
716 if ((device
= strstr( entry
->mnt_opts
, "dev=" )))
718 char *p
= strchr( device
+ 4, ',' );
723 else if (!stat( entry
->mnt_fsname
, &st
) && S_ISREG(st
.st_mode
))
725 /* if device is a regular file check for a loop mount */
726 if ((device
= strstr( entry
->mnt_opts
, "loop=" )))
728 char *p
= strchr( device
+ 5, ',' );
734 return entry
->mnt_fsname
;
740 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
742 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
747 while ((entry
= getfsent()))
749 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
750 if (!strcmp( entry
->fs_vfstype
, "nfs" ) ||
751 !strcmp( entry
->fs_vfstype
, "smbfs" ) ||
752 !strcmp( entry
->fs_vfstype
, "ncpfs" )) continue;
754 if (stat( entry
->fs_file
, &st
) == -1) continue;
755 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
756 return entry
->fs_spec
;
763 #include <sys/mnttab.h>
764 static char *parse_mount_entries( FILE *f
, dev_t dev
, ino_t ino
)
771 while (( ! getmntent( f
, &entry
) ))
773 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
774 if (!strcmp( entry
.mnt_fstype
, "nfs" ) ||
775 !strcmp( entry
.mnt_fstype
, "smbfs" ) ||
776 !strcmp( entry
.mnt_fstype
, "ncpfs" )) continue;
778 if (stat( entry
.mnt_mountp
, &st
) == -1) continue;
779 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
780 if (!strcmp( entry
.mnt_fstype
, "fd" ))
782 if ((device
= strstr( entry
.mnt_mntopts
, "dev=" )))
784 char *p
= strchr( device
+ 4, ',' );
790 return entry
.mnt_special
;
796 /***********************************************************************
797 * get_default_drive_device
799 * Return the default device to use for a given drive mount point.
801 static char *get_default_drive_device( const char *root
)
811 /* try to open it first to force it to get mounted */
812 if ((fd
= open( root
, O_RDONLY
| O_DIRECTORY
)) != -1)
814 res
= fstat( fd
, &st
);
817 /* now try normal stat just in case */
818 if (res
== -1) res
= stat( root
, &st
);
819 if (res
== -1) return NULL
;
821 mutex_lock( &mnt_mutex
);
824 if ((f
= fopen( "/proc/mounts", "r" )))
826 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
830 if ((f
= fopen( "/etc/mtab", "r" )))
832 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
835 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
836 if (!device
&& (f
= fopen( "/etc/fstab", "r" )))
838 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
842 if (device
) ret
= strdup( device
);
843 mutex_unlock( &mnt_mutex
);
845 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__ ) || defined(__DragonFly__)
850 /* try to open it first to force it to get mounted */
851 if ((fd
= open( root
, O_RDONLY
)) != -1)
853 res
= fstat( fd
, &st
);
856 /* now try normal stat just in case */
857 if (res
== -1) res
= stat( root
, &st
);
858 if (res
== -1) return NULL
;
860 mutex_lock( &mnt_mutex
);
862 /* The FreeBSD parse_mount_entries doesn't require a file argument, so just
863 * pass NULL. Leave the argument in for symmetry.
865 device
= parse_mount_entries( NULL
, st
.st_dev
, st
.st_ino
);
866 if (device
) ret
= strdup( device
);
867 mutex_unlock( &mnt_mutex
);
875 /* try to open it first to force it to get mounted */
876 if ((fd
= open( root
, O_RDONLY
)) != -1)
878 res
= fstat( fd
, &st
);
881 /* now try normal stat just in case */
882 if (res
== -1) res
= stat( root
, &st
);
883 if (res
== -1) return NULL
;
885 mutex_lock( &mnt_mutex
);
887 if ((f
= fopen( "/etc/mnttab", "r" )))
889 device
= parse_mount_entries( f
, st
.st_dev
, st
.st_ino
);
892 /* look through fstab too in case it's not mounted (for instance if it's an audio CD) */
893 if (!device
&& (f
= fopen( "/etc/vfstab", "r" )))
895 device
= parse_vfstab_entries( f
, st
.st_dev
, st
.st_ino
);
898 if (device
) ret
= strdup( device
);
899 mutex_unlock( &mnt_mutex
);
901 #elif defined(__APPLE__)
902 struct statfs
*mntStat
;
908 static const char path_bsd_device
[] = "/dev/disk";
911 res
= stat( root
, &st
);
912 if (res
== -1) return NULL
;
917 mutex_lock( &mnt_mutex
);
919 mntSize
= getmntinfo(&mntStat
, MNT_NOWAIT
);
921 for (i
= 0; i
< mntSize
&& !ret
; i
++)
923 if (stat(mntStat
[i
].f_mntonname
, &st
) == -1) continue;
924 if (st
.st_dev
!= dev
|| st
.st_ino
!= ino
) continue;
926 /* FIXME add support for mounted network drive */
927 if ( strncmp(mntStat
[i
].f_mntfromname
, path_bsd_device
, strlen(path_bsd_device
)) == 0)
929 /* set return value to the corresponding raw BSD node */
930 ret
= malloc( strlen(mntStat
[i
].f_mntfromname
) + 2 /* 2 : r and \0 */ );
933 strcpy(ret
, "/dev/r");
934 strcat(ret
, mntStat
[i
].f_mntfromname
+sizeof("/dev/")-1);
938 mutex_unlock( &mnt_mutex
);
941 if (!warned
++) FIXME( "auto detection of DOS devices not supported on this platform\n" );
947 /***********************************************************************
948 * get_device_mount_point
950 * Return the current mount point for a device.
952 static char *get_device_mount_point( dev_t dev
)
959 mutex_lock( &mnt_mutex
);
962 if ((f
= fopen( "/proc/mounts", "r" )))
964 if ((f
= fopen( "/etc/mtab", "r" )))
967 struct mntent
*entry
;
971 while ((entry
= getmntent( f
)))
973 /* don't even bother stat'ing network mounts, there's no meaningful device anyway */
974 if (!strcmp( entry
->mnt_type
, "nfs" ) ||
975 !strcmp( entry
->mnt_type
, "cifs" ) ||
976 !strcmp( entry
->mnt_type
, "smbfs" ) ||
977 !strcmp( entry
->mnt_type
, "ncpfs" )) continue;
979 if (!strcmp( entry
->mnt_type
, "supermount" ))
981 if ((device
= strstr( entry
->mnt_opts
, "dev=" )))
984 if ((p
= strchr( device
, ',' ))) *p
= 0;
987 else if (!stat( entry
->mnt_fsname
, &st
) && S_ISREG(st
.st_mode
))
989 /* if device is a regular file check for a loop mount */
990 if ((device
= strstr( entry
->mnt_opts
, "loop=" )))
993 if ((p
= strchr( device
, ',' ))) *p
= 0;
996 else device
= entry
->mnt_fsname
;
998 if (device
&& !stat( device
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== dev
)
1000 ret
= strdup( entry
->mnt_dir
);
1006 mutex_unlock( &mnt_mutex
);
1007 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1008 struct statfs
*entry
;
1012 mutex_lock( &mnt_mutex
);
1014 size
= getmntinfo( &entry
, MNT_NOWAIT
);
1015 for (i
= 0; i
< size
; i
++)
1017 if (stat( entry
[i
].f_mntfromname
, &st
) == -1) continue;
1018 if (S_ISBLK(st
.st_mode
) && st
.st_rdev
== dev
)
1020 ret
= strdup( entry
[i
].f_mntonname
);
1024 mutex_unlock( &mnt_mutex
);
1027 if (!warned
++) FIXME( "unmounting devices not supported on this platform\n" );
1033 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1034 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1047 BOOLEAN case_sensitive
;
1053 vol_capabilities_attr_t caps
;
1056 /***********************************************************************
1059 * Checks if the specified file system is in the cache.
1061 static struct fs_cache
*look_up_fs_cache( dev_t dev
)
1064 for (i
= 0; i
< ARRAY_SIZE( fs_cache
); i
++)
1065 if (fs_cache
[i
].dev
== dev
)
1070 /***********************************************************************
1073 * Adds the specified file system to the cache.
1075 static void add_fs_cache( dev_t dev
, fsid_t fsid
, BOOLEAN case_sensitive
)
1078 struct fs_cache
*entry
= look_up_fs_cache( dev
);
1079 static int once
= 0;
1082 /* Update the cache */
1084 entry
->case_sensitive
= case_sensitive
;
1088 /* Add a new entry */
1089 for (i
= 0; i
< ARRAY_SIZE( fs_cache
); i
++)
1090 if (fs_cache
[i
].dev
== 0)
1092 /* This entry is empty, use it */
1093 fs_cache
[i
].dev
= dev
;
1094 fs_cache
[i
].fsid
= fsid
;
1095 fs_cache
[i
].case_sensitive
= case_sensitive
;
1099 /* Cache is out of space, warn */
1101 WARN( "FS cache is out of space, expect performance problems\n" );
1104 /***********************************************************************
1105 * get_dir_case_sensitivity_attr
1107 * Checks if the volume containing the specified directory is case
1108 * sensitive or not. Uses getattrlist(2).
1110 static int get_dir_case_sensitivity_attr( const char *dir
)
1113 struct attrlist attr
;
1114 struct vol_caps caps
;
1115 struct get_fsid get_fsid
;
1116 struct fs_cache
*entry
;
1118 /* First get the FS ID of the volume */
1119 attr
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1121 attr
.commonattr
= ATTR_CMN_DEVID
|ATTR_CMN_FSID
;
1122 attr
.volattr
= attr
.dirattr
= attr
.fileattr
= attr
.forkattr
= 0;
1124 if (getattrlist( dir
, &attr
, &get_fsid
, sizeof(get_fsid
), 0 ) != 0 ||
1125 get_fsid
.size
!= sizeof(get_fsid
))
1127 /* Try to look it up in the cache */
1128 entry
= look_up_fs_cache( get_fsid
.dev
);
1129 if (entry
&& !memcmp( &entry
->fsid
, &get_fsid
.fsid
, sizeof(fsid_t
) ))
1130 /* Cache lookup succeeded */
1131 return entry
->case_sensitive
;
1132 /* Cache is stale at this point, we have to update it */
1134 mntpoint
= get_device_mount_point( get_fsid
.dev
);
1135 /* Now look up the case-sensitivity */
1136 attr
.commonattr
= 0;
1137 attr
.volattr
= ATTR_VOL_INFO
|ATTR_VOL_CAPABILITIES
;
1138 if (getattrlist( mntpoint
, &attr
, &caps
, sizeof(caps
), 0 ) < 0)
1141 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, TRUE
);
1145 if (caps
.size
== sizeof(caps
) &&
1146 (caps
.caps
.valid
[VOL_CAPABILITIES_FORMAT
] &
1147 (VOL_CAP_FMT_CASE_SENSITIVE
| VOL_CAP_FMT_CASE_PRESERVING
)) ==
1148 (VOL_CAP_FMT_CASE_SENSITIVE
| VOL_CAP_FMT_CASE_PRESERVING
))
1152 if ((caps
.caps
.capabilities
[VOL_CAPABILITIES_FORMAT
] &
1153 VOL_CAP_FMT_CASE_SENSITIVE
) != VOL_CAP_FMT_CASE_SENSITIVE
)
1157 /* Update the cache */
1158 add_fs_cache( get_fsid
.dev
, get_fsid
.fsid
, ret
);
1165 /***********************************************************************
1166 * get_dir_case_sensitivity_stat
1168 * Checks if the volume containing the specified directory is case
1169 * sensitive or not. Uses (f)statfs(2), statvfs(2), fstatat(2), or ioctl(2).
1171 static BOOLEAN
get_dir_case_sensitivity_stat( const char *dir
)
1173 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1176 if (statfs( dir
, &stfs
) == -1) return TRUE
;
1177 /* Assume these file systems are always case insensitive.*/
1178 if (!strcmp( stfs
.f_fstypename
, "fusefs" ) &&
1179 !strncmp( stfs
.f_mntfromname
, "ciopfs", 5 ))
1181 /* msdosfs was case-insensitive since FreeBSD 8, if not earlier */
1182 if (!strcmp( stfs
.f_fstypename
, "msdosfs" ) ||
1183 /* older CIFS protocol versions uppercase filename on the client,
1184 * newer versions should be case-insensitive on the server anyway */
1185 !strcmp( stfs
.f_fstypename
, "smbfs" ))
1187 /* no ntfs-3g: modern fusefs has no way to report the filesystem on FreeBSD
1188 * no cd9660 or udf, they're case-sensitive on FreeBSD
1191 if (!strcmp( stfs
.f_fstypename
, "msdos" ) ||
1192 !strcmp( stfs
.f_fstypename
, "cd9660" ) ||
1193 !strcmp( stfs
.f_fstypename
, "udf" ) ||
1194 !strcmp( stfs
.f_fstypename
, "ntfs" ))
1196 #ifdef _DARWIN_FEATURE_64_BIT_INODE
1197 if (!strcmp( stfs
.f_fstypename
, "hfs" ) && (stfs
.f_fssubtype
== 0 ||
1198 stfs
.f_fssubtype
== 1 ||
1199 stfs
.f_fssubtype
== 128))
1202 /* The field says "reserved", but a quick look at the kernel source
1203 * tells us that this "reserved" field is really the same as the
1204 * "fssubtype" field from the inode64 structure (see munge_statfs()
1205 * in <xnu-source>/bsd/vfs/vfs_syscalls.c).
1207 if (!strcmp( stfs
.f_fstypename
, "hfs" ) && (stfs
.f_reserved1
== 0 ||
1208 stfs
.f_reserved1
== 1 ||
1209 stfs
.f_reserved1
== 128))
1215 #elif defined(__NetBSD__)
1216 struct statvfs stfs
;
1218 if (statvfs( dir
, &stfs
) == -1) return TRUE
;
1219 /* Only assume CIOPFS is case insensitive. */
1220 if (strcmp( stfs
.f_fstypename
, "fusefs" ) ||
1221 strncmp( stfs
.f_mntfromname
, "ciopfs", 5 ))
1225 #elif defined(__linux__)
1226 BOOLEAN sens
= TRUE
;
1231 if ((fd
= open( dir
, O_RDONLY
| O_NONBLOCK
)) == -1)
1234 if (ioctl( fd
, EXT2_IOC_GETFLAGS
, &flags
) != -1 && (flags
& EXT4_CASEFOLD_FL
))
1238 else if (fstatfs( fd
, &stfs
) == 0 && /* CIOPFS is case insensitive. Instead of */
1239 stfs
.f_type
== 0x65735546 /* FUSE_SUPER_MAGIC */ && /* parsing mtab to discover if the FUSE FS */
1240 fstatat( fd
, ".ciopfs", &st
, AT_NO_AUTOMOUNT
) == 0) /* is CIOPFS, look for .ciopfs in the dir. */
1253 /***********************************************************************
1254 * get_dir_case_sensitivity
1256 * Checks if the volume containing the specified directory is case
1257 * sensitive or not. Uses multiple methods, depending on platform.
1259 static BOOLEAN
get_dir_case_sensitivity( const char *dir
)
1261 #if defined(HAVE_GETATTRLIST) && defined(ATTR_VOL_CAPABILITIES) && \
1262 defined(VOL_CAPABILITIES_FORMAT) && defined(VOL_CAP_FMT_CASE_SENSITIVE)
1263 int case_sensitive
= get_dir_case_sensitivity_attr( dir
);
1264 if (case_sensitive
!= -1) return case_sensitive
;
1266 return get_dir_case_sensitivity_stat( dir
);
1270 /***********************************************************************
1273 * Check if the specified file should be hidden based on its name and the show dot files option.
1275 static BOOL
is_hidden_file( const UNICODE_STRING
*name
)
1279 if (show_dot_files
) return FALSE
;
1281 end
= p
= name
->Buffer
+ name
->Length
/sizeof(WCHAR
);
1282 while (p
> name
->Buffer
&& p
[-1] == '\\') p
--;
1283 while (p
> name
->Buffer
&& p
[-1] != '\\') p
--;
1284 return (p
< end
&& *p
== '.');
1288 /***********************************************************************
1289 * hash_short_file_name
1291 * Transform a Unix file name into a hashed DOS name. If the name is not a valid
1292 * DOS name, it is replaced by a hashed version that fits in 8.3 format.
1293 * 'buffer' must be at least 12 characters long.
1294 * Returns length of short name in bytes; short name is NOT null-terminated.
1296 static ULONG
hash_short_file_name( const WCHAR
*name
, int length
, LPWSTR buffer
)
1298 static const char hash_chars
[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
1300 LPCWSTR p
, ext
, end
= name
+ length
;
1302 unsigned short hash
;
1305 /* Compute the hash code of the file name */
1306 /* If you know something about hash functions, feel free to */
1307 /* insert a better algorithm here... */
1308 if (!is_case_sensitive
)
1310 for (p
= name
, hash
= 0xbeef; p
< end
- 1; p
++)
1311 hash
= (hash
<<3) ^ (hash
>>5) ^ towlower(*p
) ^ (towlower(p
[1]) << 8);
1312 hash
= (hash
<<3) ^ (hash
>>5) ^ towlower(*p
); /* Last character */
1316 for (p
= name
, hash
= 0xbeef; p
< end
- 1; p
++)
1317 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
^ (p
[1] << 8);
1318 hash
= (hash
<< 3) ^ (hash
>> 5) ^ *p
; /* Last character */
1321 /* Find last dot for start of the extension */
1322 for (p
= name
+ 1, ext
= NULL
; p
< end
- 1; p
++) if (*p
== '.') ext
= p
;
1324 /* Copy first 4 chars, replacing invalid chars with '_' */
1325 for (i
= 4, p
= name
, dst
= buffer
; i
> 0; i
--, p
++)
1327 if (p
== end
|| p
== ext
) break;
1328 *dst
++ = is_invalid_dos_char(*p
) ? '_' : *p
;
1330 /* Pad to 5 chars with '~' */
1331 while (i
-- >= 0) *dst
++ = '~';
1333 /* Insert hash code converted to 3 ASCII chars */
1334 *dst
++ = hash_chars
[(hash
>> 10) & 0x1f];
1335 *dst
++ = hash_chars
[(hash
>> 5) & 0x1f];
1336 *dst
++ = hash_chars
[hash
& 0x1f];
1338 /* Copy the first 3 chars of the extension (if any) */
1342 for (i
= 3, ext
++; (i
> 0) && ext
< end
; i
--, ext
++)
1343 *dst
++ = is_invalid_dos_char(*ext
) ? '_' : *ext
;
1345 return dst
- buffer
;
1349 /***********************************************************************
1352 * Check a long file name against a mask.
1354 * Tests (done in W95 DOS shell - case insensitive):
1355 * *.txt test1.test.txt *
1357 * *.t??????.t* test1.ta.tornado.txt *
1358 * *tornado* test1.ta.tornado.txt *
1359 * t*t test1.ta.tornado.txt *
1361 * ?est??? test1.txt -
1362 * *test1.txt* test1.txt *
1363 * h?l?o*t.dat hellothisisatest.dat *
1365 static BOOLEAN
match_filename( const WCHAR
*name
, int length
, const UNICODE_STRING
*mask_str
)
1368 const WCHAR
*mask
= mask_str
->Buffer
;
1369 const WCHAR
*name_end
= name
+ length
;
1370 const WCHAR
*mask_end
= mask
+ mask_str
->Length
/ sizeof(WCHAR
);
1371 const WCHAR
*lastjoker
= NULL
;
1372 const WCHAR
*next_to_retry
= NULL
;
1374 while (name
< name_end
&& mask
< mask_end
)
1380 while (mask
< mask_end
&& *mask
== '*') mask
++; /* Skip consecutive '*' */
1381 if (mask
== mask_end
) return TRUE
; /* end of mask is all '*', so match */
1384 /* skip to the next match after the joker(s) */
1385 if (is_case_sensitive
)
1386 while (name
< name_end
&& (*name
!= *mask
)) name
++;
1388 while (name
< name_end
&& (towupper(*name
) != towupper(*mask
))) name
++;
1389 next_to_retry
= name
;
1397 if (is_case_sensitive
) mismatch
= (*mask
!= *name
);
1398 else mismatch
= (towupper(*mask
) != towupper(*name
));
1404 if (mask
== mask_end
)
1406 if (name
== name_end
) return TRUE
;
1407 if (lastjoker
) mask
= lastjoker
;
1410 else /* mismatch ! */
1412 if (lastjoker
) /* we had an '*', so we can try unlimitedly */
1416 /* this scan sequence was a mismatch, so restart
1417 * 1 char after the first char we checked last time */
1419 name
= next_to_retry
;
1421 else return FALSE
; /* bad luck */
1426 while (mask
< mask_end
&& ((*mask
== '.') || (*mask
== '*')))
1427 mask
++; /* Ignore trailing '.' or '*' in mask */
1428 return (name
== name_end
&& mask
== mask_end
);
1432 /***********************************************************************
1433 * is_legal_8dot3_name
1435 * Simplified version of RtlIsNameLegalDOS8Dot3.
1437 static BOOLEAN
is_legal_8dot3_name( const WCHAR
*name
, int len
)
1439 static const WCHAR invalid_chars
[] = { INVALID_DOS_CHARS
,':','/','\\',0 };
1442 if (len
> 12) return FALSE
;
1444 /* a starting . is invalid, except for . and .. */
1445 if (len
> 0 && name
[0] == '.') return (len
== 1 || (len
== 2 && name
[1] == '.'));
1447 for (i
= 0; i
< len
; i
++)
1449 if (name
[i
] > 0x7f) return FALSE
;
1450 if (wcschr( invalid_chars
, name
[i
] )) return FALSE
;
1453 if (dot
!= -1) return FALSE
;
1458 if (dot
== -1) return (len
<= 8);
1459 if (dot
> 8) return FALSE
;
1460 return (len
- dot
> 1 && len
- dot
< 5);
1464 /***********************************************************************
1467 * Add a file to the directory data if it matches the mask.
1469 static BOOL
append_entry( struct dir_data
*data
, const char *long_name
,
1470 const char *short_name
, const UNICODE_STRING
*mask
)
1472 int long_len
, short_len
;
1473 WCHAR long_nameW
[MAX_DIR_ENTRY_LEN
+ 1];
1474 WCHAR short_nameW
[13];
1476 long_len
= ntdll_umbstowcs( long_name
, strlen(long_name
), long_nameW
, ARRAY_SIZE(long_nameW
) );
1477 if (long_len
== ARRAY_SIZE(long_nameW
)) return TRUE
;
1478 long_nameW
[long_len
] = 0;
1482 short_len
= ntdll_umbstowcs( short_name
, strlen(short_name
),
1483 short_nameW
, ARRAY_SIZE( short_nameW
) - 1 );
1485 else /* generate a short name if necessary */
1488 if (!is_legal_8dot3_name( long_nameW
, long_len
))
1489 short_len
= hash_short_file_name( long_nameW
, long_len
, short_nameW
);
1491 short_nameW
[short_len
] = 0;
1492 wcsupr( short_nameW
);
1494 TRACE( "long %s short %s mask %s\n",
1495 debugstr_w( long_nameW
), debugstr_w( short_nameW
), debugstr_us( mask
));
1497 if (mask
&& !match_filename( long_nameW
, long_len
, mask
))
1499 if (!short_len
) return TRUE
; /* no short name to match */
1500 if (!match_filename( short_nameW
, short_len
, mask
)) return TRUE
;
1503 return add_dir_data_names( data
, long_nameW
, short_nameW
, long_name
);
1507 /* fetch the attributes of a file */
1508 static inline ULONG
get_file_attributes( const struct stat
*st
)
1512 if (S_ISDIR(st
->st_mode
))
1513 attr
= FILE_ATTRIBUTE_DIRECTORY
;
1515 attr
= FILE_ATTRIBUTE_ARCHIVE
;
1516 if (!(st
->st_mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
)))
1517 attr
|= FILE_ATTRIBUTE_READONLY
;
1522 /* decode the xattr-stored DOS attributes */
1523 static int parse_samba_dos_attrib_data( char *data
, int len
)
1528 if (len
> 2 && data
[0] == '0' && data
[1] == 'x')
1531 val
= strtol( data
, &end
, 16 );
1532 if (!*end
) return val
& XATTR_ATTRIBS_MASK
;
1537 if (!once
++) FIXME( "Unhandled " SAMBA_XATTR_DOS_ATTRIB
" extended attribute value.\n" );
1543 static BOOL
fd_is_mount_point( int fd
, const struct stat
*st
)
1546 return S_ISDIR( st
->st_mode
) && !fstatat( fd
, "..", &parent
, 0 )
1547 && (parent
.st_dev
!= st
->st_dev
|| parent
.st_ino
== st
->st_ino
);
1551 /* get the stat info and file attributes for a file (by file descriptor) */
1552 static int fd_get_file_info( int fd
, unsigned int options
, struct stat
*st
, ULONG
*attr
)
1558 ret
= fstat( fd
, st
);
1559 if (ret
== -1) return ret
;
1560 *attr
|= get_file_attributes( st
);
1561 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1562 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, st
))
1563 *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1565 attr_len
= xattr_fget( fd
, SAMBA_XATTR_DOS_ATTRIB
, attr_data
, sizeof(attr_data
)-1 );
1567 *attr
|= parse_samba_dos_attrib_data( attr_data
, attr_len
);
1568 else if (errno
!= ENODATA
&& errno
!= ENOTSUP
)
1569 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB
". errno %d (%s)\n",
1570 errno
, strerror( errno
) );
1576 static int fd_set_dos_attrib( int fd
, ULONG attr
)
1578 /* we only store the HIDDEN and SYSTEM attributes */
1579 attr
&= XATTR_ATTRIBS_MASK
;
1582 /* encode the attributes in Samba 3 ASCII format. Samba 4 has extended
1583 * this format with more features, but retains compatibility with the
1584 * earlier format. */
1586 int len
= sprintf( data
, "0x%x", attr
);
1587 return xattr_fset( fd
, SAMBA_XATTR_DOS_ATTRIB
, data
, len
);
1589 else return xattr_fremove( fd
, SAMBA_XATTR_DOS_ATTRIB
);
1593 /* set the stat info and file attributes for a file (by file descriptor) */
1594 NTSTATUS
fd_set_file_info( int fd
, ULONG attr
)
1598 if (fstat( fd
, &st
) == -1) return errno_to_status( errno
);
1599 if (attr
& FILE_ATTRIBUTE_READONLY
)
1601 if (S_ISDIR( st
.st_mode
))
1602 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
1604 st
.st_mode
&= ~0222; /* clear write permission bits */
1608 /* add write permission only where we already have read permission */
1609 st
.st_mode
|= (0600 | ((st
.st_mode
& 044) >> 1)) & (~start_umask
);
1611 if (fchmod( fd
, st
.st_mode
) == -1) return errno_to_status( errno
);
1613 if (fd_set_dos_attrib( fd
, attr
) == -1 && errno
!= ENOTSUP
)
1614 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB
". errno %d (%s)\n",
1615 errno
, strerror( errno
) );
1617 return STATUS_SUCCESS
;
1621 /* get the stat info and file attributes for a file (by name) */
1622 static int get_file_info( const char *path
, struct stat
*st
, ULONG
*attr
)
1629 ret
= lstat( path
, st
);
1630 if (ret
== -1) return ret
;
1631 if (S_ISLNK( st
->st_mode
))
1633 ret
= stat( path
, st
);
1634 if (ret
== -1) return ret
;
1635 /* is a symbolic link and a directory, consider these "reparse points" */
1636 if (S_ISDIR( st
->st_mode
)) *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1638 else if (S_ISDIR( st
->st_mode
) && (parent_path
= malloc( strlen(path
) + 4 )))
1640 struct stat parent_st
;
1642 /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
1643 strcpy( parent_path
, path
);
1644 strcat( parent_path
, "/.." );
1645 if (!stat( parent_path
, &parent_st
)
1646 && (st
->st_dev
!= parent_st
.st_dev
|| st
->st_ino
== parent_st
.st_ino
))
1647 *attr
|= FILE_ATTRIBUTE_REPARSE_POINT
;
1649 free( parent_path
);
1651 *attr
|= get_file_attributes( st
);
1653 attr_len
= xattr_get( path
, SAMBA_XATTR_DOS_ATTRIB
, attr_data
, sizeof(attr_data
)-1 );
1655 *attr
|= parse_samba_dos_attrib_data( attr_data
, attr_len
);
1656 else if (errno
!= ENODATA
&& errno
!= ENOTSUP
)
1657 WARN( "Failed to get extended attribute " SAMBA_XATTR_DOS_ATTRIB
" from \"%s\". errno %d (%s)\n",
1658 path
, errno
, strerror( errno
) );
1664 #if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
1665 static int futimens( int fd
, const struct timespec spec
[2] )
1667 return syscall( __NR_utimensat
, fd
, NULL
, spec
, 0 );
1669 #define HAVE_FUTIMENS
1670 #endif /* __ANDROID__ */
1673 #define UTIME_OMIT ((1 << 30) - 2)
1676 static BOOL
set_file_times_precise( int fd
, const LARGE_INTEGER
*mtime
,
1677 const LARGE_INTEGER
*atime
, NTSTATUS
*status
)
1679 #ifdef HAVE_FUTIMENS
1680 struct timespec tv
[2];
1682 tv
[0].tv_sec
= tv
[1].tv_sec
= 0;
1683 tv
[0].tv_nsec
= tv
[1].tv_nsec
= UTIME_OMIT
;
1684 if (atime
->QuadPart
)
1686 tv
[0].tv_sec
= atime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1687 tv
[0].tv_nsec
= (atime
->QuadPart
% 10000000) * 100;
1689 if (mtime
->QuadPart
)
1691 tv
[1].tv_sec
= mtime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1692 tv
[1].tv_nsec
= (mtime
->QuadPart
% 10000000) * 100;
1695 if (!&futimens
) return FALSE
;
1697 if (futimens( fd
, tv
) == -1) *status
= errno_to_status( errno
);
1698 else *status
= STATUS_SUCCESS
;
1706 static NTSTATUS
set_file_times( int fd
, const LARGE_INTEGER
*mtime
, const LARGE_INTEGER
*atime
)
1708 NTSTATUS status
= STATUS_SUCCESS
;
1709 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1710 struct timeval tv
[2];
1714 if (set_file_times_precise( fd
, mtime
, atime
, &status
))
1717 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
1718 if (!atime
->QuadPart
|| !mtime
->QuadPart
)
1721 tv
[0].tv_sec
= tv
[0].tv_usec
= 0;
1722 tv
[1].tv_sec
= tv
[1].tv_usec
= 0;
1723 if (!fstat( fd
, &st
))
1725 tv
[0].tv_sec
= st
.st_atime
;
1726 tv
[1].tv_sec
= st
.st_mtime
;
1727 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1728 tv
[0].tv_usec
= st
.st_atim
.tv_nsec
/ 1000;
1729 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1730 tv
[0].tv_usec
= st
.st_atimespec
.tv_nsec
/ 1000;
1732 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1733 tv
[1].tv_usec
= st
.st_mtim
.tv_nsec
/ 1000;
1734 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1735 tv
[1].tv_usec
= st
.st_mtimespec
.tv_nsec
/ 1000;
1739 if (atime
->QuadPart
)
1741 tv
[0].tv_sec
= atime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1742 tv
[0].tv_usec
= (atime
->QuadPart
% 10000000) / 10;
1744 if (mtime
->QuadPart
)
1746 tv
[1].tv_sec
= mtime
->QuadPart
/ 10000000 - SECS_1601_TO_1970
;
1747 tv
[1].tv_usec
= (mtime
->QuadPart
% 10000000) / 10;
1750 if (futimes( fd
, tv
) == -1) status
= errno_to_status( errno
);
1751 #elif defined(HAVE_FUTIMESAT)
1752 if (futimesat( fd
, NULL
, tv
) == -1) status
= errno_to_status( errno
);
1755 #else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
1756 FIXME( "setting file times not supported\n" );
1757 status
= STATUS_NOT_IMPLEMENTED
;
1763 static inline void get_file_times( const struct stat
*st
, LARGE_INTEGER
*mtime
, LARGE_INTEGER
*ctime
,
1764 LARGE_INTEGER
*atime
, LARGE_INTEGER
*creation
)
1766 mtime
->QuadPart
= ticks_from_time_t( st
->st_mtime
);
1767 ctime
->QuadPart
= ticks_from_time_t( st
->st_ctime
);
1768 atime
->QuadPart
= ticks_from_time_t( st
->st_atime
);
1769 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1770 mtime
->QuadPart
+= st
->st_mtim
.tv_nsec
/ 100;
1771 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1772 mtime
->QuadPart
+= st
->st_mtimespec
.tv_nsec
/ 100;
1774 #ifdef HAVE_STRUCT_STAT_ST_CTIM
1775 ctime
->QuadPart
+= st
->st_ctim
.tv_nsec
/ 100;
1776 #elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
1777 ctime
->QuadPart
+= st
->st_ctimespec
.tv_nsec
/ 100;
1779 #ifdef HAVE_STRUCT_STAT_ST_ATIM
1780 atime
->QuadPart
+= st
->st_atim
.tv_nsec
/ 100;
1781 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
1782 atime
->QuadPart
+= st
->st_atimespec
.tv_nsec
/ 100;
1784 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1785 creation
->QuadPart
= ticks_from_time_t( st
->st_birthtime
);
1786 #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
1787 creation
->QuadPart
+= st
->st_birthtim
.tv_nsec
/ 100;
1788 #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
1789 creation
->QuadPart
+= st
->st_birthtimespec
.tv_nsec
/ 100;
1791 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
1792 creation
->QuadPart
= ticks_from_time_t( st
->__st_birthtime
);
1793 #ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
1794 creation
->QuadPart
+= st
->__st_birthtim
.tv_nsec
/ 100;
1802 /* fill in the file information that depends on the stat and attribute info */
1803 static NTSTATUS
fill_file_info( const struct stat
*st
, ULONG attr
, void *ptr
,
1804 FILE_INFORMATION_CLASS
class )
1808 case FileBasicInformation
:
1810 FILE_BASIC_INFORMATION
*info
= ptr
;
1812 get_file_times( st
, &info
->LastWriteTime
, &info
->ChangeTime
,
1813 &info
->LastAccessTime
, &info
->CreationTime
);
1814 info
->FileAttributes
= attr
;
1817 case FileStandardInformation
:
1819 FILE_STANDARD_INFORMATION
*info
= ptr
;
1821 if ((info
->Directory
= S_ISDIR(st
->st_mode
)))
1823 info
->AllocationSize
.QuadPart
= 0;
1824 info
->EndOfFile
.QuadPart
= 0;
1825 info
->NumberOfLinks
= 1;
1829 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1830 info
->EndOfFile
.QuadPart
= st
->st_size
;
1831 info
->NumberOfLinks
= st
->st_nlink
;
1835 case FileInternalInformation
:
1837 FILE_INTERNAL_INFORMATION
*info
= ptr
;
1838 info
->IndexNumber
.QuadPart
= st
->st_ino
;
1841 case FileEndOfFileInformation
:
1843 FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
1844 info
->EndOfFile
.QuadPart
= S_ISDIR(st
->st_mode
) ? 0 : st
->st_size
;
1847 case FileAllInformation
:
1849 FILE_ALL_INFORMATION
*info
= ptr
;
1850 fill_file_info( st
, attr
, &info
->BasicInformation
, FileBasicInformation
);
1851 fill_file_info( st
, attr
, &info
->StandardInformation
, FileStandardInformation
);
1852 fill_file_info( st
, attr
, &info
->InternalInformation
, FileInternalInformation
);
1855 /* all directory structures start with the FileDirectoryInformation layout */
1856 case FileBothDirectoryInformation
:
1857 case FileFullDirectoryInformation
:
1858 case FileDirectoryInformation
:
1860 FILE_DIRECTORY_INFORMATION
*info
= ptr
;
1862 get_file_times( st
, &info
->LastWriteTime
, &info
->ChangeTime
,
1863 &info
->LastAccessTime
, &info
->CreationTime
);
1864 if (S_ISDIR(st
->st_mode
))
1866 info
->AllocationSize
.QuadPart
= 0;
1867 info
->EndOfFile
.QuadPart
= 0;
1871 info
->AllocationSize
.QuadPart
= (ULONGLONG
)st
->st_blocks
* 512;
1872 info
->EndOfFile
.QuadPart
= st
->st_size
;
1874 info
->FileAttributes
= attr
;
1877 case FileIdFullDirectoryInformation
:
1879 FILE_ID_FULL_DIRECTORY_INFORMATION
*info
= ptr
;
1880 info
->FileId
.QuadPart
= st
->st_ino
;
1881 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
1884 case FileIdBothDirectoryInformation
:
1886 FILE_ID_BOTH_DIRECTORY_INFORMATION
*info
= ptr
;
1887 info
->FileId
.QuadPart
= st
->st_ino
;
1888 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
1891 case FileIdGlobalTxDirectoryInformation
:
1893 FILE_ID_GLOBAL_TX_DIR_INFORMATION
*info
= ptr
;
1894 info
->FileId
.QuadPart
= st
->st_ino
;
1895 fill_file_info( st
, attr
, info
, FileDirectoryInformation
);
1900 return STATUS_INVALID_INFO_CLASS
;
1902 return STATUS_SUCCESS
;
1906 static NTSTATUS
server_get_unix_name( HANDLE handle
, char **unix_name
)
1908 data_size_t size
= 1024;
1914 if (!(name
= malloc( size
+ 1 ))) return STATUS_NO_MEMORY
;
1916 SERVER_START_REQ( get_handle_unix_name
)
1918 req
->handle
= wine_server_obj_handle( handle
);
1919 wine_server_set_reply( req
, name
, size
);
1920 ret
= wine_server_call( req
);
1921 size
= reply
->name_len
;
1932 if (ret
!= STATUS_BUFFER_OVERFLOW
) break;
1937 static NTSTATUS
fill_name_info( const char *unix_name
, FILE_NAME_INFORMATION
*info
, LONG
*name_len
)
1942 if (!(status
= unix_to_nt_file_name( unix_name
, &nt_name
)))
1944 const WCHAR
*ptr
= nt_name
;
1945 const WCHAR
*end
= ptr
+ wcslen( nt_name
);
1947 /* Skip the volume mount point. */
1948 while (ptr
!= end
&& *ptr
== '\\') ++ptr
;
1949 while (ptr
!= end
&& *ptr
!= '\\') ++ptr
;
1950 while (ptr
!= end
&& *ptr
== '\\') ++ptr
;
1951 while (ptr
!= end
&& *ptr
!= '\\') ++ptr
;
1953 info
->FileNameLength
= (end
- ptr
) * sizeof(WCHAR
);
1954 if (*name_len
< info
->FileNameLength
) status
= STATUS_BUFFER_OVERFLOW
;
1955 else *name_len
= info
->FileNameLength
;
1957 memcpy( info
->FileName
, ptr
, *name_len
);
1965 static NTSTATUS
get_full_size_info(int fd
, FILE_FS_FULL_SIZE_INFORMATION
*info
) {
1969 #if !defined(linux) || !defined(HAVE_FSTATFS)
1970 struct statvfs stfs
;
1975 if (fstat( fd
, &st
) < 0) return errno_to_status( errno
);
1976 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
)) return STATUS_INVALID_DEVICE_REQUEST
;
1978 /* Linux's fstatvfs is buggy */
1979 #if !defined(linux) || !defined(HAVE_FSTATFS)
1980 if (fstatvfs( fd
, &stfs
) < 0) return errno_to_status( errno
);
1981 bsize
= stfs
.f_frsize
;
1983 if (fstatfs( fd
, &stfs
) < 0) return errno_to_status( errno
);
1984 bsize
= stfs
.f_bsize
;
1986 if (bsize
== 2048) /* assume CD-ROM */
1988 info
->BytesPerSector
= 2048;
1989 info
->SectorsPerAllocationUnit
= 1;
1993 info
->BytesPerSector
= 512;
1994 info
->SectorsPerAllocationUnit
= 8;
1996 info
->TotalAllocationUnits
.QuadPart
= bsize
* stfs
.f_blocks
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
1997 info
->CallerAvailableAllocationUnits
.QuadPart
= bsize
* stfs
.f_bavail
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
1998 info
->ActualAvailableAllocationUnits
.QuadPart
= bsize
* stfs
.f_bfree
/ (info
->BytesPerSector
* info
->SectorsPerAllocationUnit
);
1999 return STATUS_SUCCESS
;
2003 static NTSTATUS
server_get_file_info( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
,
2004 ULONG length
, FILE_INFORMATION_CLASS info_class
)
2006 SERVER_START_REQ( get_file_info
)
2008 req
->handle
= wine_server_obj_handle( handle
);
2009 req
->info_class
= info_class
;
2010 wine_server_set_reply( req
, buffer
, length
);
2011 io
->u
.Status
= wine_server_call( req
);
2012 io
->Information
= wine_server_reply_size( reply
);
2015 if (io
->u
.Status
== STATUS_NOT_IMPLEMENTED
)
2016 FIXME( "Unsupported info class %x\n", info_class
);
2017 return io
->u
.Status
;
2022 static NTSTATUS
server_open_file_object( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
2023 ULONG sharing
, ULONG options
)
2027 SERVER_START_REQ( open_file_object
)
2029 req
->access
= access
;
2030 req
->attributes
= attr
->Attributes
;
2031 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
2032 req
->sharing
= sharing
;
2033 req
->options
= options
;
2034 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
2035 status
= wine_server_call( req
);
2036 *handle
= wine_server_ptr_handle( reply
->handle
);
2043 /* retrieve device/inode number for all the drives */
2044 static unsigned int get_drives_info( struct file_identity info
[MAX_DOS_DRIVES
] )
2046 static pthread_mutex_t cache_mutex
= PTHREAD_MUTEX_INITIALIZER
;
2047 static struct file_identity cache
[MAX_DOS_DRIVES
];
2048 static time_t last_update
;
2049 static unsigned int nb_drives
;
2051 time_t now
= time(NULL
);
2053 mutex_lock( &cache_mutex
);
2054 if (now
!= last_update
)
2060 if ((buffer
= malloc( strlen(config_dir
) + sizeof("/dosdevices/a:") )))
2062 strcpy( buffer
, config_dir
);
2063 strcat( buffer
, "/dosdevices/a:" );
2064 p
= buffer
+ strlen(buffer
) - 2;
2066 for (i
= nb_drives
= 0; i
< MAX_DOS_DRIVES
; i
++)
2069 if (!stat( buffer
, &st
))
2071 cache
[i
].dev
= st
.st_dev
;
2072 cache
[i
].ino
= st
.st_ino
;
2085 memcpy( info
, cache
, sizeof(cache
) );
2087 mutex_unlock( &cache_mutex
);
2092 /* Find a DOS device which can act as the root of "path".
2093 * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
2094 static int find_dos_device( const char *path
)
2096 int len
= strlen(path
);
2100 struct file_identity info
[MAX_DOS_DRIVES
];
2103 if (!get_drives_info( info
)) return -1;
2105 if (stat( path
, &st
) < 0) return -1;
2108 /* strip off trailing slashes */
2109 while (len
> 1 && path
[len
- 1] == '/') len
--;
2111 /* make a copy of the path */
2112 if (!(buffer
= malloc( len
+ 1 ))) return -1;
2113 memcpy( buffer
, path
, len
);
2118 if (!stat( buffer
, &st
) && S_ISDIR( st
.st_mode
))
2120 if (st
.st_dev
!= dev_id
) break;
2122 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
2124 if ((info
[drive
].dev
== st
.st_dev
) && (info
[drive
].ino
== st
.st_ino
))
2126 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
2127 debugstr_a(path
), 'A' + drive
, debugstr_a(buffer
), debugstr_a(path
+ len
));
2133 if (len
<= 1) break; /* reached root */
2134 while (len
> 1 && path
[len
- 1] != '/') len
--;
2135 while (len
> 1 && path
[len
- 1] == '/') len
--;
2142 static NTSTATUS
get_mountmgr_fs_info( HANDLE handle
, int fd
, struct mountmgr_unix_drive
*drive
, ULONG size
)
2144 OBJECT_ATTRIBUTES attr
;
2145 UNICODE_STRING string
;
2147 IO_STATUS_BLOCK io
= {0};
2152 if ((status
= server_get_unix_name( handle
, &unix_name
))) return status
;
2153 letter
= find_dos_device( unix_name
);
2156 memset( drive
, 0, sizeof(*drive
) );
2162 drive
->unix_dev
= st
.st_rdev
? st
.st_rdev
: st
.st_dev
;
2165 drive
->letter
= 'a' + letter
;
2167 init_unicode_string( &string
, MOUNTMGR_DEVICE_NAME
);
2168 InitializeObjectAttributes( &attr
, &string
, 0, NULL
, NULL
);
2169 status
= server_open_file_object( &mountmgr
, GENERIC_READ
| SYNCHRONIZE
, &attr
,
2170 FILE_SHARE_READ
| FILE_SHARE_WRITE
, FILE_SYNCHRONOUS_IO_NONALERT
);
2171 if (status
) return status
;
2173 status
= NtDeviceIoControlFile( mountmgr
, NULL
, NULL
, NULL
, &io
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
,
2174 drive
, sizeof(*drive
), drive
, size
);
2175 NtClose( mountmgr
);
2176 if (status
== STATUS_BUFFER_OVERFLOW
) status
= STATUS_SUCCESS
;
2177 else if (status
) WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status
);
2182 /***********************************************************************
2183 * get_dir_data_entry
2185 * Return a directory entry from the cached data.
2187 static NTSTATUS
get_dir_data_entry( struct dir_data
*dir_data
, void *info_ptr
, IO_STATUS_BLOCK
*io
,
2188 ULONG max_length
, FILE_INFORMATION_CLASS
class,
2189 union file_directory_info
**last_info
)
2191 const struct dir_data_names
*names
= &dir_data
->names
[dir_data
->pos
];
2192 union file_directory_info
*info
;
2194 ULONG name_len
, start
, dir_size
, attributes
;
2196 if (get_file_info( names
->unix_name
, &st
, &attributes
) == -1)
2198 TRACE( "file no longer exists %s\n", names
->unix_name
);
2199 return STATUS_SUCCESS
;
2201 if (is_ignored_file( &st
))
2203 TRACE( "ignoring file %s\n", names
->unix_name
);
2204 return STATUS_SUCCESS
;
2206 start
= dir_info_align( io
->Information
);
2207 dir_size
= dir_info_size( class, 0 );
2208 if (start
+ dir_size
> max_length
) return STATUS_MORE_ENTRIES
;
2210 max_length
-= start
+ dir_size
;
2211 name_len
= wcslen( names
->long_name
) * sizeof(WCHAR
);
2212 /* if this is not the first entry, fail; the first entry is always returned (but truncated) */
2213 if (*last_info
&& name_len
> max_length
) return STATUS_MORE_ENTRIES
;
2215 info
= (union file_directory_info
*)((char *)info_ptr
+ start
);
2216 info
->dir
.NextEntryOffset
= 0;
2217 info
->dir
.FileIndex
= 0; /* NTFS always has 0 here, so let's not bother with it */
2219 /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */
2220 if (class != FileNamesInformation
)
2222 if (st
.st_dev
!= dir_data
->id
.dev
) st
.st_ino
= 0; /* ignore inode if on a different device */
2224 if (!show_dot_files
&& names
->long_name
[0] == '.' && names
->long_name
[1] &&
2225 (names
->long_name
[1] != '.' || names
->long_name
[2]))
2226 attributes
|= FILE_ATTRIBUTE_HIDDEN
;
2228 fill_file_info( &st
, attributes
, info
, class );
2233 case FileDirectoryInformation
:
2234 info
->dir
.FileNameLength
= name_len
;
2237 case FileFullDirectoryInformation
:
2238 info
->full
.EaSize
= 0; /* FIXME */
2239 info
->full
.FileNameLength
= name_len
;
2242 case FileIdFullDirectoryInformation
:
2243 info
->id_full
.EaSize
= 0; /* FIXME */
2244 info
->id_full
.FileNameLength
= name_len
;
2247 case FileBothDirectoryInformation
:
2248 info
->both
.EaSize
= 0; /* FIXME */
2249 info
->both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2250 memcpy( info
->both
.ShortName
, names
->short_name
, info
->both
.ShortNameLength
);
2251 info
->both
.FileNameLength
= name_len
;
2254 case FileIdBothDirectoryInformation
:
2255 info
->id_both
.EaSize
= 0; /* FIXME */
2256 info
->id_both
.ShortNameLength
= wcslen( names
->short_name
) * sizeof(WCHAR
);
2257 memcpy( info
->id_both
.ShortName
, names
->short_name
, info
->id_both
.ShortNameLength
);
2258 info
->id_both
.FileNameLength
= name_len
;
2261 case FileIdGlobalTxDirectoryInformation
:
2262 info
->id_tx
.TxInfoFlags
= 0;
2263 info
->id_tx
.FileNameLength
= name_len
;
2266 case FileNamesInformation
:
2267 info
->names
.FileNameLength
= name_len
;
2275 memcpy( (char *)info
+ dir_size
, names
->long_name
, min( name_len
, max_length
) );
2276 io
->Information
= start
+ dir_size
+ min( name_len
, max_length
);
2277 if (*last_info
) (*last_info
)->next
= (char *)info
- (char *)*last_info
;
2279 return name_len
> max_length
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
2282 #ifdef VFAT_IOCTL_READDIR_BOTH
2284 /***********************************************************************
2285 * read_directory_vfat
2287 * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile.
2289 static NTSTATUS
read_directory_data_vfat( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2291 char *short_name
, *long_name
;
2292 KERNEL_DIRENT de
[2];
2293 NTSTATUS status
= STATUS_NO_MEMORY
;
2294 off_t old_pos
= lseek( fd
, 0, SEEK_CUR
);
2296 lseek( fd
, 0, SEEK_SET
);
2298 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1)
2300 if (errno
!= ENOENT
)
2302 status
= STATUS_NOT_SUPPORTED
;
2308 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2309 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2311 while (de
[0].d_reclen
)
2313 if (strcmp( de
[0].d_name
, "." ) && strcmp( de
[0].d_name
, ".." ))
2315 if (de
[1].d_name
[0])
2317 short_name
= de
[0].d_name
;
2318 long_name
= de
[1].d_name
;
2322 long_name
= de
[0].d_name
;
2325 if (!append_entry( data
, long_name
, short_name
, mask
)) goto done
;
2327 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)de
) == -1) break;
2329 status
= STATUS_SUCCESS
;
2331 lseek( fd
, old_pos
, SEEK_SET
);
2334 #endif /* VFAT_IOCTL_READDIR_BOTH */
2337 #ifdef HAVE_GETATTRLIST
2338 /***********************************************************************
2339 * read_directory_getattrlist
2341 * Read a single file from a directory by determining whether the file
2342 * identified by mask exists using getattrlist.
2344 static NTSTATUS
read_directory_data_getattrlist( struct dir_data
*data
, const char *unix_name
)
2346 struct attrlist attrlist
;
2347 #include "pshpack4.h"
2351 struct attrreference name_reference
;
2353 char name
[NAME_MAX
* 3 + 1];
2355 #include "poppack.h"
2357 memset( &attrlist
, 0, sizeof(attrlist
) );
2358 attrlist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
2359 attrlist
.commonattr
= ATTR_CMN_NAME
| ATTR_CMN_OBJTYPE
;
2360 if (getattrlist( unix_name
, &attrlist
, &buffer
, sizeof(buffer
), FSOPT_NOFOLLOW
) == -1)
2361 return STATUS_NO_SUCH_FILE
;
2362 /* If unix_name named a symlink, the above may have succeeded even if the symlink is broken.
2363 Check that with another call without FSOPT_NOFOLLOW. We don't ask for any attributes. */
2364 if (buffer
.type
== VLNK
)
2367 attrlist
.commonattr
= 0;
2368 if (getattrlist( unix_name
, &attrlist
, &dummy
, sizeof(dummy
), 0 ) == -1)
2369 return STATUS_NO_SUCH_FILE
;
2372 TRACE( "found %s\n", buffer
.name
);
2374 if (!append_entry( data
, buffer
.name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2376 return STATUS_SUCCESS
;
2378 #endif /* HAVE_GETATTRLIST */
2381 /***********************************************************************
2382 * read_directory_stat
2384 * Read a single file from a directory by determining whether the file
2385 * identified by mask exists using stat.
2387 static NTSTATUS
read_directory_data_stat( struct dir_data
*data
, const char *unix_name
)
2391 /* if the file system is not case sensitive we can't find the actual name through stat() */
2392 if (!get_dir_case_sensitivity(".")) return STATUS_NO_SUCH_FILE
;
2393 if (stat( unix_name
, &st
) == -1) return STATUS_NO_SUCH_FILE
;
2395 TRACE( "found %s\n", unix_name
);
2397 if (!append_entry( data
, unix_name
, NULL
, NULL
)) return STATUS_NO_MEMORY
;
2399 return STATUS_SUCCESS
;
2403 /***********************************************************************
2404 * read_directory_readdir
2406 * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile.
2408 static NTSTATUS
read_directory_data_readdir( struct dir_data
*data
, const UNICODE_STRING
*mask
)
2411 NTSTATUS status
= STATUS_NO_MEMORY
;
2412 DIR *dir
= opendir( "." );
2414 if (!dir
) return STATUS_NO_SUCH_FILE
;
2416 if (!append_entry( data
, ".", NULL
, mask
)) goto done
;
2417 if (!append_entry( data
, "..", NULL
, mask
)) goto done
;
2418 while ((de
= readdir( dir
)))
2420 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
2421 if (!append_entry( data
, de
->d_name
, NULL
, mask
)) goto done
;
2423 status
= STATUS_SUCCESS
;
2431 /***********************************************************************
2432 * read_directory_data
2434 * Read the full contents of a directory, using one of the above helper functions.
2436 static NTSTATUS
read_directory_data( struct dir_data
*data
, int fd
, const UNICODE_STRING
*mask
)
2440 #ifdef VFAT_IOCTL_READDIR_BOTH
2441 if (!(status
= read_directory_data_vfat( data
, fd
, mask
))) return status
;
2444 if (!has_wildcard( mask
))
2446 /* convert the mask to a Unix name and check for it */
2447 char unix_name
[MAX_DIR_ENTRY_LEN
* 3 + 1];
2448 int ret
= ntdll_wcstoumbs( mask
->Buffer
, mask
->Length
/ sizeof(WCHAR
),
2449 unix_name
, sizeof(unix_name
) - 1, TRUE
);
2453 #ifdef HAVE_GETATTRLIST
2454 if (!(status
= read_directory_data_getattrlist( data
, unix_name
))) return status
;
2456 if (!(status
= read_directory_data_stat( data
, unix_name
))) return status
;
2460 return read_directory_data_readdir( data
, mask
);
2464 /* compare file names for directory sorting */
2465 static int name_compare( const void *a
, const void *b
)
2467 const struct dir_data_names
*file_a
= (const struct dir_data_names
*)a
;
2468 const struct dir_data_names
*file_b
= (const struct dir_data_names
*)b
;
2469 int ret
= wcsicmp( file_a
->long_name
, file_b
->long_name
);
2470 if (!ret
) ret
= wcscmp( file_a
->long_name
, file_b
->long_name
);
2475 /***********************************************************************
2476 * init_cached_dir_data
2478 * Initialize the cached directory contents.
2480 static NTSTATUS
init_cached_dir_data( struct dir_data
**data_ret
, int fd
, const UNICODE_STRING
*mask
)
2482 struct dir_data
*data
;
2487 if (!(data
= calloc( 1, sizeof(*data
) ))) return STATUS_NO_MEMORY
;
2489 if ((status
= read_directory_data( data
, fd
, mask
)))
2491 free_dir_data( data
);
2495 /* sort filenames, but not "." and ".." */
2497 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, "." )) i
++;
2498 if (i
< data
->count
&& !strcmp( data
->names
[i
].unix_name
, ".." )) i
++;
2499 if (i
< data
->count
) qsort( data
->names
+ i
, data
->count
- i
, sizeof(*data
->names
), name_compare
);
2504 data
->id
.dev
= st
.st_dev
;
2505 data
->id
.ino
= st
.st_ino
;
2508 TRACE( "mask %s found %u files\n", debugstr_us( mask
), data
->count
);
2509 for (i
= 0; i
< data
->count
; i
++)
2510 TRACE( "%s %s\n", debugstr_w(data
->names
[i
].long_name
), debugstr_w(data
->names
[i
].short_name
) );
2513 return data
->count
? STATUS_SUCCESS
: STATUS_NO_SUCH_FILE
;
2517 /***********************************************************************
2518 * get_cached_dir_data
2520 * Retrieve the cached directory data, or initialize it if necessary.
2522 static NTSTATUS
get_cached_dir_data( HANDLE handle
, struct dir_data
**data_ret
, int fd
,
2523 const UNICODE_STRING
*mask
)
2526 int entry
= -1, free_entries
[16];
2529 SERVER_START_REQ( get_directory_cache_entry
)
2531 req
->handle
= wine_server_obj_handle( handle
);
2532 wine_server_set_reply( req
, free_entries
, sizeof(free_entries
) );
2533 if (!(status
= wine_server_call( req
))) entry
= reply
->entry
;
2535 for (i
= 0; i
< wine_server_reply_size( reply
) / sizeof(*free_entries
); i
++)
2537 int free_idx
= free_entries
[i
];
2538 if (free_idx
< dir_data_cache_size
)
2540 free_dir_data( dir_data_cache
[free_idx
] );
2541 dir_data_cache
[free_idx
] = NULL
;
2549 if (status
== STATUS_SHARING_VIOLATION
) FIXME( "shared directory handle not supported yet\n" );
2553 if (entry
>= dir_data_cache_size
)
2555 unsigned int size
= max( dir_data_cache_initial_size
, max( dir_data_cache_size
* 2, entry
+ 1 ) );
2556 struct dir_data
**new_cache
= realloc( dir_data_cache
, size
* sizeof(*new_cache
) );
2558 if (!new_cache
) return STATUS_NO_MEMORY
;
2559 memset( new_cache
+ dir_data_cache_size
, 0, (size
- dir_data_cache_size
) * sizeof(*new_cache
) );
2560 dir_data_cache
= new_cache
;
2561 dir_data_cache_size
= size
;
2564 if (!dir_data_cache
[entry
]) status
= init_cached_dir_data( &dir_data_cache
[entry
], fd
, mask
);
2566 *data_ret
= dir_data_cache
[entry
];
2571 /******************************************************************************
2572 * NtQueryDirectoryFile (NTDLL.@)
2574 NTSTATUS WINAPI
NtQueryDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc_routine
,
2575 void *apc_context
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
2576 FILE_INFORMATION_CLASS info_class
, BOOLEAN single_entry
,
2577 UNICODE_STRING
*mask
, BOOLEAN restart_scan
)
2579 int cwd
, fd
, needs_close
;
2580 enum server_fd_type type
;
2581 struct dir_data
*data
;
2584 TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
2585 handle
, event
, apc_routine
, apc_context
, io
, buffer
,
2586 length
, info_class
, single_entry
, debugstr_us(mask
),
2589 if (event
|| apc_routine
)
2591 FIXME( "Unsupported yet option\n" );
2592 return STATUS_NOT_IMPLEMENTED
;
2596 case FileDirectoryInformation
:
2597 case FileBothDirectoryInformation
:
2598 case FileFullDirectoryInformation
:
2599 case FileIdBothDirectoryInformation
:
2600 case FileIdFullDirectoryInformation
:
2601 case FileIdGlobalTxDirectoryInformation
:
2602 case FileNamesInformation
:
2603 if (length
< dir_info_align( dir_info_size( info_class
, 1 ))) return STATUS_INFO_LENGTH_MISMATCH
;
2605 case FileObjectIdInformation
:
2606 if (length
!= sizeof(FILE_OBJECTID_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2607 return STATUS_INVALID_INFO_CLASS
;
2608 case FileQuotaInformation
:
2609 if (length
!= sizeof(FILE_QUOTA_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2610 return STATUS_INVALID_INFO_CLASS
;
2611 case FileReparsePointInformation
:
2612 if (length
!= sizeof(FILE_REPARSE_POINT_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
2613 return STATUS_INVALID_INFO_CLASS
;
2615 return STATUS_INVALID_INFO_CLASS
;
2617 if (!buffer
) return STATUS_ACCESS_VIOLATION
;
2619 if ((status
= server_get_unix_fd( handle
, FILE_LIST_DIRECTORY
, &fd
, &needs_close
, &type
, NULL
)))
2622 if (type
!= FD_TYPE_DIR
)
2624 if (needs_close
) close( fd
);
2625 return STATUS_INVALID_PARAMETER
;
2628 io
->Information
= 0;
2630 mutex_lock( &dir_mutex
);
2632 cwd
= open( ".", O_RDONLY
);
2633 if (fchdir( fd
) != -1)
2635 if (!(status
= get_cached_dir_data( handle
, &data
, fd
, mask
)))
2637 union file_directory_info
*last_info
= NULL
;
2639 if (restart_scan
) data
->pos
= 0;
2641 while (!status
&& data
->pos
< data
->count
)
2643 status
= get_dir_data_entry( data
, buffer
, io
, length
, info_class
, &last_info
);
2644 if (!status
|| status
== STATUS_BUFFER_OVERFLOW
) data
->pos
++;
2645 if (single_entry
&& last_info
) break;
2648 if (!last_info
) status
= STATUS_NO_MORE_FILES
;
2649 else if (status
== STATUS_MORE_ENTRIES
) status
= STATUS_SUCCESS
;
2651 io
->u
.Status
= status
;
2653 if (cwd
== -1 || fchdir( cwd
) == -1) chdir( "/" );
2655 else status
= errno_to_status( errno
);
2657 mutex_unlock( &dir_mutex
);
2659 if (needs_close
) close( fd
);
2660 if (cwd
!= -1) close( cwd
);
2661 TRACE( "=> %x (%ld)\n", status
, io
->Information
);
2666 /***********************************************************************
2669 * Find a file in a directory the hard way, by doing a case-insensitive search.
2670 * The file found is appended to unix_name at pos.
2671 * There must be at least MAX_DIR_ENTRY_LEN+2 chars available at pos.
2673 static NTSTATUS
find_file_in_dir( char *unix_name
, int pos
, const WCHAR
*name
, int length
,
2674 BOOLEAN check_case
)
2676 WCHAR buffer
[MAX_DIR_ENTRY_LEN
];
2677 BOOLEAN is_name_8_dot_3
;
2683 /* try a shortcut for this directory */
2685 unix_name
[pos
++] = '/';
2686 ret
= ntdll_wcstoumbs( name
, length
, unix_name
+ pos
, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
2687 if (ret
>= 0 && ret
<= MAX_DIR_ENTRY_LEN
)
2689 unix_name
[pos
+ ret
] = 0;
2690 if (!stat( unix_name
, &st
)) return STATUS_SUCCESS
;
2692 if (check_case
) goto not_found
; /* we want an exact match */
2694 if (pos
> 1) unix_name
[pos
- 1] = 0;
2695 else unix_name
[1] = 0; /* keep the initial slash */
2697 /* check if it fits in 8.3 so that we don't look for short names if we won't need them */
2699 is_name_8_dot_3
= is_legal_8dot3_name( name
, length
);
2700 #ifndef VFAT_IOCTL_READDIR_BOTH
2701 is_name_8_dot_3
= is_name_8_dot_3
&& length
>= 8 && name
[4] == '~';
2704 if (!is_name_8_dot_3
&& !get_dir_case_sensitivity( unix_name
)) goto not_found
;
2706 /* now look for it through the directory */
2708 #ifdef VFAT_IOCTL_READDIR_BOTH
2709 if (is_name_8_dot_3
)
2711 int fd
= open( unix_name
, O_RDONLY
| O_DIRECTORY
);
2714 KERNEL_DIRENT kde
[2];
2716 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) != -1)
2718 unix_name
[pos
- 1] = '/';
2719 while (kde
[0].d_reclen
)
2721 if (kde
[1].d_name
[0])
2723 ret
= ntdll_umbstowcs( kde
[1].d_name
, strlen(kde
[1].d_name
),
2724 buffer
, MAX_DIR_ENTRY_LEN
);
2725 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2727 strcpy( unix_name
+ pos
, kde
[1].d_name
);
2729 return STATUS_SUCCESS
;
2732 ret
= ntdll_umbstowcs( kde
[0].d_name
, strlen(kde
[0].d_name
),
2733 buffer
, MAX_DIR_ENTRY_LEN
);
2734 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2736 strcpy( unix_name
+ pos
,
2737 kde
[1].d_name
[0] ? kde
[1].d_name
: kde
[0].d_name
);
2739 return STATUS_SUCCESS
;
2741 if (ioctl( fd
, VFAT_IOCTL_READDIR_BOTH
, (long)kde
) == -1)
2747 /* if that did not work, restore previous state of unix_name */
2748 unix_name
[pos
- 1] = 0;
2752 /* fall through to normal handling */
2754 #endif /* VFAT_IOCTL_READDIR_BOTH */
2756 if (!(dir
= opendir( unix_name
))) return errno_to_status( errno
);
2758 unix_name
[pos
- 1] = '/';
2759 while ((de
= readdir( dir
)))
2761 ret
= ntdll_umbstowcs( de
->d_name
, strlen(de
->d_name
), buffer
, MAX_DIR_ENTRY_LEN
);
2762 if (ret
== length
&& !wcsnicmp( buffer
, name
, ret
))
2764 strcpy( unix_name
+ pos
, de
->d_name
);
2766 return STATUS_SUCCESS
;
2769 if (!is_name_8_dot_3
) continue;
2771 if (!is_legal_8dot3_name( buffer
, ret
))
2773 WCHAR short_nameW
[12];
2774 ret
= hash_short_file_name( buffer
, ret
, short_nameW
);
2775 if (ret
== length
&& !wcsnicmp( short_nameW
, name
, length
))
2777 strcpy( unix_name
+ pos
, de
->d_name
);
2779 return STATUS_SUCCESS
;
2786 unix_name
[pos
- 1] = 0;
2787 return STATUS_OBJECT_NAME_NOT_FOUND
;
2793 static const WCHAR catrootW
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t',0};
2794 static const WCHAR catroot2W
[] = {'s','y','s','t','e','m','3','2','\\','c','a','t','r','o','o','t','2',0};
2795 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};
2796 static const WCHAR driversetcW
[] = {'s','y','s','t','e','m','3','2','\\','d','r','i','v','e','r','s','\\','e','t','c',0};
2797 static const WCHAR logfilesW
[] = {'s','y','s','t','e','m','3','2','\\','l','o','g','f','i','l','e','s',0};
2798 static const WCHAR spoolW
[] = {'s','y','s','t','e','m','3','2','\\','s','p','o','o','l',0};
2799 static const WCHAR system32W
[] = {'s','y','s','t','e','m','3','2',0};
2800 static const WCHAR syswow64W
[] = {'s','y','s','w','o','w','6','4',0};
2801 static const WCHAR sysnativeW
[] = {'s','y','s','n','a','t','i','v','e',0};
2802 static const WCHAR regeditW
[] = {'r','e','g','e','d','i','t','.','e','x','e',0};
2803 static const WCHAR syswow64_regeditW
[] = {'s','y','s','w','o','w','6','4','\\','r','e','g','e','d','i','t','.','e','x','e',0};
2804 static const WCHAR windirW
[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\',0};
2805 static const WCHAR syswow64dirW
[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','w','o','w','6','4','\\'};
2807 static const WCHAR
* const no_redirect
[] =
2817 static struct file_identity windir
, sysdir
;
2819 static inline ULONG
starts_with_path( const WCHAR
*name
, ULONG name_len
, const WCHAR
*prefix
)
2821 ULONG len
= wcslen( prefix
);
2823 if (name_len
< len
) return 0;
2824 if (wcsnicmp( name
, prefix
, len
)) return 0;
2825 if (name_len
> len
&& name
[len
] != '\\') return 0;
2829 static BOOL
replace_path( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*str
, ULONG prefix_len
,
2830 const WCHAR
*match
, const WCHAR
*replace
)
2832 const WCHAR
*name
= attr
->ObjectName
->Buffer
;
2833 ULONG match_len
, replace_len
, len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
2836 if (!starts_with_path( name
+ prefix_len
, len
- prefix_len
, match
)) return FALSE
;
2838 match_len
= wcslen( match
);
2839 replace_len
= wcslen( replace
);
2840 str
->Length
= (len
+ replace_len
- match_len
) * sizeof(WCHAR
);
2841 str
->MaximumLength
= str
->Length
+ sizeof(WCHAR
);
2842 if (!(p
= str
->Buffer
= malloc( str
->MaximumLength
))) return FALSE
;
2844 memcpy( p
, name
, prefix_len
* sizeof(WCHAR
) );
2846 memcpy( p
, replace
, replace_len
* sizeof(WCHAR
) );
2848 name
+= prefix_len
+ match_len
;
2849 len
-= prefix_len
+ match_len
;
2850 memcpy( p
, name
, len
* sizeof(WCHAR
) );
2852 attr
->ObjectName
= str
;
2856 /***********************************************************************
2859 static void init_redirects(void)
2861 static const char system_dir
[] = "/dosdevices/c:/windows/system32";
2865 if (!(dir
= malloc( strlen(config_dir
) + sizeof(system_dir
) ))) return;
2866 strcpy( dir
, config_dir
);
2867 strcat( dir
, system_dir
);
2868 if (!stat( dir
, &st
))
2870 sysdir
.dev
= st
.st_dev
;
2871 sysdir
.ino
= st
.st_ino
;
2873 *strrchr( dir
, '/' ) = 0;
2874 if (!stat( dir
, &st
))
2876 windir
.dev
= st
.st_dev
;
2877 windir
.ino
= st
.st_ino
;
2879 else ERR( "%s: %s\n", dir
, strerror(errno
) );
2884 /***********************************************************************
2887 BOOL
get_redirect( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*redir
)
2889 const WCHAR
*name
= attr
->ObjectName
->Buffer
;
2890 unsigned int i
, prefix_len
= 0, len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
2892 redir
->Buffer
= NULL
;
2893 if (!NtCurrentTeb64()) return FALSE
;
2894 if (!len
) return FALSE
;
2896 if (!attr
->RootDirectory
)
2898 prefix_len
= wcslen( windirW
);
2899 if (len
< prefix_len
|| wcsnicmp( name
, windirW
, prefix_len
)) return FALSE
;
2903 int fd
, needs_close
;
2906 if (server_get_unix_fd( attr
->RootDirectory
, 0, &fd
, &needs_close
, NULL
, NULL
)) return FALSE
;
2908 if (needs_close
) close( fd
);
2909 if (!is_same_file( &windir
, &st
))
2911 if (!is_same_file( &sysdir
, &st
)) return FALSE
;
2912 if (NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
]) return FALSE
;
2913 if (name
[0] == '\\') return FALSE
;
2915 /* only check for paths that should NOT be redirected */
2916 for (i
= 0; i
< ARRAY_SIZE( no_redirect
); i
++)
2917 if (starts_with_path( name
, len
, no_redirect
[i
] + 9 /* "system32\\" */)) return FALSE
;
2919 /* redirect everything else */
2920 redir
->Length
= sizeof(syswow64dirW
) + len
* sizeof(WCHAR
);
2921 redir
->MaximumLength
= redir
->Length
+ sizeof(WCHAR
);
2922 if (!(redir
->Buffer
= malloc( redir
->MaximumLength
))) return FALSE
;
2923 memcpy( redir
->Buffer
, syswow64dirW
, sizeof(syswow64dirW
) );
2924 memcpy( redir
->Buffer
+ ARRAY_SIZE(syswow64dirW
), name
, len
* sizeof(WCHAR
) );
2925 redir
->Buffer
[redir
->Length
/ sizeof(WCHAR
)] = 0;
2926 attr
->RootDirectory
= 0;
2927 attr
->ObjectName
= redir
;
2932 /* sysnative is redirected even when redirection is disabled */
2934 if (replace_path( attr
, redir
, prefix_len
, sysnativeW
, system32W
)) return TRUE
;
2936 if (NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
]) return FALSE
;
2938 for (i
= 0; i
< ARRAY_SIZE( no_redirect
); i
++)
2939 if (starts_with_path( name
+ prefix_len
, len
- prefix_len
, no_redirect
[i
] )) return FALSE
;
2941 if (replace_path( attr
, redir
, prefix_len
, system32W
, syswow64W
)) return TRUE
;
2942 if (replace_path( attr
, redir
, prefix_len
, regeditW
, syswow64_regeditW
)) return TRUE
;
2948 /* there are no redirects on 64-bit */
2949 BOOL
get_redirect( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*redir
)
2951 redir
->Buffer
= NULL
;
2958 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
2960 /***********************************************************************
2963 void init_files(void)
2968 if (is_wow64
) init_redirects();
2970 /* a couple of directories that we don't want to return in directory searches */
2971 ignore_file( config_dir
);
2972 ignore_file( "/dev" );
2973 ignore_file( "/proc" );
2975 ignore_file( "/sys" );
2977 /* retrieve initial umask */
2978 start_umask
= umask( 0777 );
2979 umask( start_umask
);
2981 if (!open_hkcu_key( "Software\\Wine", &key
))
2983 static WCHAR showdotfilesW
[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
2986 UNICODE_STRING nameW
;
2988 init_unicode_string( &nameW
, showdotfilesW
);
2989 if (!NtQueryValueKey( key
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
2991 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
2992 show_dot_files
= IS_OPTION_TRUE( str
[0] );
2999 /******************************************************************************
3002 * Get the Unix path of a DOS device.
3004 static NTSTATUS
get_dos_device( char **unix_name
, int start_pos
)
3007 char *new_name
, *dev
= *unix_name
+ start_pos
;
3009 /* special case for drive devices */
3010 if (dev
[0] && dev
[1] == ':' && !dev
[2]) strcpy( dev
+ 1, "::" );
3012 if (strchr( dev
, '/' )) goto failed
;
3016 if (!stat( *unix_name
, &st
))
3018 TRACE( "-> %s\n", debugstr_a(*unix_name
));
3019 return STATUS_SUCCESS
;
3023 /* now try some defaults for it */
3024 if (!strcmp( dev
, "aux" ))
3026 strcpy( dev
, "com1" );
3029 if (!strcmp( dev
, "prn" ))
3031 strcpy( dev
, "lpt1" );
3036 if (dev
[1] == ':' && dev
[2] == ':') /* drive device */
3038 dev
[2] = 0; /* remove last ':' to get the drive mount point symlink */
3039 new_name
= get_default_drive_device( *unix_name
);
3042 *unix_name
= new_name
;
3043 if (!new_name
) return STATUS_BAD_DEVICE_TYPE
;
3044 dev
= NULL
; /* last try */
3049 return STATUS_BAD_DEVICE_TYPE
;
3053 /* return the length of the DOS namespace prefix if any */
3054 static inline int get_dos_prefix_len( const UNICODE_STRING
*name
)
3056 static const WCHAR nt_prefixW
[] = {'\\','?','?','\\'};
3057 static const WCHAR dosdev_prefixW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
3059 if (name
->Length
>= sizeof(nt_prefixW
) &&
3060 !memcmp( name
->Buffer
, nt_prefixW
, sizeof(nt_prefixW
) ))
3061 return ARRAY_SIZE( nt_prefixW
);
3063 if (name
->Length
>= sizeof(dosdev_prefixW
) &&
3064 !wcsnicmp( name
->Buffer
, dosdev_prefixW
, ARRAY_SIZE( dosdev_prefixW
)))
3065 return ARRAY_SIZE( dosdev_prefixW
);
3071 /***********************************************************************
3072 * remove_last_componentA
3074 * Remove the last component of the path. Helper for find_drive_rootA.
3076 static inline unsigned int remove_last_componentA( const char *path
, unsigned int len
)
3082 /* find start of the last path component */
3083 unsigned int prev
= len
;
3084 if (prev
<= 1) break; /* reached root */
3085 while (prev
> 1 && path
[prev
- 1] != '/') prev
--;
3086 /* does removing it take us up a level? */
3087 if (len
- prev
!= 1 || path
[prev
] != '.') /* not '.' */
3089 if (len
- prev
== 2 && path
[prev
] == '.' && path
[prev
+1] == '.') /* is it '..'? */
3094 /* strip off trailing slashes */
3095 while (prev
> 1 && path
[prev
- 1] == '/') prev
--;
3102 /***********************************************************************
3105 * Find a drive for which the root matches the beginning of the given path.
3106 * This can be used to translate a Unix path into a drive + DOS path.
3107 * Return value is the drive, or -1 on error. On success, ppath is modified
3108 * to point to the beginning of the DOS path.
3110 static NTSTATUS
find_drive_rootA( LPCSTR
*ppath
, unsigned int len
, int *drive_ret
)
3112 /* Starting with the full path, check if the device and inode match any of
3113 * the wine 'drives'. If not then remove the last path component and try
3114 * again. If the last component was a '..' then skip a normal component
3115 * since it's a directory that's ascended back out of.
3119 const char *path
= *ppath
;
3121 struct file_identity info
[MAX_DOS_DRIVES
];
3123 /* get device and inode of all drives */
3124 if (!get_drives_info( info
)) return STATUS_OBJECT_PATH_NOT_FOUND
;
3126 /* strip off trailing slashes */
3127 while (len
> 1 && path
[len
- 1] == '/') len
--;
3129 /* make a copy of the path */
3130 if (!(buffer
= malloc( len
+ 1 ))) return STATUS_NO_MEMORY
;
3131 memcpy( buffer
, path
, len
);
3136 if (!stat( buffer
, &st
) && S_ISDIR( st
.st_mode
))
3138 /* Find the drive */
3139 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
3141 if ((info
[drive
].dev
== st
.st_dev
) && (info
[drive
].ino
== st
.st_ino
))
3143 if (len
== 1) len
= 0; /* preserve root slash in returned path */
3144 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
3145 debugstr_a(path
), 'A' + drive
, debugstr_a(buffer
), debugstr_a(path
+ len
));
3149 return STATUS_SUCCESS
;
3153 if (len
<= 1) break; /* reached root */
3154 len
= remove_last_componentA( buffer
, len
);
3158 return STATUS_OBJECT_PATH_NOT_FOUND
;
3162 /******************************************************************************
3165 * Recursively search directories from the dir queue for a given inode.
3167 static NTSTATUS
find_file_id( char **unix_name
, ULONG
*len
, ULONGLONG file_id
, dev_t dev
)
3174 char *name
= *unix_name
;
3176 while (!(status
= next_dir_in_queue( name
)))
3178 if (!(dir
= opendir( name
))) continue;
3179 TRACE( "searching %s for %s\n", debugstr_a(name
), wine_dbgstr_longlong(file_id
) );
3180 pos
= strlen( name
);
3181 if (pos
+ MAX_DIR_ENTRY_LEN
>= *len
/ sizeof(WCHAR
))
3183 if (!(name
= realloc( name
, *len
* 2 )))
3186 return STATUS_NO_MEMORY
;
3192 while ((de
= readdir( dir
)))
3194 if (!strcmp( de
->d_name
, "." ) || !strcmp( de
->d_name
, ".." )) continue;
3195 strcpy( name
+ pos
, de
->d_name
);
3196 if (lstat( name
, &st
) == -1) continue;
3197 if (st
.st_dev
!= dev
) continue;
3198 if (st
.st_ino
== file_id
)
3201 return STATUS_SUCCESS
;
3203 if (!S_ISDIR( st
.st_mode
)) continue;
3204 if ((status
= add_dir_to_queue( name
)) != STATUS_SUCCESS
)
3216 /******************************************************************************
3217 * file_id_to_unix_file_name
3219 * Lookup a file from its file id instead of its name.
3221 static NTSTATUS
file_id_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char **unix_name_ret
,
3222 UNICODE_STRING
*nt_name
)
3224 enum server_fd_type type
;
3225 int old_cwd
, root_fd
, needs_close
;
3230 struct stat st
, root_st
;
3232 nt_name
->Buffer
= NULL
;
3233 if (attr
->ObjectName
->Length
!= sizeof(ULONGLONG
)) return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3234 if (!attr
->RootDirectory
) return STATUS_INVALID_PARAMETER
;
3235 memcpy( &file_id
, attr
->ObjectName
->Buffer
, sizeof(file_id
) );
3237 len
= 2 * MAX_DIR_ENTRY_LEN
+ 4;
3238 if (!(unix_name
= malloc( len
))) return STATUS_NO_MEMORY
;
3239 strcpy( unix_name
, "." );
3241 if ((status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3244 if (type
!= FD_TYPE_DIR
)
3246 status
= STATUS_OBJECT_TYPE_MISMATCH
;
3250 fstat( root_fd
, &root_st
);
3251 if (root_st
.st_ino
== file_id
) /* shortcut for "." */
3253 status
= STATUS_SUCCESS
;
3257 mutex_lock( &dir_mutex
);
3258 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3260 /* shortcut for ".." */
3261 if (!stat( "..", &st
) && st
.st_dev
== root_st
.st_dev
&& st
.st_ino
== file_id
)
3263 strcpy( unix_name
, ".." );
3264 status
= STATUS_SUCCESS
;
3268 status
= add_dir_to_queue( "." );
3270 status
= find_file_id( &unix_name
, &len
, file_id
, root_st
.st_dev
);
3271 if (!status
) /* get rid of "./" prefix */
3272 memmove( unix_name
, unix_name
+ 2, strlen(unix_name
) - 1 );
3275 if (fchdir( old_cwd
) == -1) chdir( "/" );
3277 else status
= errno_to_status( errno
);
3278 mutex_unlock( &dir_mutex
);
3279 if (old_cwd
!= -1) close( old_cwd
);
3282 if (status
== STATUS_SUCCESS
)
3284 TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id
), debugstr_a(unix_name
) );
3285 *unix_name_ret
= unix_name
;
3287 nt_name
->MaximumLength
= (strlen(unix_name
) + 1) * sizeof(WCHAR
);
3288 if ((nt_name
->Buffer
= malloc( nt_name
->MaximumLength
)))
3290 DWORD i
, len
= ntdll_umbstowcs( unix_name
, strlen(unix_name
), nt_name
->Buffer
, strlen(unix_name
) );
3291 nt_name
->Buffer
[len
] = 0;
3292 nt_name
->Length
= len
* sizeof(WCHAR
);
3293 for (i
= 0; i
< len
; i
++) if (nt_name
->Buffer
[i
] == '/') nt_name
->Buffer
[i
] = '\\';
3298 TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id
), attr
->RootDirectory
);
3301 if (needs_close
) close( root_fd
);
3306 /******************************************************************************
3309 * Helper for nt_to_unix_file_name
3311 static NTSTATUS
lookup_unix_name( const WCHAR
*name
, int name_len
, char **buffer
, int unix_len
, int pos
,
3312 UINT disposition
, BOOL is_unix
)
3314 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, '/', 0 };
3318 char *unix_name
= *buffer
;
3319 const WCHAR
*ptr
, *end
;
3321 /* check syntax of individual components */
3323 for (ptr
= name
, end
= name
+ name_len
; ptr
< end
; ptr
++)
3325 if (*ptr
== '\\') return STATUS_OBJECT_NAME_INVALID
; /* duplicate backslash */
3328 if (ptr
+ 1 == end
) return STATUS_OBJECT_NAME_INVALID
; /* "." element */
3329 if (ptr
[1] == '\\') return STATUS_OBJECT_NAME_INVALID
; /* "." element */
3332 if (ptr
+ 2 == end
) return STATUS_OBJECT_NAME_INVALID
; /* ".." element */
3333 if (ptr
[2] == '\\') return STATUS_OBJECT_NAME_INVALID
; /* ".." element */
3336 /* check for invalid characters (all chars except 0 are valid for unix) */
3337 for ( ; ptr
< end
&& *ptr
!= '\\'; ptr
++)
3339 if (!*ptr
) return STATUS_OBJECT_NAME_INVALID
;
3340 if (is_unix
) continue;
3341 if (*ptr
< 32 || wcschr( invalid_charsW
, *ptr
)) return STATUS_OBJECT_NAME_INVALID
;
3345 /* try a shortcut first */
3347 unix_name
[pos
] = '/';
3348 ret
= ntdll_wcstoumbs( name
, name_len
, unix_name
+ pos
+ 1, unix_len
- pos
- 1, TRUE
);
3349 if (ret
>= 0 && ret
< unix_len
- pos
- 1)
3352 unix_name
[pos
+ 1 + ret
] = 0;
3353 for (p
= unix_name
+ pos
; *p
; p
++) if (*p
== '\\') *p
= '/';
3354 if (!stat( unix_name
, &st
))
3356 if (disposition
== FILE_CREATE
) return STATUS_OBJECT_NAME_COLLISION
;
3357 return STATUS_SUCCESS
;
3361 if (!name_len
) /* empty name -> drive root doesn't exist */
3362 return STATUS_OBJECT_PATH_NOT_FOUND
;
3363 if (is_unix
&& (disposition
== FILE_OPEN
|| disposition
== FILE_OVERWRITE
))
3364 return STATUS_OBJECT_NAME_NOT_FOUND
;
3366 /* now do it component by component */
3370 const WCHAR
*end
, *next
;
3373 while (end
< name
+ name_len
&& *end
!= '\\') end
++;
3375 if (next
< name
+ name_len
) next
++;
3376 name_len
-= next
- name
;
3378 /* grow the buffer if needed */
3380 if (unix_len
- pos
< MAX_DIR_ENTRY_LEN
+ 3)
3383 unix_len
+= 2 * MAX_DIR_ENTRY_LEN
;
3384 if (!(new_name
= realloc( unix_name
, unix_len
))) return STATUS_NO_MEMORY
;
3385 unix_name
= *buffer
= new_name
;
3388 status
= find_file_in_dir( unix_name
, pos
, name
, end
- name
, is_unix
);
3390 /* if this is the last element, not finding it is not necessarily fatal */
3393 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
3395 if (disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
3397 ret
= ntdll_wcstoumbs( name
, end
- name
, unix_name
+ pos
+ 1, MAX_DIR_ENTRY_LEN
+ 1, TRUE
);
3398 if (ret
> 0 && ret
<= MAX_DIR_ENTRY_LEN
)
3400 unix_name
[pos
] = '/';
3402 if (end
< next
) unix_name
[pos
++] = '/';
3404 status
= STATUS_NO_SUCH_FILE
;
3409 else if (status
== STATUS_SUCCESS
&& disposition
== FILE_CREATE
)
3411 status
= STATUS_OBJECT_NAME_COLLISION
;
3413 if (end
< next
) strcat( unix_name
, "/" );
3415 else if (status
== STATUS_OBJECT_NAME_NOT_FOUND
) status
= STATUS_OBJECT_PATH_NOT_FOUND
;
3417 if (status
!= STATUS_SUCCESS
) break;
3419 pos
+= strlen( unix_name
+ pos
);
3427 /******************************************************************************
3428 * nt_to_unix_file_name_no_root
3430 static NTSTATUS
nt_to_unix_file_name_no_root( const UNICODE_STRING
*nameW
, char **unix_name_ret
,
3433 static const WCHAR unixW
[] = {'u','n','i','x'};
3434 static const WCHAR invalid_charsW
[] = { INVALID_NT_CHARS
, 0 };
3436 NTSTATUS status
= STATUS_SUCCESS
;
3440 int pos
, ret
, name_len
, unix_len
, prefix_len
;
3441 WCHAR prefix
[MAX_DIR_ENTRY_LEN
+ 1];
3442 BOOLEAN is_unix
= FALSE
;
3444 name
= nameW
->Buffer
;
3445 name_len
= nameW
->Length
/ sizeof(WCHAR
);
3447 if (!name_len
|| name
[0] != '\\') return STATUS_OBJECT_PATH_SYNTAX_BAD
;
3449 if (!(pos
= get_dos_prefix_len( nameW
)))
3450 return STATUS_BAD_DEVICE_TYPE
; /* no DOS prefix, assume NT native name */
3455 if (!name_len
) return STATUS_OBJECT_NAME_INVALID
;
3457 /* check for sub-directory */
3458 for (pos
= 0; pos
< name_len
&& pos
<= MAX_DIR_ENTRY_LEN
; pos
++)
3460 if (name
[pos
] == '\\') break;
3461 if (name
[pos
] < 32 || wcschr( invalid_charsW
, name
[pos
] ))
3462 return STATUS_OBJECT_NAME_INVALID
;
3463 prefix
[pos
] = (name
[pos
] >= 'A' && name
[pos
] <= 'Z') ? name
[pos
] + 'a' - 'A' : name
[pos
];
3465 if (pos
> MAX_DIR_ENTRY_LEN
) return STATUS_OBJECT_NAME_INVALID
;
3467 if (pos
>= 4 && !memcmp( prefix
, unixW
, sizeof(unixW
) ))
3469 /* allow slash for unix namespace */
3470 if (pos
> 4 && prefix
[4] == '/') pos
= 4;
3474 prefix
[prefix_len
] = 0;
3476 unix_len
= name_len
* 3 + MAX_DIR_ENTRY_LEN
+ 3;
3477 unix_len
+= strlen(config_dir
) + sizeof("/dosdevices/");
3478 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3479 strcpy( unix_name
, config_dir
);
3480 strcat( unix_name
, "/dosdevices/" );
3481 pos
= strlen(unix_name
);
3483 ret
= ntdll_wcstoumbs( prefix
, prefix_len
, unix_name
+ pos
, unix_len
- pos
- 1, TRUE
);
3487 return STATUS_OBJECT_NAME_INVALID
;
3490 if (prefix_len
== name_len
) /* no subdir, plain DOS device */
3492 unix_name
[pos
+ ret
] = 0;
3493 *unix_name_ret
= unix_name
;
3494 return get_dos_device( unix_name_ret
, pos
);
3498 /* check if prefix exists (except for DOS drives to avoid extra stat calls) */
3500 if (wcschr( prefix
, '/' ))
3503 return STATUS_OBJECT_PATH_NOT_FOUND
;
3506 if (prefix_len
!= 2 || prefix
[1] != ':')
3509 if (lstat( unix_name
, &st
) == -1 && errno
== ENOENT
)
3514 return STATUS_BAD_DEVICE_TYPE
;
3516 pos
= 0; /* fall back to unix root */
3520 prefix_len
++; /* skip initial backslash */
3521 if (name_len
> prefix_len
&& name
[prefix_len
] == '\\') prefix_len
++; /* allow a second backslash */
3523 name_len
-= prefix_len
;
3525 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, pos
, disposition
, is_unix
);
3526 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3528 TRACE( "%s -> %s\n", debugstr_us(nameW
), debugstr_a(unix_name
) );
3529 *unix_name_ret
= unix_name
;
3533 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
3540 /******************************************************************************
3541 * nt_to_unix_file_name
3543 * Convert a file name from NT namespace to Unix namespace.
3545 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3546 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3547 * returned, but the unix name is still filled in properly.
3549 NTSTATUS
nt_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char **name_ret
, UINT disposition
)
3551 enum server_fd_type type
;
3552 int old_cwd
, root_fd
, needs_close
;
3555 int name_len
, unix_len
;
3558 if (!attr
->RootDirectory
) /* without root dir fall back to normal lookup */
3559 return nt_to_unix_file_name_no_root( attr
->ObjectName
, name_ret
, disposition
);
3561 name
= attr
->ObjectName
->Buffer
;
3562 name_len
= attr
->ObjectName
->Length
/ sizeof(WCHAR
);
3564 if (name_len
&& name
[0] == '\\') return STATUS_INVALID_PARAMETER
;
3566 unix_len
= name_len
* 3 + MAX_DIR_ENTRY_LEN
+ 3;
3567 if (!(unix_name
= malloc( unix_len
))) return STATUS_NO_MEMORY
;
3570 if (!(status
= server_get_unix_fd( attr
->RootDirectory
, 0, &root_fd
, &needs_close
, &type
, NULL
)))
3572 if (type
!= FD_TYPE_DIR
)
3574 if (needs_close
) close( root_fd
);
3575 status
= STATUS_BAD_DEVICE_TYPE
;
3579 mutex_lock( &dir_mutex
);
3580 if ((old_cwd
= open( ".", O_RDONLY
)) != -1 && fchdir( root_fd
) != -1)
3582 status
= lookup_unix_name( name
, name_len
, &unix_name
, unix_len
, 1, disposition
, FALSE
);
3583 if (fchdir( old_cwd
) == -1) chdir( "/" );
3585 else status
= errno_to_status( errno
);
3586 mutex_unlock( &dir_mutex
);
3587 if (old_cwd
!= -1) close( old_cwd
);
3588 if (needs_close
) close( root_fd
);
3591 else if (status
== STATUS_OBJECT_TYPE_MISMATCH
) status
= STATUS_BAD_DEVICE_TYPE
;
3593 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
3595 TRACE( "%s -> %s\n", debugstr_us(attr
->ObjectName
), debugstr_a(unix_name
) );
3596 *name_ret
= unix_name
;
3600 TRACE( "%s not found in %s\n", debugstr_w(name
), unix_name
);
3607 /******************************************************************************
3608 * wine_nt_to_unix_file_name
3610 * Convert a file name from NT namespace to Unix namespace.
3612 * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path
3613 * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is
3614 * returned, but the unix name is still filled in properly.
3616 NTSTATUS WINAPI
wine_nt_to_unix_file_name( const OBJECT_ATTRIBUTES
*attr
, char *nameA
, ULONG
*size
,
3619 char *buffer
= NULL
;
3621 UNICODE_STRING redir
;
3622 OBJECT_ATTRIBUTES new_attr
= *attr
;
3624 get_redirect( &new_attr
, &redir
);
3625 status
= nt_to_unix_file_name( &new_attr
, &buffer
, disposition
);
3629 struct stat st1
, st2
;
3630 char *name
= buffer
;
3632 /* remove dosdevices prefix for z: drive if it points to the Unix root */
3633 if (!strncmp( buffer
, config_dir
, strlen(config_dir
) ) &&
3634 !strncmp( buffer
+ strlen(config_dir
), "/dosdevices/z:/", 15 ))
3636 char *p
= buffer
+ strlen(config_dir
) + 14;
3638 if (!stat( buffer
, &st1
) && !stat( "/", &st2
) &&
3639 st1
.st_dev
== st2
.st_dev
&& st1
.st_ino
== st2
.st_ino
)
3644 if (*size
> strlen(name
)) strcpy( nameA
, name
);
3645 else status
= STATUS_BUFFER_TOO_SMALL
;
3646 *size
= strlen(name
) + 1;
3649 free( redir
.Buffer
);
3654 /******************************************************************
3657 * Get rid of . and .. components in the path.
3659 static void collapse_path( WCHAR
*path
)
3661 WCHAR
*p
, *start
, *next
;
3663 /* convert every / into a \ */
3664 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
3667 while (*p
&& *p
!= '\\') p
++;
3670 /* collapse duplicate backslashes */
3672 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
3682 case '\\': /* .\ component */
3684 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3686 case 0: /* final . */
3691 if (p
[2] == '\\') /* ..\ component */
3697 while (p
> start
&& p
[-1] != '\\') p
--;
3699 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
3702 else if (!p
[2]) /* final .. */
3707 while (p
> start
&& p
[-1] != '\\') p
--;
3716 /* skip to the next component */
3717 while (*p
&& *p
!= '\\') p
++;
3720 /* remove last dot in previous dir name */
3721 if (p
> start
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
3726 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
3727 while (p
> start
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
3732 /******************************************************************
3733 * unix_to_nt_file_name
3735 NTSTATUS
unix_to_nt_file_name( const char *name
, WCHAR
**nt
)
3737 static const WCHAR unix_prefixW
[] = {'\\','?','?','\\','u','n','i','x',0};
3738 WCHAR dos_prefixW
[] = {'\\','?','?','\\','A',':','\\',0};
3739 const WCHAR
*prefix
= unix_prefixW
;
3740 unsigned int lenW
, lenA
= strlen(name
);
3741 const char *path
= name
;
3746 status
= find_drive_rootA( &path
, lenA
, &drive
);
3747 lenA
-= path
- name
;
3749 if (status
== STATUS_SUCCESS
)
3751 while (lenA
&& path
[0] == '/') { lenA
--; path
++; }
3752 dos_prefixW
[4] += drive
;
3753 prefix
= dos_prefixW
;
3755 else if (status
!= STATUS_OBJECT_PATH_NOT_FOUND
) return status
;
3757 lenW
= wcslen( prefix
);
3758 if (!(buffer
= malloc( (lenA
+ lenW
+ 1) * sizeof(WCHAR
) ))) return STATUS_NO_MEMORY
;
3759 memcpy( buffer
, prefix
, lenW
* sizeof(WCHAR
) );
3760 lenW
+= ntdll_umbstowcs( path
, lenA
, buffer
+ lenW
, lenA
);
3762 collapse_path( buffer
);
3764 return STATUS_SUCCESS
;
3768 /******************************************************************
3769 * wine_unix_to_nt_file_name
3771 NTSTATUS WINAPI
wine_unix_to_nt_file_name( const char *name
, WCHAR
*buffer
, ULONG
*size
)
3773 WCHAR
*nt_name
= NULL
;
3776 if (name
[0] != '/') return STATUS_INVALID_PARAMETER
; /* relative paths are not supported */
3778 status
= unix_to_nt_file_name( name
, &nt_name
);
3781 if (*size
> wcslen(nt_name
)) wcscpy( buffer
, nt_name
);
3782 else status
= STATUS_BUFFER_TOO_SMALL
;
3783 *size
= wcslen(nt_name
) + 1;
3790 /***********************************************************************
3793 * Simplified version of RtlGetFullPathName_U.
3795 NTSTATUS
get_full_path( const WCHAR
*name
, const WCHAR
*curdir
, WCHAR
**path
)
3797 static const WCHAR uncW
[] = {'\\','?','?','\\','U','N','C','\\',0};
3798 static const WCHAR devW
[] = {'\\','?','?','\\',0};
3799 static const WCHAR unixW
[] = {'u','n','i','x'};
3800 WCHAR
*ret
, root
[] = {'\\','?','?','\\','C',':','\\',0};
3801 NTSTATUS status
= STATUS_SUCCESS
;
3802 const WCHAR
*prefix
;
3804 if (IS_SEPARATOR(name
[0]) && IS_SEPARATOR(name
[1])) /* \\ prefix */
3806 if ((name
[2] == '.' || name
[2] == '?') && IS_SEPARATOR(name
[3])) /* \\?\ device */
3809 if (!wcsnicmp( name
, unixW
, 4 ) && IS_SEPARATOR(name
[4])) /* \\?\unix special name */
3813 unix_name
= malloc( wcslen(name
) * 3 + 1 );
3814 ntdll_wcstoumbs( name
, wcslen(name
) + 1, unix_name
, wcslen(name
) * 3 + 1, FALSE
);
3815 status
= unix_to_nt_file_name( unix_name
, path
);
3821 else prefix
= uncW
; /* UNC path */
3823 else if (IS_SEPARATOR(name
[0])) /* absolute path */
3825 root
[4] = curdir
[4];
3828 else if (name
[0] && name
[1] == ':') /* drive letter */
3830 root
[4] = towupper(name
[0]);
3834 else prefix
= curdir
; /* relative path */
3836 ret
= malloc( (wcslen(prefix
) + wcslen(name
) + 1) * sizeof(WCHAR
) );
3837 wcscpy( ret
, prefix
);
3838 wcscat( ret
, name
);
3839 collapse_path( ret
);
3841 return STATUS_SUCCESS
;
3845 /***********************************************************************
3848 * Unmount the specified device.
3850 static NTSTATUS
unmount_device( HANDLE handle
)
3853 int unix_fd
, needs_close
;
3855 if (!(status
= server_get_unix_fd( handle
, 0, &unix_fd
, &needs_close
, NULL
, NULL
)))
3858 char *mount_point
= NULL
;
3860 if (fstat( unix_fd
, &st
) == -1 || !is_valid_mounted_device( &st
))
3861 status
= STATUS_INVALID_PARAMETER
;
3864 if ((mount_point
= get_device_mount_point( st
.st_rdev
)))
3867 static const char umount
[] = "diskutil unmount >/dev/null 2>&1 ";
3869 static const char umount
[] = "umount >/dev/null 2>&1 ";
3871 char *cmd
= malloc( strlen(mount_point
)+sizeof(umount
));
3874 strcpy( cmd
, umount
);
3875 strcat( cmd
, mount_point
);
3879 /* umount will fail to release the loop device since we still have
3880 a handle to it, so we release it here */
3881 if (major(st
.st_rdev
) == LOOP_MAJOR
) ioctl( unix_fd
, 0x4c01 /*LOOP_CLR_FD*/, 0 );
3884 free( mount_point
);
3887 if (needs_close
) close( unix_fd
);
3893 /******************************************************************************
3896 * Helper for NtCreateFile that takes a Unix path.
3898 NTSTATUS
open_unix_file( HANDLE
*handle
, const char *unix_name
, ACCESS_MASK access
,
3899 OBJECT_ATTRIBUTES
*attr
, ULONG attributes
, ULONG sharing
, ULONG disposition
,
3900 ULONG options
, void *ea_buffer
, ULONG ea_length
)
3902 struct object_attributes
*objattr
;
3906 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
3908 SERVER_START_REQ( create_file
)
3910 req
->access
= access
;
3911 req
->sharing
= sharing
;
3912 req
->create
= disposition
;
3913 req
->options
= options
;
3914 req
->attrs
= attributes
;
3915 wine_server_add_data( req
, objattr
, len
);
3916 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
3917 status
= wine_server_call( req
);
3918 *handle
= wine_server_ptr_handle( reply
->handle
);
3926 /******************************************************************************
3927 * NtCreateFile (NTDLL.@)
3929 NTSTATUS WINAPI
NtCreateFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
3930 IO_STATUS_BLOCK
*io
, LARGE_INTEGER
*alloc_size
,
3931 ULONG attributes
, ULONG sharing
, ULONG disposition
,
3932 ULONG options
, void *ea_buffer
, ULONG ea_length
)
3934 OBJECT_ATTRIBUTES new_attr
;
3935 UNICODE_STRING nt_name
;
3937 BOOL created
= FALSE
;
3940 TRACE( "handle=%p access=%08x name=%s objattr=%08x root=%p sec=%p io=%p alloc_size=%p "
3941 "attr=%08x sharing=%08x disp=%d options=%08x ea=%p.0x%08x\n",
3942 handle
, access
, debugstr_us(attr
->ObjectName
), attr
->Attributes
,
3943 attr
->RootDirectory
, attr
->SecurityDescriptor
, io
, alloc_size
,
3944 attributes
, sharing
, disposition
, options
, ea_buffer
, ea_length
);
3947 if (!attr
|| !attr
->ObjectName
) return STATUS_INVALID_PARAMETER
;
3949 if (alloc_size
) FIXME( "alloc_size not supported\n" );
3952 if (options
& FILE_OPEN_BY_FILE_ID
)
3954 status
= file_id_to_unix_file_name( &new_attr
, &unix_name
, &nt_name
);
3955 if (!status
) new_attr
.ObjectName
= &nt_name
;
3959 get_redirect( &new_attr
, &nt_name
);
3960 status
= nt_to_unix_file_name( &new_attr
, &unix_name
, disposition
);
3963 if (status
== STATUS_BAD_DEVICE_TYPE
)
3965 status
= server_open_file_object( handle
, access
, &new_attr
, sharing
, options
);
3966 if (status
== STATUS_SUCCESS
) io
->Information
= FILE_OPENED
;
3967 free( nt_name
.Buffer
);
3968 return io
->u
.Status
= status
;
3971 if (status
== STATUS_NO_SUCH_FILE
&& disposition
!= FILE_OPEN
&& disposition
!= FILE_OVERWRITE
)
3974 status
= STATUS_SUCCESS
;
3977 if (status
== STATUS_SUCCESS
)
3979 status
= open_unix_file( handle
, unix_name
, access
, &new_attr
, attributes
,
3980 sharing
, disposition
, options
, ea_buffer
, ea_length
);
3983 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
3985 if (status
== STATUS_SUCCESS
)
3987 if (created
) io
->Information
= FILE_CREATED
;
3988 else switch(disposition
)
3990 case FILE_SUPERSEDE
:
3991 io
->Information
= FILE_SUPERSEDED
;
3994 io
->Information
= FILE_CREATED
;
3998 io
->Information
= FILE_OPENED
;
4000 case FILE_OVERWRITE
:
4001 case FILE_OVERWRITE_IF
:
4002 io
->Information
= FILE_OVERWRITTEN
;
4006 if (io
->Information
== FILE_CREATED
&& (attributes
& XATTR_ATTRIBS_MASK
))
4008 int fd
, needs_close
;
4010 /* set any DOS extended attributes */
4011 if (!server_get_unix_fd( *handle
, 0, &fd
, &needs_close
, NULL
, NULL
))
4013 if (fd_set_dos_attrib( fd
, attributes
) == -1 && errno
!= ENOTSUP
)
4014 WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB
". errno %d (%s)",
4015 errno
, strerror( errno
) );
4016 if (needs_close
) close( fd
);
4020 else if (status
== STATUS_TOO_MANY_OPENED_FILES
)
4023 if (!once
++) ERR_(winediag
)( "Too many open files, ulimit -n probably needs to be increased\n" );
4026 free( nt_name
.Buffer
);
4027 return io
->u
.Status
= status
;
4031 /******************************************************************************
4032 * NtOpenFile (NTDLL.@)
4034 NTSTATUS WINAPI
NtOpenFile( HANDLE
*handle
, ACCESS_MASK access
, OBJECT_ATTRIBUTES
*attr
,
4035 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG options
)
4037 return NtCreateFile( handle
, access
, attr
, io
, NULL
, 0, sharing
, FILE_OPEN
, options
, NULL
, 0 );
4041 /******************************************************************************
4042 * NtCreateMailslotFile (NTDLL.@)
4044 NTSTATUS WINAPI
NtCreateMailslotFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
4045 IO_STATUS_BLOCK
*io
, ULONG options
, ULONG quota
, ULONG msg_size
,
4046 LARGE_INTEGER
*timeout
)
4050 struct object_attributes
*objattr
;
4052 TRACE( "%p %08x %p %p %08x %08x %08x %p\n",
4053 handle
, access
, attr
, io
, options
, quota
, msg_size
, timeout
);
4056 if (!attr
) return STATUS_INVALID_PARAMETER
;
4057 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
4059 SERVER_START_REQ( create_mailslot
)
4061 req
->access
= access
;
4062 req
->max_msgsize
= msg_size
;
4063 req
->read_timeout
= timeout
? timeout
->QuadPart
: -1;
4064 wine_server_add_data( req
, objattr
, len
);
4065 if (!(status
= wine_server_call( req
))) *handle
= wine_server_ptr_handle( reply
->handle
);
4074 /******************************************************************
4075 * NtCreateNamedPipeFile (NTDLL.@)
4077 NTSTATUS WINAPI
NtCreateNamedPipeFile( HANDLE
*handle
, ULONG access
, OBJECT_ATTRIBUTES
*attr
,
4078 IO_STATUS_BLOCK
*io
, ULONG sharing
, ULONG dispo
, ULONG options
,
4079 ULONG pipe_type
, ULONG read_mode
, ULONG completion_mode
,
4080 ULONG max_inst
, ULONG inbound_quota
, ULONG outbound_quota
,
4081 LARGE_INTEGER
*timeout
)
4085 struct object_attributes
*objattr
;
4088 if (!attr
) return STATUS_INVALID_PARAMETER
;
4090 TRACE( "(%p %x %s %p %x %d %x %d %d %d %d %d %d %p)\n",
4091 handle
, access
, debugstr_us(attr
->ObjectName
), io
, sharing
, dispo
,
4092 options
, pipe_type
, read_mode
, completion_mode
, max_inst
, inbound_quota
,
4093 outbound_quota
, timeout
);
4095 /* assume we only get relative timeout */
4096 if (timeout
->QuadPart
> 0) FIXME( "Wrong time %s\n", wine_dbgstr_longlong(timeout
->QuadPart
) );
4098 if ((status
= alloc_object_attributes( attr
, &objattr
, &len
))) return status
;
4100 SERVER_START_REQ( create_named_pipe
)
4102 req
->access
= access
;
4103 req
->options
= options
;
4104 req
->sharing
= sharing
;
4106 (pipe_type
? NAMED_PIPE_MESSAGE_STREAM_WRITE
: 0) |
4107 (read_mode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0) |
4108 (completion_mode
? NAMED_PIPE_NONBLOCKING_MODE
: 0);
4109 req
->maxinstances
= max_inst
;
4110 req
->outsize
= outbound_quota
;
4111 req
->insize
= inbound_quota
;
4112 req
->timeout
= timeout
->QuadPart
;
4113 wine_server_add_data( req
, objattr
, len
);
4114 if (!(status
= wine_server_call( req
))) *handle
= wine_server_ptr_handle( reply
->handle
);
4123 /******************************************************************
4124 * NtDeleteFile (NTDLL.@)
4126 NTSTATUS WINAPI
NtDeleteFile( OBJECT_ATTRIBUTES
*attr
)
4131 UNICODE_STRING nt_name
;
4132 OBJECT_ATTRIBUTES new_attr
= *attr
;
4134 get_redirect( &new_attr
, &nt_name
);
4135 if (!(status
= nt_to_unix_file_name( &new_attr
, &unix_name
, FILE_OPEN
)))
4137 if (!(status
= open_unix_file( &handle
, unix_name
, GENERIC_READ
| GENERIC_WRITE
| DELETE
, &new_attr
,
4138 0, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, FILE_OPEN
,
4139 FILE_DELETE_ON_CLOSE
, NULL
, 0 )))
4143 free( nt_name
.Buffer
);
4148 /******************************************************************************
4149 * NtQueryFullAttributesFile (NTDLL.@)
4151 NTSTATUS WINAPI
NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES
*attr
,
4152 FILE_NETWORK_OPEN_INFORMATION
*info
)
4156 UNICODE_STRING redir
;
4157 OBJECT_ATTRIBUTES new_attr
= *attr
;
4159 get_redirect( &new_attr
, &redir
);
4160 if (!(status
= nt_to_unix_file_name( &new_attr
, &unix_name
, FILE_OPEN
)))
4165 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4166 status
= errno_to_status( errno
);
4167 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4168 status
= STATUS_INVALID_INFO_CLASS
;
4171 FILE_BASIC_INFORMATION basic
;
4172 FILE_STANDARD_INFORMATION std
;
4174 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
4175 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
4177 info
->CreationTime
= basic
.CreationTime
;
4178 info
->LastAccessTime
= basic
.LastAccessTime
;
4179 info
->LastWriteTime
= basic
.LastWriteTime
;
4180 info
->ChangeTime
= basic
.ChangeTime
;
4181 info
->AllocationSize
= std
.AllocationSize
;
4182 info
->EndOfFile
= std
.EndOfFile
;
4183 info
->FileAttributes
= basic
.FileAttributes
;
4184 if (is_hidden_file( attr
->ObjectName
)) info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
4188 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
4189 free( redir
.Buffer
);
4194 /******************************************************************************
4195 * NtQueryAttributesFile (NTDLL.@)
4197 NTSTATUS WINAPI
NtQueryAttributesFile( const OBJECT_ATTRIBUTES
*attr
, FILE_BASIC_INFORMATION
*info
)
4201 UNICODE_STRING redir
;
4202 OBJECT_ATTRIBUTES new_attr
= *attr
;
4204 get_redirect( &new_attr
, &redir
);
4205 if (!(status
= nt_to_unix_file_name( &new_attr
, &unix_name
, FILE_OPEN
)))
4210 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4211 status
= errno_to_status( errno
);
4212 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4213 status
= STATUS_INVALID_INFO_CLASS
;
4216 status
= fill_file_info( &st
, attributes
, info
, FileBasicInformation
);
4217 if (is_hidden_file( attr
->ObjectName
)) info
->FileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
4221 else WARN( "%s not found (%x)\n", debugstr_us(attr
->ObjectName
), status
);
4222 free( redir
.Buffer
);
4227 /******************************************************************************
4228 * NtQueryInformationFile (NTDLL.@)
4230 NTSTATUS WINAPI
NtQueryInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
4231 void *ptr
, ULONG len
, FILE_INFORMATION_CLASS
class )
4233 static const size_t info_sizes
[] =
4236 sizeof(FILE_DIRECTORY_INFORMATION
), /* FileDirectoryInformation */
4237 sizeof(FILE_FULL_DIRECTORY_INFORMATION
), /* FileFullDirectoryInformation */
4238 sizeof(FILE_BOTH_DIRECTORY_INFORMATION
), /* FileBothDirectoryInformation */
4239 sizeof(FILE_BASIC_INFORMATION
), /* FileBasicInformation */
4240 sizeof(FILE_STANDARD_INFORMATION
), /* FileStandardInformation */
4241 sizeof(FILE_INTERNAL_INFORMATION
), /* FileInternalInformation */
4242 sizeof(FILE_EA_INFORMATION
), /* FileEaInformation */
4243 0, /* FileAccessInformation */
4244 sizeof(FILE_NAME_INFORMATION
), /* FileNameInformation */
4245 sizeof(FILE_RENAME_INFORMATION
)-sizeof(WCHAR
), /* FileRenameInformation */
4246 0, /* FileLinkInformation */
4247 sizeof(FILE_NAMES_INFORMATION
)-sizeof(WCHAR
), /* FileNamesInformation */
4248 sizeof(FILE_DISPOSITION_INFORMATION
), /* FileDispositionInformation */
4249 sizeof(FILE_POSITION_INFORMATION
), /* FilePositionInformation */
4250 sizeof(FILE_FULL_EA_INFORMATION
), /* FileFullEaInformation */
4251 0, /* FileModeInformation */
4252 sizeof(FILE_ALIGNMENT_INFORMATION
), /* FileAlignmentInformation */
4253 sizeof(FILE_ALL_INFORMATION
), /* FileAllInformation */
4254 sizeof(FILE_ALLOCATION_INFORMATION
), /* FileAllocationInformation */
4255 sizeof(FILE_END_OF_FILE_INFORMATION
), /* FileEndOfFileInformation */
4256 0, /* FileAlternateNameInformation */
4257 sizeof(FILE_STREAM_INFORMATION
)-sizeof(WCHAR
), /* FileStreamInformation */
4258 sizeof(FILE_PIPE_INFORMATION
), /* FilePipeInformation */
4259 sizeof(FILE_PIPE_LOCAL_INFORMATION
), /* FilePipeLocalInformation */
4260 0, /* FilePipeRemoteInformation */
4261 sizeof(FILE_MAILSLOT_QUERY_INFORMATION
), /* FileMailslotQueryInformation */
4262 0, /* FileMailslotSetInformation */
4263 0, /* FileCompressionInformation */
4264 0, /* FileObjectIdInformation */
4265 0, /* FileCompletionInformation */
4266 0, /* FileMoveClusterInformation */
4267 0, /* FileQuotaInformation */
4268 0, /* FileReparsePointInformation */
4269 sizeof(FILE_NETWORK_OPEN_INFORMATION
), /* FileNetworkOpenInformation */
4270 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
), /* FileAttributeTagInformation */
4271 0, /* FileTrackingInformation */
4272 0, /* FileIdBothDirectoryInformation */
4273 0, /* FileIdFullDirectoryInformation */
4274 0, /* FileValidDataLengthInformation */
4275 0, /* FileShortNameInformation */
4276 0, /* FileIoCompletionNotificationInformation, */
4277 0, /* FileIoStatusBlockRangeInformation */
4278 0, /* FileIoPriorityHintInformation */
4279 0, /* FileSfioReserveInformation */
4280 0, /* FileSfioVolumeInformation */
4281 0, /* FileHardLinkInformation */
4282 0, /* FileProcessIdsUsingFileInformation */
4283 0, /* FileNormalizedNameInformation */
4284 0, /* FileNetworkPhysicalNameInformation */
4285 0, /* FileIdGlobalTxDirectoryInformation */
4286 0, /* FileIsRemoteDeviceInformation */
4287 0, /* FileAttributeCacheInformation */
4288 0, /* FileNumaNodeInformation */
4289 0, /* FileStandardLinkInformation */
4290 0, /* FileRemoteProtocolInformation */
4291 0, /* FileRenameInformationBypassAccessCheck */
4292 0, /* FileLinkInformationBypassAccessCheck */
4293 0, /* FileVolumeNameInformation */
4294 sizeof(FILE_ID_INFORMATION
), /* FileIdInformation */
4295 0, /* FileIdExtdDirectoryInformation */
4296 0, /* FileReplaceCompletionInformation */
4297 0, /* FileHardLinkFullIdInformation */
4298 0, /* FileIdExtdBothDirectoryInformation */
4302 int fd
, needs_close
= FALSE
;
4304 unsigned int options
;
4307 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, len
, class);
4309 io
->Information
= 0;
4311 if (class <= 0 || class >= FileMaximumInformation
)
4312 return io
->u
.Status
= STATUS_INVALID_INFO_CLASS
;
4313 if (!info_sizes
[class])
4314 return server_get_file_info( handle
, io
, ptr
, len
, class );
4315 if (len
< info_sizes
[class])
4316 return io
->u
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4318 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, &options
)))
4320 if (status
!= STATUS_BAD_DEVICE_TYPE
) return io
->u
.Status
= status
;
4321 return server_get_file_info( handle
, io
, ptr
, len
, class );
4326 case FileBasicInformation
:
4327 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1)
4328 status
= errno_to_status( errno
);
4329 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4330 status
= STATUS_INVALID_INFO_CLASS
;
4332 fill_file_info( &st
, attr
, ptr
, class );
4334 case FileStandardInformation
:
4336 FILE_STANDARD_INFORMATION
*info
= ptr
;
4338 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4341 fill_file_info( &st
, attr
, info
, class );
4342 info
->DeletePending
= FALSE
; /* FIXME */
4346 case FilePositionInformation
:
4348 FILE_POSITION_INFORMATION
*info
= ptr
;
4349 off_t res
= lseek( fd
, 0, SEEK_CUR
);
4350 if (res
== (off_t
)-1) status
= errno_to_status( errno
);
4351 else info
->CurrentByteOffset
.QuadPart
= res
;
4354 case FileInternalInformation
:
4355 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4356 else fill_file_info( &st
, attr
, ptr
, class );
4358 case FileEaInformation
:
4360 FILE_EA_INFORMATION
*info
= ptr
;
4364 case FileEndOfFileInformation
:
4365 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4366 else fill_file_info( &st
, attr
, ptr
, class );
4368 case FileAllInformation
:
4370 FILE_ALL_INFORMATION
*info
= ptr
;
4373 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4374 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4375 status
= STATUS_INVALID_INFO_CLASS
;
4376 else if (!(status
= server_get_unix_name( handle
, &unix_name
)))
4378 LONG name_len
= len
- FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
);
4380 fill_file_info( &st
, attr
, info
, FileAllInformation
);
4381 info
->StandardInformation
.DeletePending
= FALSE
; /* FIXME */
4382 info
->EaInformation
.EaSize
= 0;
4383 info
->AccessInformation
.AccessFlags
= 0; /* FIXME */
4384 info
->PositionInformation
.CurrentByteOffset
.QuadPart
= lseek( fd
, 0, SEEK_CUR
);
4385 info
->ModeInformation
.Mode
= 0; /* FIXME */
4386 info
->AlignmentInformation
.AlignmentRequirement
= 1; /* FIXME */
4388 status
= fill_name_info( unix_name
, &info
->NameInformation
, &name_len
);
4390 io
->Information
= FIELD_OFFSET(FILE_ALL_INFORMATION
, NameInformation
.FileName
) + name_len
;
4394 case FileMailslotQueryInformation
:
4396 FILE_MAILSLOT_QUERY_INFORMATION
*info
= ptr
;
4398 SERVER_START_REQ( set_mailslot_info
)
4400 req
->handle
= wine_server_obj_handle( handle
);
4402 status
= wine_server_call( req
);
4403 if (status
== STATUS_SUCCESS
)
4405 info
->MaximumMessageSize
= reply
->max_msgsize
;
4406 info
->MailslotQuota
= 0;
4407 info
->NextMessageSize
= 0;
4408 info
->MessagesAvailable
= 0;
4409 info
->ReadTimeout
.QuadPart
= reply
->read_timeout
;
4416 ULONG size
= info
->MaximumMessageSize
? info
->MaximumMessageSize
: 0x10000;
4417 if (size
> 0x10000) size
= 0x10000;
4418 if ((tmpbuf
= malloc( size
)))
4420 if (!server_get_unix_fd( handle
, FILE_READ_DATA
, &fd
, &needs_close
, NULL
, NULL
))
4422 int res
= recv( fd
, tmpbuf
, size
, MSG_PEEK
);
4423 info
->MessagesAvailable
= (res
> 0);
4424 info
->NextMessageSize
= (res
>= 0) ? res
: MAILSLOT_NO_MESSAGE
;
4425 if (needs_close
) close( fd
);
4432 case FileNameInformation
:
4434 FILE_NAME_INFORMATION
*info
= ptr
;
4437 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
4439 LONG name_len
= len
- FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
);
4440 status
= fill_name_info( unix_name
, info
, &name_len
);
4442 io
->Information
= FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
) + name_len
;
4446 case FileNetworkOpenInformation
:
4448 FILE_NETWORK_OPEN_INFORMATION
*info
= ptr
;
4451 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
4456 if (get_file_info( unix_name
, &st
, &attributes
) == -1)
4457 status
= errno_to_status( errno
);
4458 else if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
4459 status
= STATUS_INVALID_INFO_CLASS
;
4462 FILE_BASIC_INFORMATION basic
;
4463 FILE_STANDARD_INFORMATION std
;
4465 fill_file_info( &st
, attributes
, &basic
, FileBasicInformation
);
4466 fill_file_info( &st
, attributes
, &std
, FileStandardInformation
);
4468 info
->CreationTime
= basic
.CreationTime
;
4469 info
->LastAccessTime
= basic
.LastAccessTime
;
4470 info
->LastWriteTime
= basic
.LastWriteTime
;
4471 info
->ChangeTime
= basic
.ChangeTime
;
4472 info
->AllocationSize
= std
.AllocationSize
;
4473 info
->EndOfFile
= std
.EndOfFile
;
4474 info
->FileAttributes
= basic
.FileAttributes
;
4480 case FileIdInformation
:
4481 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4484 struct mountmgr_unix_drive drive
;
4485 FILE_ID_INFORMATION
*info
= ptr
;
4487 info
->VolumeSerialNumber
= 0;
4488 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) ))
4489 info
->VolumeSerialNumber
= drive
.serial
;
4490 memset( &info
->FileId
, 0, sizeof(info
->FileId
) );
4491 *(ULONGLONG
*)&info
->FileId
= st
.st_ino
;
4494 case FileAttributeTagInformation
:
4495 if (fd_get_file_info( fd
, options
, &st
, &attr
) == -1) status
= errno_to_status( errno
);
4498 FILE_ATTRIBUTE_TAG_INFORMATION
*info
= ptr
;
4499 info
->FileAttributes
= attr
;
4500 info
->ReparseTag
= 0; /* FIXME */
4501 if ((options
& FILE_OPEN_REPARSE_POINT
) && fd_is_mount_point( fd
, &st
))
4502 info
->ReparseTag
= IO_REPARSE_TAG_MOUNT_POINT
;
4506 FIXME("Unsupported class (%d)\n", class);
4507 status
= STATUS_NOT_IMPLEMENTED
;
4510 if (needs_close
) close( fd
);
4511 if (status
== STATUS_SUCCESS
&& !io
->Information
) io
->Information
= info_sizes
[class];
4512 return io
->u
.Status
= status
;
4516 /******************************************************************************
4517 * NtSetInformationFile (NTDLL.@)
4519 NTSTATUS WINAPI
NtSetInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
4520 void *ptr
, ULONG len
, FILE_INFORMATION_CLASS
class )
4522 int fd
, needs_close
;
4523 NTSTATUS status
= STATUS_SUCCESS
;
4525 TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle
, io
, ptr
, len
, class );
4529 case FileBasicInformation
:
4530 if (len
>= sizeof(FILE_BASIC_INFORMATION
))
4532 const FILE_BASIC_INFORMATION
*info
= ptr
;
4533 LARGE_INTEGER mtime
, atime
;
4535 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4536 return io
->u
.Status
= status
;
4538 mtime
.QuadPart
= info
->LastWriteTime
.QuadPart
== -1 ? 0 : info
->LastWriteTime
.QuadPart
;
4539 atime
.QuadPart
= info
->LastAccessTime
.QuadPart
== -1 ? 0 : info
->LastAccessTime
.QuadPart
;
4541 if (atime
.QuadPart
|| mtime
.QuadPart
)
4542 status
= set_file_times( fd
, &mtime
, &atime
);
4544 if (status
== STATUS_SUCCESS
&& info
->FileAttributes
)
4545 status
= fd_set_file_info( fd
, info
->FileAttributes
);
4547 if (needs_close
) close( fd
);
4549 else status
= STATUS_INVALID_PARAMETER_3
;
4552 case FilePositionInformation
:
4553 if (len
>= sizeof(FILE_POSITION_INFORMATION
))
4555 const FILE_POSITION_INFORMATION
*info
= ptr
;
4557 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
4558 return io
->u
.Status
= status
;
4560 if (lseek( fd
, info
->CurrentByteOffset
.QuadPart
, SEEK_SET
) == (off_t
)-1)
4561 status
= errno_to_status( errno
);
4563 if (needs_close
) close( fd
);
4565 else status
= STATUS_INVALID_PARAMETER_3
;
4568 case FileEndOfFileInformation
:
4569 if (len
>= sizeof(FILE_END_OF_FILE_INFORMATION
))
4571 const FILE_END_OF_FILE_INFORMATION
*info
= ptr
;
4573 SERVER_START_REQ( set_fd_eof_info
)
4575 req
->handle
= wine_server_obj_handle( handle
);
4576 req
->eof
= info
->EndOfFile
.QuadPart
;
4577 status
= wine_server_call( req
);
4581 else status
= STATUS_INVALID_PARAMETER_3
;
4584 case FilePipeInformation
:
4585 if (len
>= sizeof(FILE_PIPE_INFORMATION
))
4587 FILE_PIPE_INFORMATION
*info
= ptr
;
4589 if ((info
->CompletionMode
| info
->ReadMode
) & ~1)
4591 status
= STATUS_INVALID_PARAMETER
;
4595 SERVER_START_REQ( set_named_pipe_info
)
4597 req
->handle
= wine_server_obj_handle( handle
);
4598 req
->flags
= (info
->CompletionMode
? NAMED_PIPE_NONBLOCKING_MODE
: 0) |
4599 (info
->ReadMode
? NAMED_PIPE_MESSAGE_STREAM_READ
: 0);
4600 status
= wine_server_call( req
);
4604 else status
= STATUS_INVALID_PARAMETER_3
;
4607 case FileMailslotSetInformation
:
4609 FILE_MAILSLOT_SET_INFORMATION
*info
= ptr
;
4611 SERVER_START_REQ( set_mailslot_info
)
4613 req
->handle
= wine_server_obj_handle( handle
);
4614 req
->flags
= MAILSLOT_SET_READ_TIMEOUT
;
4615 req
->read_timeout
= info
->ReadTimeout
.QuadPart
;
4616 status
= wine_server_call( req
);
4622 case FileCompletionInformation
:
4623 if (len
>= sizeof(FILE_COMPLETION_INFORMATION
))
4625 FILE_COMPLETION_INFORMATION
*info
= ptr
;
4627 SERVER_START_REQ( set_completion_info
)
4629 req
->handle
= wine_server_obj_handle( handle
);
4630 req
->chandle
= wine_server_obj_handle( info
->CompletionPort
);
4631 req
->ckey
= info
->CompletionKey
;
4632 status
= wine_server_call( req
);
4636 else status
= STATUS_INVALID_PARAMETER_3
;
4639 case FileIoCompletionNotificationInformation
:
4640 if (len
>= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
))
4642 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION
*info
= ptr
;
4644 if (info
->Flags
& FILE_SKIP_SET_USER_EVENT_ON_FAST_IO
)
4645 FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
4647 SERVER_START_REQ( set_fd_completion_mode
)
4649 req
->handle
= wine_server_obj_handle( handle
);
4650 req
->flags
= info
->Flags
;
4651 status
= wine_server_call( req
);
4655 else status
= STATUS_INFO_LENGTH_MISMATCH
;
4658 case FileIoPriorityHintInformation
:
4659 if (len
>= sizeof(FILE_IO_PRIORITY_HINT_INFO
))
4661 FILE_IO_PRIORITY_HINT_INFO
*info
= ptr
;
4662 if (info
->PriorityHint
< MaximumIoPriorityHintType
)
4663 TRACE( "ignoring FileIoPriorityHintInformation %u\n", info
->PriorityHint
);
4665 status
= STATUS_INVALID_PARAMETER
;
4667 else status
= STATUS_INFO_LENGTH_MISMATCH
;
4670 case FileAllInformation
:
4671 status
= STATUS_INVALID_INFO_CLASS
;
4674 case FileValidDataLengthInformation
:
4675 if (len
>= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION
))
4678 const FILE_VALID_DATA_LENGTH_INFORMATION
*info
= ptr
;
4680 if ((status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, NULL
, NULL
)))
4681 return io
->u
.Status
= status
;
4683 if (fstat( fd
, &st
) == -1) status
= errno_to_status( errno
);
4684 else if (info
->ValidDataLength
.QuadPart
<= 0 || (off_t
)info
->ValidDataLength
.QuadPart
> st
.st_size
)
4685 status
= STATUS_INVALID_PARAMETER
;
4688 #ifdef HAVE_POSIX_FALLOCATE
4690 if ((err
= posix_fallocate( fd
, 0, (off_t
)info
->ValidDataLength
.QuadPart
)) != 0)
4692 if (err
== EOPNOTSUPP
) WARN( "posix_fallocate not supported on this filesystem\n" );
4693 else status
= errno_to_status( err
);
4696 WARN( "setting valid data length not supported\n" );
4699 if (needs_close
) close( fd
);
4701 else status
= STATUS_INVALID_PARAMETER_3
;
4704 case FileDispositionInformation
:
4705 if (len
>= sizeof(FILE_DISPOSITION_INFORMATION
))
4707 FILE_DISPOSITION_INFORMATION
*info
= ptr
;
4709 SERVER_START_REQ( set_fd_disp_info
)
4711 req
->handle
= wine_server_obj_handle( handle
);
4712 req
->unlink
= info
->DoDeleteFile
;
4713 status
= wine_server_call( req
);
4717 else status
= STATUS_INVALID_PARAMETER_3
;
4720 case FileRenameInformation
:
4721 if (len
>= sizeof(FILE_RENAME_INFORMATION
))
4723 FILE_RENAME_INFORMATION
*info
= ptr
;
4724 UNICODE_STRING name_str
, redir
;
4725 OBJECT_ATTRIBUTES attr
;
4728 name_str
.Buffer
= info
->FileName
;
4729 name_str
.Length
= info
->FileNameLength
;
4730 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4731 InitializeObjectAttributes( &attr
, &name_str
, OBJ_CASE_INSENSITIVE
, info
->RootDirectory
, NULL
);
4732 get_redirect( &attr
, &redir
);
4734 status
= nt_to_unix_file_name( &attr
, &unix_name
, FILE_OPEN_IF
);
4735 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
4737 SERVER_START_REQ( set_fd_name_info
)
4739 req
->handle
= wine_server_obj_handle( handle
);
4740 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4741 req
->namelen
= attr
.ObjectName
->Length
;
4743 req
->replace
= info
->ReplaceIfExists
;
4744 wine_server_add_data( req
, attr
.ObjectName
->Buffer
, attr
.ObjectName
->Length
);
4745 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4746 status
= wine_server_call( req
);
4752 free( redir
.Buffer
);
4754 else status
= STATUS_INVALID_PARAMETER_3
;
4757 case FileLinkInformation
:
4758 if (len
>= sizeof(FILE_LINK_INFORMATION
))
4760 FILE_LINK_INFORMATION
*info
= ptr
;
4761 UNICODE_STRING name_str
, redir
;
4762 OBJECT_ATTRIBUTES attr
;
4765 name_str
.Buffer
= info
->FileName
;
4766 name_str
.Length
= info
->FileNameLength
;
4767 name_str
.MaximumLength
= info
->FileNameLength
+ sizeof(WCHAR
);
4768 InitializeObjectAttributes( &attr
, &name_str
, OBJ_CASE_INSENSITIVE
, info
->RootDirectory
, NULL
);
4769 get_redirect( &attr
, &redir
);
4771 status
= nt_to_unix_file_name( &attr
, &unix_name
, FILE_OPEN_IF
);
4772 if (status
== STATUS_SUCCESS
|| status
== STATUS_NO_SUCH_FILE
)
4774 SERVER_START_REQ( set_fd_name_info
)
4776 req
->handle
= wine_server_obj_handle( handle
);
4777 req
->rootdir
= wine_server_obj_handle( attr
.RootDirectory
);
4778 req
->namelen
= attr
.ObjectName
->Length
;
4780 req
->replace
= info
->ReplaceIfExists
;
4781 wine_server_add_data( req
, attr
.ObjectName
->Buffer
, attr
.ObjectName
->Length
);
4782 wine_server_add_data( req
, unix_name
, strlen(unix_name
) );
4783 status
= wine_server_call( req
);
4789 free( redir
.Buffer
);
4791 else status
= STATUS_INVALID_PARAMETER_3
;
4795 FIXME("Unsupported class (%d)\n", class);
4796 status
= STATUS_NOT_IMPLEMENTED
;
4799 io
->Information
= 0;
4800 return io
->u
.Status
= status
;
4804 /***********************************************************************
4805 * Asynchronous file I/O *
4808 struct async_fileio_read
4810 struct async_fileio io
;
4812 unsigned int already
;
4817 struct async_fileio_write
4819 struct async_fileio io
;
4821 unsigned int already
;
4825 struct async_fileio_read_changes
4827 struct async_fileio io
;
4836 struct async_fileio io
;
4837 void *buffer
; /* buffer for output */
4838 ULONG size
; /* size of buffer */
4841 static struct async_fileio
*fileio_freelist
;
4843 void release_fileio( struct async_fileio
*io
)
4847 struct async_fileio
*next
= fileio_freelist
;
4849 if (InterlockedCompareExchangePointer( (void **)&fileio_freelist
, io
, next
) == next
) return;
4853 struct async_fileio
*alloc_fileio( DWORD size
, async_callback_t callback
, HANDLE handle
)
4855 /* first free remaining previous fileinfos */
4856 struct async_fileio
*io
= InterlockedExchangePointer( (void **)&fileio_freelist
, NULL
);
4860 struct async_fileio
*next
= io
->next
;
4865 if ((io
= malloc( size
)))
4867 io
->callback
= callback
;
4868 io
->handle
= handle
;
4873 /* callback for irp async I/O completion */
4874 static BOOL
irp_completion( void *user
, ULONG_PTR
*info
, NTSTATUS
*status
)
4876 struct async_irp
*async
= user
;
4878 if (*status
== STATUS_ALERTED
)
4880 SERVER_START_REQ( get_async_result
)
4882 req
->user_arg
= wine_server_client_ptr( async
);
4883 wine_server_set_reply( req
, async
->buffer
, async
->size
);
4884 *status
= virtual_locked_server_call( req
);
4888 release_fileio( &async
->io
);
4892 static BOOL
async_read_proc( void *user
, ULONG_PTR
*info
, NTSTATUS
*status
)
4894 struct async_fileio_read
*fileio
= user
;
4895 int fd
, needs_close
, result
;
4899 case STATUS_ALERTED
: /* got some new data */
4900 /* check to see if the data is ready (non-blocking) */
4901 if ((*status
= server_get_unix_fd( fileio
->io
.handle
, FILE_READ_DATA
, &fd
,
4902 &needs_close
, NULL
, NULL
)))
4905 result
= virtual_locked_read(fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
-fileio
->already
);
4906 if (needs_close
) close( fd
);
4910 if (errno
== EAGAIN
|| errno
== EINTR
)
4913 /* check to see if the transfer is complete */
4914 *status
= errno_to_status( errno
);
4916 else if (result
== 0)
4918 *status
= fileio
->already
? STATUS_SUCCESS
: STATUS_PIPE_BROKEN
;
4922 fileio
->already
+= result
;
4924 if (fileio
->already
< fileio
->count
&& !fileio
->avail_mode
)
4927 *status
= STATUS_SUCCESS
;
4931 case STATUS_TIMEOUT
:
4932 case STATUS_IO_TIMEOUT
:
4933 if (fileio
->already
) *status
= STATUS_SUCCESS
;
4937 *info
= fileio
->already
;
4938 release_fileio( &fileio
->io
);
4942 static BOOL
async_write_proc( void *user
, ULONG_PTR
*info
, NTSTATUS
*status
)
4944 struct async_fileio_write
*fileio
= user
;
4945 int result
, fd
, needs_close
;
4946 enum server_fd_type type
;
4950 case STATUS_ALERTED
:
4951 /* write some data (non-blocking) */
4952 if ((*status
= server_get_unix_fd( fileio
->io
.handle
, FILE_WRITE_DATA
, &fd
,
4953 &needs_close
, &type
, NULL
)))
4956 if (!fileio
->count
&& type
== FD_TYPE_MAILSLOT
)
4957 result
= send( fd
, fileio
->buffer
, 0, 0 );
4959 result
= write( fd
, &fileio
->buffer
[fileio
->already
], fileio
->count
- fileio
->already
);
4961 if (needs_close
) close( fd
);
4965 if (errno
== EAGAIN
|| errno
== EINTR
) return FALSE
;
4966 *status
= errno_to_status( errno
);
4970 fileio
->already
+= result
;
4971 if (fileio
->already
< fileio
->count
) return FALSE
;
4972 *status
= STATUS_SUCCESS
;
4976 case STATUS_TIMEOUT
:
4977 case STATUS_IO_TIMEOUT
:
4978 if (fileio
->already
) *status
= STATUS_SUCCESS
;
4982 *info
= fileio
->already
;
4983 release_fileio( &fileio
->io
);
4987 /* do a read call through the server */
4988 static NTSTATUS
server_read_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
4989 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG size
,
4990 LARGE_INTEGER
*offset
, ULONG
*key
)
4992 struct async_irp
*async
;
4997 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
4998 return STATUS_NO_MEMORY
;
5000 async
->buffer
= buffer
;
5003 SERVER_START_REQ( read
)
5005 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, iosb_client_ptr(io
) );
5006 req
->pos
= offset
? offset
->QuadPart
: 0;
5007 wine_server_set_reply( req
, buffer
, size
);
5008 status
= virtual_locked_server_call( req
);
5009 wait_handle
= wine_server_ptr_handle( reply
->wait
);
5010 options
= reply
->options
;
5011 if (wait_handle
&& status
!= STATUS_PENDING
)
5013 io
->u
.Status
= status
;
5014 io
->Information
= wine_server_reply_size( reply
);
5019 if (status
!= STATUS_PENDING
) free( async
);
5021 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
5025 /* do a write call through the server */
5026 static NTSTATUS
server_write_file( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5027 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG size
,
5028 LARGE_INTEGER
*offset
, ULONG
*key
)
5030 struct async_irp
*async
;
5035 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
5036 return STATUS_NO_MEMORY
;
5038 async
->buffer
= NULL
;
5041 SERVER_START_REQ( write
)
5043 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, iosb_client_ptr(io
) );
5044 req
->pos
= offset
? offset
->QuadPart
: 0;
5045 wine_server_add_data( req
, buffer
, size
);
5046 status
= wine_server_call( req
);
5047 wait_handle
= wine_server_ptr_handle( reply
->wait
);
5048 options
= reply
->options
;
5049 if (wait_handle
&& status
!= STATUS_PENDING
)
5051 io
->u
.Status
= status
;
5052 io
->Information
= reply
->size
;
5057 if (status
!= STATUS_PENDING
) free( async
);
5059 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
5063 /* do an ioctl call through the server */
5064 static NTSTATUS
server_ioctl_file( HANDLE handle
, HANDLE event
,
5065 PIO_APC_ROUTINE apc
, PVOID apc_context
,
5066 IO_STATUS_BLOCK
*io
, ULONG code
,
5067 const void *in_buffer
, ULONG in_size
,
5068 PVOID out_buffer
, ULONG out_size
)
5070 struct async_irp
*async
;
5075 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
5076 return STATUS_NO_MEMORY
;
5077 async
->buffer
= out_buffer
;
5078 async
->size
= out_size
;
5080 SERVER_START_REQ( ioctl
)
5083 req
->async
= server_async( handle
, &async
->io
, event
, apc
, apc_context
, iosb_client_ptr(io
) );
5084 wine_server_add_data( req
, in_buffer
, in_size
);
5085 if ((code
& 3) != METHOD_BUFFERED
) wine_server_add_data( req
, out_buffer
, out_size
);
5086 wine_server_set_reply( req
, out_buffer
, out_size
);
5087 status
= virtual_locked_server_call( req
);
5088 wait_handle
= wine_server_ptr_handle( reply
->wait
);
5089 options
= reply
->options
;
5090 if (wait_handle
&& status
!= STATUS_PENDING
)
5092 io
->u
.Status
= status
;
5093 io
->Information
= wine_server_reply_size( reply
);
5098 if (status
== STATUS_NOT_SUPPORTED
)
5099 WARN("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
5100 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
5102 if (status
!= STATUS_PENDING
) free( async
);
5104 if (wait_handle
) status
= wait_async( wait_handle
, (options
& FILE_SYNCHRONOUS_IO_ALERT
) );
5111 int interval
; /* max interval between two bytes */
5112 int total
; /* total timeout for the whole operation */
5113 int end_time
; /* absolute time of end of operation */
5116 /* retrieve the I/O timeouts to use for a given handle */
5117 static NTSTATUS
get_io_timeouts( HANDLE handle
, enum server_fd_type type
, ULONG count
, BOOL is_read
,
5118 struct io_timeouts
*timeouts
)
5120 NTSTATUS status
= STATUS_SUCCESS
;
5122 timeouts
->interval
= timeouts
->total
= -1;
5126 case FD_TYPE_SERIAL
:
5128 /* GetCommTimeouts */
5130 IO_STATUS_BLOCK io
= {0};
5132 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
5133 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
5138 if (st
.ReadIntervalTimeout
)
5139 timeouts
->interval
= st
.ReadIntervalTimeout
;
5141 if (st
.ReadTotalTimeoutMultiplier
|| st
.ReadTotalTimeoutConstant
)
5143 timeouts
->total
= st
.ReadTotalTimeoutConstant
;
5144 if (st
.ReadTotalTimeoutMultiplier
!= MAXDWORD
)
5145 timeouts
->total
+= count
* st
.ReadTotalTimeoutMultiplier
;
5147 else if (st
.ReadIntervalTimeout
== MAXDWORD
)
5148 timeouts
->interval
= timeouts
->total
= 0;
5152 if (st
.WriteTotalTimeoutMultiplier
|| st
.WriteTotalTimeoutConstant
)
5154 timeouts
->total
= st
.WriteTotalTimeoutConstant
;
5155 if (st
.WriteTotalTimeoutMultiplier
!= MAXDWORD
)
5156 timeouts
->total
+= count
* st
.WriteTotalTimeoutMultiplier
;
5161 case FD_TYPE_MAILSLOT
:
5164 timeouts
->interval
= 0; /* return as soon as we got something */
5165 SERVER_START_REQ( set_mailslot_info
)
5167 req
->handle
= wine_server_obj_handle( handle
);
5169 if (!(status
= wine_server_call( req
)) &&
5170 reply
->read_timeout
!= TIMEOUT_INFINITE
)
5171 timeouts
->total
= reply
->read_timeout
/ -10000;
5176 case FD_TYPE_SOCKET
:
5178 if (is_read
) timeouts
->interval
= 0; /* return as soon as we got something */
5183 if (timeouts
->total
!= -1) timeouts
->end_time
= NtGetTickCount() + timeouts
->total
;
5184 return STATUS_SUCCESS
;
5188 /* retrieve the timeout for the next wait, in milliseconds */
5189 static inline int get_next_io_timeout( const struct io_timeouts
*timeouts
, ULONG already
)
5193 if (timeouts
->total
!= -1)
5195 ret
= timeouts
->end_time
- NtGetTickCount();
5196 if (ret
< 0) ret
= 0;
5198 if (already
&& timeouts
->interval
!= -1)
5200 if (ret
== -1 || ret
> timeouts
->interval
) ret
= timeouts
->interval
;
5206 /* retrieve the avail_mode flag for async reads */
5207 static NTSTATUS
get_io_avail_mode( HANDLE handle
, enum server_fd_type type
, BOOL
*avail_mode
)
5209 NTSTATUS status
= STATUS_SUCCESS
;
5213 case FD_TYPE_SERIAL
:
5215 /* GetCommTimeouts */
5217 IO_STATUS_BLOCK io
= {0};
5219 status
= NtDeviceIoControlFile( handle
, NULL
, NULL
, NULL
, &io
,
5220 IOCTL_SERIAL_GET_TIMEOUTS
, NULL
, 0, &st
, sizeof(st
) );
5222 *avail_mode
= (!st
.ReadTotalTimeoutMultiplier
&&
5223 !st
.ReadTotalTimeoutConstant
&&
5224 st
.ReadIntervalTimeout
== MAXDWORD
);
5227 case FD_TYPE_MAILSLOT
:
5228 case FD_TYPE_SOCKET
:
5233 *avail_mode
= FALSE
;
5239 /* register an async I/O for a file read; helper for NtReadFile */
5240 static NTSTATUS
register_async_file_read( HANDLE handle
, HANDLE event
,
5241 PIO_APC_ROUTINE apc
, void *apc_user
,
5242 client_ptr_t iosb
, void *buffer
,
5243 ULONG already
, ULONG length
, BOOL avail_mode
)
5245 struct async_fileio_read
*fileio
;
5248 if (!(fileio
= (struct async_fileio_read
*)alloc_fileio( sizeof(*fileio
), async_read_proc
, handle
)))
5249 return STATUS_NO_MEMORY
;
5251 fileio
->already
= already
;
5252 fileio
->count
= length
;
5253 fileio
->buffer
= buffer
;
5254 fileio
->avail_mode
= avail_mode
;
5256 SERVER_START_REQ( register_async
)
5258 req
->type
= ASYNC_TYPE_READ
;
5259 req
->count
= length
;
5260 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, iosb
);
5261 status
= wine_server_call( req
);
5265 if (status
!= STATUS_PENDING
) free( fileio
);
5269 void add_completion( HANDLE handle
, ULONG_PTR value
, NTSTATUS status
, ULONG info
, BOOL async
)
5271 SERVER_START_REQ( add_fd_completion
)
5273 req
->handle
= wine_server_obj_handle( handle
);
5274 req
->cvalue
= value
;
5275 req
->status
= status
;
5276 req
->information
= info
;
5278 wine_server_call( req
);
5283 static NTSTATUS
set_pending_write( HANDLE device
)
5287 SERVER_START_REQ( set_serial_info
)
5289 req
->handle
= wine_server_obj_handle( device
);
5290 req
->flags
= SERIALINFO_PENDING_WRITE
;
5291 status
= wine_server_call( req
);
5298 /******************************************************************************
5299 * NtReadFile (NTDLL.@)
5301 NTSTATUS WINAPI
NtReadFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5302 IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
5303 LARGE_INTEGER
*offset
, ULONG
*key
)
5305 int result
, unix_handle
, needs_close
;
5306 unsigned int options
;
5307 struct io_timeouts timeouts
;
5308 NTSTATUS status
, ret_status
;
5310 client_ptr_t iosb_ptr
= iosb_client_ptr(io
);
5311 enum server_fd_type type
;
5312 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5313 BOOL send_completion
= FALSE
, async_read
, timeout_init_done
= FALSE
;
5315 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5316 handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5318 if (!io
) return STATUS_ACCESS_VIOLATION
;
5320 status
= server_get_unix_fd( handle
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5321 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5323 if (!virtual_check_buffer_for_write( buffer
, length
)) return STATUS_ACCESS_VIOLATION
;
5325 if (status
== STATUS_BAD_DEVICE_TYPE
)
5326 return server_read_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5328 async_read
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5330 if (type
== FD_TYPE_FILE
)
5332 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5334 status
= STATUS_INVALID_PARAMETER
;
5338 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5340 /* async I/O doesn't make sense on regular files */
5341 while ((result
= virtual_locked_pread( unix_handle
, buffer
, length
, offset
->QuadPart
)) == -1)
5345 status
= errno_to_status( errno
);
5349 if (!async_read
) /* update file pointer position */
5350 lseek( unix_handle
, offset
->QuadPart
+ result
, SEEK_SET
);
5353 status
= (total
|| !length
) ? STATUS_SUCCESS
: STATUS_END_OF_FILE
;
5357 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5359 if (async_read
&& (!offset
|| offset
->QuadPart
< 0))
5361 status
= STATUS_INVALID_PARAMETER
;
5365 else if (type
== FD_TYPE_SOCKET
)
5367 status
= sock_read( handle
, unix_handle
, event
, apc
, apc_user
, io
, buffer
, length
);
5368 if (needs_close
) close( unix_handle
);
5372 if (type
== FD_TYPE_SERIAL
&& async_read
&& length
)
5374 /* an asynchronous serial port read with a read interval timeout needs to
5375 skip the synchronous read to make sure that the server starts the read
5376 interval timer after the first read */
5377 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5378 if (timeouts
.interval
)
5380 status
= register_async_file_read( handle
, event
, apc
, apc_user
, iosb_ptr
,
5381 buffer
, total
, length
, FALSE
);
5388 if ((result
= virtual_locked_read( unix_handle
, (char *)buffer
+ total
, length
- total
)) >= 0)
5391 if (!result
|| total
== length
)
5395 status
= STATUS_SUCCESS
;
5402 case FD_TYPE_DEVICE
:
5403 status
= length
? STATUS_END_OF_FILE
: STATUS_SUCCESS
;
5405 case FD_TYPE_SERIAL
:
5408 status
= STATUS_SUCCESS
;
5413 status
= STATUS_PIPE_BROKEN
;
5417 else if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5419 else if (errno
!= EAGAIN
)
5421 if (errno
== EINTR
) continue;
5422 if (!total
) status
= errno_to_status( errno
);
5430 if ((status
= get_io_avail_mode( handle
, type
, &avail_mode
))) goto err
;
5431 if (total
&& avail_mode
)
5433 status
= STATUS_SUCCESS
;
5436 status
= register_async_file_read( handle
, event
, apc
, apc_user
, iosb_ptr
,
5437 buffer
, total
, length
, avail_mode
);
5440 else /* synchronous read, wait for the fd to become ready */
5445 if (!timeout_init_done
)
5447 timeout_init_done
= TRUE
;
5448 if ((status
= get_io_timeouts( handle
, type
, length
, TRUE
, &timeouts
))) goto err
;
5449 if (event
) NtResetEvent( event
, NULL
);
5451 timeout
= get_next_io_timeout( &timeouts
, total
);
5453 pfd
.fd
= unix_handle
;
5454 pfd
.events
= POLLIN
;
5456 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
5458 if (total
) /* return with what we got so far */
5459 status
= STATUS_SUCCESS
;
5461 status
= (type
== FD_TYPE_MAILSLOT
) ? STATUS_IO_TIMEOUT
: STATUS_TIMEOUT
;
5464 if (ret
== -1 && errno
!= EINTR
)
5466 status
= errno_to_status( errno
);
5469 /* will now restart the read */
5474 send_completion
= cvalue
!= 0;
5477 if (needs_close
) close( unix_handle
);
5478 if (status
== STATUS_SUCCESS
|| (status
== STATUS_END_OF_FILE
&& (!async_read
|| type
== FD_TYPE_FILE
)))
5480 io
->u
.Status
= status
;
5481 io
->Information
= total
;
5482 TRACE("= SUCCESS (%u)\n", total
);
5483 if (event
) NtSetEvent( event
, NULL
);
5484 if (apc
&& (!status
|| async_read
)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
,
5485 (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
5489 TRACE("= 0x%08x\n", status
);
5490 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5493 ret_status
= async_read
&& type
== FD_TYPE_FILE
&& (status
== STATUS_SUCCESS
|| status
== STATUS_END_OF_FILE
)
5494 ? STATUS_PENDING
: status
;
5496 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
5501 /******************************************************************************
5502 * NtReadFileScatter (NTDLL.@)
5504 NTSTATUS WINAPI
NtReadFileScatter( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5505 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
5506 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
5508 int result
, unix_handle
, needs_close
;
5509 unsigned int options
;
5511 ULONG pos
= 0, total
= 0;
5512 client_ptr_t iosb_ptr
= iosb_client_ptr(io
);
5513 enum server_fd_type type
;
5514 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5515 BOOL send_completion
= FALSE
;
5517 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5518 file
, event
, apc
, apc_user
, io
, segments
, length
, offset
, key
);
5520 if (!io
) return STATUS_ACCESS_VIOLATION
;
5522 status
= server_get_unix_fd( file
, FILE_READ_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5523 if (status
) return status
;
5525 if ((type
!= FD_TYPE_FILE
) ||
5526 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
5527 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
5529 status
= STATUS_INVALID_PARAMETER
;
5535 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5536 result
= pread( unix_handle
, (char *)segments
->Buffer
+ pos
,
5537 min( length
- pos
, page_size
- pos
), offset
->QuadPart
+ total
);
5539 result
= read( unix_handle
, (char *)segments
->Buffer
+ pos
, min( length
- pos
, page_size
- pos
) );
5543 if (errno
== EINTR
) continue;
5544 status
= errno_to_status( errno
);
5550 if ((pos
+= result
) == page_size
)
5557 if (total
== 0) status
= STATUS_END_OF_FILE
;
5559 send_completion
= cvalue
!= 0;
5561 if (needs_close
) close( unix_handle
);
5562 io
->u
.Status
= status
;
5563 io
->Information
= total
;
5564 TRACE("= 0x%08x (%u)\n", status
, total
);
5565 if (event
) NtSetEvent( event
, NULL
);
5566 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
, (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
5567 if (send_completion
) add_completion( file
, cvalue
, status
, total
, TRUE
);
5569 return STATUS_PENDING
;
5572 if (needs_close
) close( unix_handle
);
5573 if (event
) NtResetEvent( event
, NULL
);
5574 TRACE("= 0x%08x\n", status
);
5579 /******************************************************************************
5580 * NtWriteFile (NTDLL.@)
5582 NTSTATUS WINAPI
NtWriteFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5583 IO_STATUS_BLOCK
*io
, const void *buffer
, ULONG length
,
5584 LARGE_INTEGER
*offset
, ULONG
*key
)
5586 int result
, unix_handle
, needs_close
;
5587 unsigned int options
;
5588 struct io_timeouts timeouts
;
5589 NTSTATUS status
, ret_status
;
5591 client_ptr_t iosb_ptr
= iosb_client_ptr(io
);
5592 enum server_fd_type type
;
5593 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5594 BOOL send_completion
= FALSE
, async_write
, append_write
= FALSE
, timeout_init_done
= FALSE
;
5595 LARGE_INTEGER offset_eof
;
5597 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n",
5598 handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5600 if (!io
) return STATUS_ACCESS_VIOLATION
;
5602 status
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5603 if (status
== STATUS_ACCESS_DENIED
)
5605 status
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &unix_handle
,
5606 &needs_close
, &type
, &options
);
5607 append_write
= TRUE
;
5609 if (status
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5611 async_write
= !(options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
));
5613 if (!virtual_check_buffer_for_read( buffer
, length
))
5615 status
= STATUS_INVALID_USER_BUFFER
;
5619 if (status
== STATUS_BAD_DEVICE_TYPE
)
5620 return server_write_file( handle
, event
, apc
, apc_user
, io
, buffer
, length
, offset
, key
);
5622 if (type
== FD_TYPE_FILE
)
5625 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5627 status
= STATUS_INVALID_PARAMETER
;
5633 offset_eof
.QuadPart
= FILE_WRITE_TO_END_OF_FILE
;
5634 offset
= &offset_eof
;
5637 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5639 off_t off
= offset
->QuadPart
;
5641 if (offset
->QuadPart
== FILE_WRITE_TO_END_OF_FILE
)
5645 if (fstat( unix_handle
, &st
) == -1)
5647 status
= errno_to_status( errno
);
5652 else if (offset
->QuadPart
< 0)
5654 status
= STATUS_INVALID_PARAMETER
;
5658 /* async I/O doesn't make sense on regular files */
5659 while ((result
= pwrite( unix_handle
, buffer
, length
, off
)) == -1)
5663 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5664 else status
= errno_to_status( errno
);
5669 if (!async_write
) /* update file pointer position */
5670 lseek( unix_handle
, off
+ result
, SEEK_SET
);
5673 status
= STATUS_SUCCESS
;
5677 else if (type
== FD_TYPE_SERIAL
|| type
== FD_TYPE_DEVICE
)
5680 (!offset
|| (offset
->QuadPart
< 0 && offset
->QuadPart
!= FILE_WRITE_TO_END_OF_FILE
)))
5682 status
= STATUS_INVALID_PARAMETER
;
5686 else if (type
== FD_TYPE_SOCKET
)
5688 status
= sock_write( handle
, unix_handle
, event
, apc
, apc_user
, io
, buffer
, length
);
5689 if (needs_close
) close( unix_handle
);
5695 /* zero-length writes on sockets may not work with plain write(2) */
5696 if (!length
&& type
== FD_TYPE_MAILSLOT
)
5697 result
= send( unix_handle
, buffer
, 0, 0 );
5699 result
= write( unix_handle
, (const char *)buffer
+ total
, length
- total
);
5704 if (total
== length
)
5706 status
= STATUS_SUCCESS
;
5709 if (type
== FD_TYPE_FILE
) continue; /* no async I/O on regular files */
5711 else if (errno
!= EAGAIN
)
5713 if (errno
== EINTR
) continue;
5716 if (errno
== EFAULT
) status
= STATUS_INVALID_USER_BUFFER
;
5717 else status
= errno_to_status( errno
);
5724 struct async_fileio_write
*fileio
;
5726 fileio
= (struct async_fileio_write
*)alloc_fileio( sizeof(*fileio
), async_write_proc
, handle
);
5729 status
= STATUS_NO_MEMORY
;
5732 fileio
->already
= total
;
5733 fileio
->count
= length
;
5734 fileio
->buffer
= buffer
;
5736 SERVER_START_REQ( register_async
)
5738 req
->type
= ASYNC_TYPE_WRITE
;
5739 req
->count
= length
;
5740 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_user
, iosb_ptr
);
5741 status
= wine_server_call( req
);
5745 if (status
!= STATUS_PENDING
) free( fileio
);
5748 else /* synchronous write, wait for the fd to become ready */
5753 if (!timeout_init_done
)
5755 timeout_init_done
= TRUE
;
5756 if ((status
= get_io_timeouts( handle
, type
, length
, FALSE
, &timeouts
)))
5758 if (event
) NtResetEvent( event
, NULL
);
5760 timeout
= get_next_io_timeout( &timeouts
, total
);
5762 pfd
.fd
= unix_handle
;
5763 pfd
.events
= POLLOUT
;
5765 if (!timeout
|| !(ret
= poll( &pfd
, 1, timeout
)))
5767 /* return with what we got so far */
5768 status
= total
? STATUS_SUCCESS
: STATUS_TIMEOUT
;
5771 if (ret
== -1 && errno
!= EINTR
)
5773 status
= errno_to_status( errno
);
5776 /* will now restart the write */
5781 send_completion
= cvalue
!= 0;
5784 if (needs_close
) close( unix_handle
);
5786 if (type
== FD_TYPE_SERIAL
&& (status
== STATUS_SUCCESS
|| status
== STATUS_PENDING
))
5787 set_pending_write( handle
);
5789 if (status
== STATUS_SUCCESS
)
5791 io
->u
.Status
= status
;
5792 io
->Information
= total
;
5793 TRACE("= SUCCESS (%u)\n", total
);
5794 if (event
) NtSetEvent( event
, NULL
);
5795 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
, (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
5799 TRACE("= 0x%08x\n", status
);
5800 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5803 ret_status
= async_write
&& type
== FD_TYPE_FILE
&& status
== STATUS_SUCCESS
? STATUS_PENDING
: status
;
5804 if (send_completion
) add_completion( handle
, cvalue
, status
, total
, ret_status
== STATUS_PENDING
);
5809 /******************************************************************************
5810 * NtWriteFileGather (NTDLL.@)
5812 NTSTATUS WINAPI
NtWriteFileGather( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_user
,
5813 IO_STATUS_BLOCK
*io
, FILE_SEGMENT_ELEMENT
*segments
,
5814 ULONG length
, LARGE_INTEGER
*offset
, ULONG
*key
)
5816 int result
, unix_handle
, needs_close
;
5817 unsigned int options
;
5819 ULONG pos
= 0, total
= 0;
5820 client_ptr_t iosb_ptr
= iosb_client_ptr(io
);
5821 enum server_fd_type type
;
5822 ULONG_PTR cvalue
= apc
? 0 : (ULONG_PTR
)apc_user
;
5823 BOOL send_completion
= FALSE
;
5825 TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n",
5826 file
, event
, apc
, apc_user
, io
, segments
, length
, offset
, key
);
5828 if (length
% page_size
) return STATUS_INVALID_PARAMETER
;
5829 if (!io
) return STATUS_ACCESS_VIOLATION
;
5831 status
= server_get_unix_fd( file
, FILE_WRITE_DATA
, &unix_handle
, &needs_close
, &type
, &options
);
5832 if (status
) return status
;
5834 if ((type
!= FD_TYPE_FILE
) ||
5835 (options
& (FILE_SYNCHRONOUS_IO_ALERT
| FILE_SYNCHRONOUS_IO_NONALERT
)) ||
5836 !(options
& FILE_NO_INTERMEDIATE_BUFFERING
))
5838 status
= STATUS_INVALID_PARAMETER
;
5844 if (offset
&& offset
->QuadPart
!= FILE_USE_FILE_POINTER_POSITION
)
5845 result
= pwrite( unix_handle
, (char *)segments
->Buffer
+ pos
,
5846 page_size
- pos
, offset
->QuadPart
+ total
);
5848 result
= write( unix_handle
, (char *)segments
->Buffer
+ pos
, page_size
- pos
);
5852 if (errno
== EINTR
) continue;
5853 if (errno
== EFAULT
)
5855 status
= STATUS_INVALID_USER_BUFFER
;
5858 status
= errno_to_status( errno
);
5863 status
= STATUS_DISK_FULL
;
5868 if ((pos
+= result
) == page_size
)
5875 send_completion
= cvalue
!= 0;
5878 if (needs_close
) close( unix_handle
);
5879 if (status
== STATUS_SUCCESS
)
5881 io
->u
.Status
= status
;
5882 io
->Information
= total
;
5883 TRACE("= SUCCESS (%u)\n", total
);
5884 if (event
) NtSetEvent( event
, NULL
);
5885 if (apc
) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC
)apc
, (ULONG_PTR
)apc_user
, iosb_ptr
, 0 );
5889 TRACE("= 0x%08x\n", status
);
5890 if (status
!= STATUS_PENDING
&& event
) NtResetEvent( event
, NULL
);
5892 if (send_completion
) add_completion( file
, cvalue
, status
, total
, FALSE
);
5897 /******************************************************************************
5898 * NtDeviceIoControlFile (NTDLL.@)
5900 NTSTATUS WINAPI
NtDeviceIoControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5901 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
5902 void *out_buffer
, ULONG out_size
)
5904 ULONG device
= (code
>> 16);
5905 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
5907 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5908 handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
5910 /* some broken applications call this frequently with INVALID_HANDLE_VALUE,
5911 * and run slowly if we make a server call every time */
5912 if (HandleToLong( handle
) == ~0)
5913 return STATUS_INVALID_HANDLE
;
5917 case FILE_DEVICE_BEEP
:
5918 case FILE_DEVICE_NETWORK
:
5919 status
= sock_ioctl( handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
5920 if (status
!= STATUS_NOT_SUPPORTED
&& status
!= STATUS_BAD_DEVICE_TYPE
) return status
;
5922 case FILE_DEVICE_DISK
:
5923 case FILE_DEVICE_CD_ROM
:
5924 case FILE_DEVICE_DVD
:
5925 case FILE_DEVICE_CONTROLLER
:
5926 case FILE_DEVICE_MASS_STORAGE
:
5927 status
= cdrom_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5928 in_buffer
, in_size
, out_buffer
, out_size
);
5930 case FILE_DEVICE_SERIAL_PORT
:
5931 status
= serial_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5932 in_buffer
, in_size
, out_buffer
, out_size
);
5934 case FILE_DEVICE_TAPE
:
5935 status
= tape_DeviceIoControl( handle
, event
, apc
, apc_context
, io
, code
,
5936 in_buffer
, in_size
, out_buffer
, out_size
);
5940 if (status
== STATUS_NOT_SUPPORTED
|| status
== STATUS_BAD_DEVICE_TYPE
)
5941 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5942 in_buffer
, in_size
, out_buffer
, out_size
);
5944 if (status
!= STATUS_PENDING
&& !NT_ERROR(status
)) io
->u
.Status
= status
;
5949 /* Tell Valgrind to ignore any holes in structs we will be passing to the
5951 static void ignore_server_ioctl_struct_holes( ULONG code
, const void *in_buffer
, ULONG in_size
)
5953 #ifdef VALGRIND_MAKE_MEM_DEFINED
5954 # define IGNORE_STRUCT_HOLE(buf, size, t, f1, f2) \
5956 if (FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1) < FIELD_OFFSET(t, f2)) \
5957 if ((size) >= FIELD_OFFSET(t, f2)) \
5958 VALGRIND_MAKE_MEM_DEFINED( \
5959 (const char *)(buf) + FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1), \
5960 FIELD_OFFSET(t, f2) - FIELD_OFFSET(t, f1) + sizeof(((t *)0)->f1)); \
5965 case FSCTL_PIPE_WAIT
:
5966 IGNORE_STRUCT_HOLE(in_buffer
, in_size
, FILE_PIPE_WAIT_FOR_BUFFER
, TimeoutSpecified
, Name
);
5973 /******************************************************************************
5974 * NtFsControlFile (NTDLL.@)
5976 NTSTATUS WINAPI
NtFsControlFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
, void *apc_context
,
5977 IO_STATUS_BLOCK
*io
, ULONG code
, void *in_buffer
, ULONG in_size
,
5978 void *out_buffer
, ULONG out_size
)
5982 TRACE( "(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
5983 handle
, event
, apc
, apc_context
, io
, code
, in_buffer
, in_size
, out_buffer
, out_size
);
5985 if (!io
) return STATUS_INVALID_PARAMETER
;
5987 ignore_server_ioctl_struct_holes( code
, in_buffer
, in_size
);
5991 case FSCTL_DISMOUNT_VOLUME
:
5992 status
= server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
5993 in_buffer
, in_size
, out_buffer
, out_size
);
5994 if (!status
) status
= unmount_device( handle
);
5997 case FSCTL_PIPE_IMPERSONATE
:
5998 FIXME("FSCTL_PIPE_IMPERSONATE: impersonating self\n");
5999 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
6000 in_buffer
, in_size
, out_buffer
, out_size
);
6002 case FSCTL_IS_VOLUME_MOUNTED
:
6003 case FSCTL_LOCK_VOLUME
:
6004 case FSCTL_UNLOCK_VOLUME
:
6005 FIXME("stub! return success - Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
6006 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
6007 status
= STATUS_SUCCESS
;
6010 case FSCTL_GET_RETRIEVAL_POINTERS
:
6012 RETRIEVAL_POINTERS_BUFFER
*buffer
= (RETRIEVAL_POINTERS_BUFFER
*)out_buffer
;
6014 FIXME("stub: FSCTL_GET_RETRIEVAL_POINTERS\n");
6016 if (out_size
>= sizeof(RETRIEVAL_POINTERS_BUFFER
))
6018 buffer
->ExtentCount
= 1;
6019 buffer
->StartingVcn
.QuadPart
= 1;
6020 buffer
->Extents
[0].NextVcn
.QuadPart
= 0;
6021 buffer
->Extents
[0].Lcn
.QuadPart
= 0;
6022 io
->Information
= sizeof(RETRIEVAL_POINTERS_BUFFER
);
6023 status
= STATUS_SUCCESS
;
6027 io
->Information
= 0;
6028 status
= STATUS_BUFFER_TOO_SMALL
;
6033 case FSCTL_GET_OBJECT_ID
:
6035 FILE_OBJECTID_BUFFER
*info
= out_buffer
;
6036 int fd
, needs_close
;
6039 io
->Information
= 0;
6040 if (out_size
>= sizeof(*info
))
6042 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
6045 if (needs_close
) close( fd
);
6046 memset( info
, 0, sizeof(*info
) );
6047 memcpy( info
->ObjectId
, &st
.st_dev
, sizeof(st
.st_dev
) );
6048 memcpy( info
->ObjectId
+ 8, &st
.st_ino
, sizeof(st
.st_ino
) );
6049 io
->Information
= sizeof(*info
);
6051 else status
= STATUS_BUFFER_TOO_SMALL
;
6055 case FSCTL_SET_SPARSE
:
6056 TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
6057 io
->Information
= 0;
6058 status
= STATUS_SUCCESS
;
6061 return server_ioctl_file( handle
, event
, apc
, apc_context
, io
, code
,
6062 in_buffer
, in_size
, out_buffer
, out_size
);
6065 if (status
!= STATUS_PENDING
) io
->u
.Status
= status
;
6070 /******************************************************************************
6071 * NtFlushBuffersFile (NTDLL.@)
6073 NTSTATUS WINAPI
NtFlushBuffersFile( HANDLE handle
, IO_STATUS_BLOCK
*io
)
6077 enum server_fd_type type
;
6078 int fd
, needs_close
;
6080 if (!io
|| !virtual_check_buffer_for_write( io
, sizeof(*io
) )) return STATUS_ACCESS_VIOLATION
;
6082 ret
= server_get_unix_fd( handle
, FILE_WRITE_DATA
, &fd
, &needs_close
, &type
, NULL
);
6083 if (ret
== STATUS_ACCESS_DENIED
)
6084 ret
= server_get_unix_fd( handle
, FILE_APPEND_DATA
, &fd
, &needs_close
, &type
, NULL
);
6086 if (!ret
&& (type
== FD_TYPE_FILE
|| type
== FD_TYPE_DIR
|| type
== FD_TYPE_CHAR
))
6088 if (fsync(fd
)) ret
= errno_to_status( errno
);
6090 io
->Information
= 0;
6092 else if (!ret
&& type
== FD_TYPE_SERIAL
)
6094 ret
= serial_FlushBuffersFile( fd
);
6096 else if (ret
!= STATUS_ACCESS_DENIED
)
6098 struct async_irp
*async
;
6100 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
6101 return STATUS_NO_MEMORY
;
6102 async
->buffer
= NULL
;
6105 SERVER_START_REQ( flush
)
6107 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, iosb_client_ptr(io
) );
6108 ret
= wine_server_call( req
);
6109 wait_handle
= wine_server_ptr_handle( reply
->event
);
6110 if (wait_handle
&& ret
!= STATUS_PENDING
)
6113 io
->Information
= 0;
6118 if (ret
!= STATUS_PENDING
) free( async
);
6120 if (wait_handle
) ret
= wait_async( wait_handle
, FALSE
);
6123 if (needs_close
) close( fd
);
6128 /**************************************************************************
6129 * NtCancelIoFile (NTDLL.@)
6131 NTSTATUS WINAPI
NtCancelIoFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
)
6135 TRACE( "%p %p\n", handle
, io_status
);
6137 SERVER_START_REQ( cancel_async
)
6139 req
->handle
= wine_server_obj_handle( handle
);
6140 req
->only_thread
= TRUE
;
6141 if (!(status
= wine_server_call( req
)))
6143 io_status
->u
.Status
= status
;
6144 io_status
->Information
= 0;
6153 /**************************************************************************
6154 * NtCancelIoFileEx (NTDLL.@)
6156 NTSTATUS WINAPI
NtCancelIoFileEx( HANDLE handle
, IO_STATUS_BLOCK
*io
, IO_STATUS_BLOCK
*io_status
)
6160 TRACE( "%p %p %p\n", handle
, io
, io_status
);
6162 SERVER_START_REQ( cancel_async
)
6164 req
->handle
= wine_server_obj_handle( handle
);
6165 req
->iosb
= wine_server_client_ptr( io
);
6166 if (!(status
= wine_server_call( req
)))
6168 io_status
->u
.Status
= status
;
6169 io_status
->Information
= 0;
6178 /******************************************************************
6179 * NtLockFile (NTDLL.@)
6181 NTSTATUS WINAPI
NtLockFile( HANDLE file
, HANDLE event
, PIO_APC_ROUTINE apc
, void* apc_user
,
6182 IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
6183 LARGE_INTEGER
*count
, ULONG
*key
, BOOLEAN dont_wait
, BOOLEAN exclusive
)
6190 if (apc
|| io_status
|| key
)
6192 FIXME("Unimplemented yet parameter\n");
6193 return STATUS_NOT_IMPLEMENTED
;
6195 if (apc_user
&& !warn
++) FIXME("I/O completion on lock not implemented yet\n");
6199 SERVER_START_REQ( lock_file
)
6201 req
->handle
= wine_server_obj_handle( file
);
6202 req
->offset
= offset
->QuadPart
;
6203 req
->count
= count
->QuadPart
;
6204 req
->shared
= !exclusive
;
6205 req
->wait
= !dont_wait
;
6206 ret
= wine_server_call( req
);
6207 handle
= wine_server_ptr_handle( reply
->handle
);
6208 async
= reply
->overlapped
;
6211 if (ret
!= STATUS_PENDING
)
6213 if (!ret
&& event
) NtSetEvent( event
, NULL
);
6218 FIXME( "Async I/O lock wait not implemented, might deadlock\n" );
6219 if (handle
) NtClose( handle
);
6220 return STATUS_PENDING
;
6224 NtWaitForSingleObject( handle
, FALSE
, NULL
);
6227 else /* Unix lock conflict, sleep a bit and retry */
6230 time
.QuadPart
= -100 * (ULONGLONG
)10000;
6231 NtDelayExecution( FALSE
, &time
);
6237 /******************************************************************
6238 * NtUnlockFile (NTDLL.@)
6240 NTSTATUS WINAPI
NtUnlockFile( HANDLE handle
, IO_STATUS_BLOCK
*io_status
, LARGE_INTEGER
*offset
,
6241 LARGE_INTEGER
*count
, ULONG
*key
)
6245 TRACE( "%p %x%08x %x%08x\n",
6246 handle
, offset
->u
.HighPart
, offset
->u
.LowPart
, count
->u
.HighPart
, count
->u
.LowPart
);
6248 if (io_status
|| key
)
6250 FIXME("Unimplemented yet parameter\n");
6251 return STATUS_NOT_IMPLEMENTED
;
6254 SERVER_START_REQ( unlock_file
)
6256 req
->handle
= wine_server_obj_handle( handle
);
6257 req
->offset
= offset
->QuadPart
;
6258 req
->count
= count
->QuadPart
;
6259 status
= wine_server_call( req
);
6266 static BOOL
read_changes_apc( void *user
, ULONG_PTR
*info
, NTSTATUS
*status
)
6268 struct async_fileio_read_changes
*fileio
= user
;
6271 if (*status
== STATUS_ALERTED
)
6273 SERVER_START_REQ( read_change
)
6275 req
->handle
= wine_server_obj_handle( fileio
->io
.handle
);
6276 wine_server_set_reply( req
, fileio
->data
, fileio
->data_size
);
6277 *status
= wine_server_call( req
);
6278 size
= wine_server_reply_size( reply
);
6282 if (*status
== STATUS_SUCCESS
&& fileio
->buffer
)
6284 FILE_NOTIFY_INFORMATION
*pfni
= fileio
->buffer
;
6285 int i
, left
= fileio
->buffer_size
;
6286 DWORD
*last_entry_offset
= NULL
;
6287 struct filesystem_event
*event
= (struct filesystem_event
*)fileio
->data
;
6289 while (size
&& left
>= sizeof(*pfni
))
6291 DWORD len
= (left
- offsetof(FILE_NOTIFY_INFORMATION
, FileName
)) / sizeof(WCHAR
);
6293 /* convert to an NT style path */
6294 for (i
= 0; i
< event
->len
; i
++)
6295 if (event
->name
[i
] == '/') event
->name
[i
] = '\\';
6297 pfni
->Action
= event
->action
;
6298 pfni
->FileNameLength
= ntdll_umbstowcs( event
->name
, event
->len
, pfni
->FileName
, len
);
6299 last_entry_offset
= &pfni
->NextEntryOffset
;
6301 if (pfni
->FileNameLength
== len
) break;
6303 i
= offsetof(FILE_NOTIFY_INFORMATION
, FileName
[pfni
->FileNameLength
]);
6304 pfni
->FileNameLength
*= sizeof(WCHAR
);
6305 pfni
->NextEntryOffset
= i
;
6306 pfni
= (FILE_NOTIFY_INFORMATION
*)((char*)pfni
+ i
);
6309 i
= (offsetof(struct filesystem_event
, name
[event
->len
])
6310 + sizeof(int)-1) / sizeof(int) * sizeof(int);
6311 event
= (struct filesystem_event
*)((char*)event
+ i
);
6317 *status
= STATUS_NOTIFY_ENUM_DIR
;
6322 if (last_entry_offset
) *last_entry_offset
= 0;
6323 size
= fileio
->buffer_size
- left
;
6328 *status
= STATUS_NOTIFY_ENUM_DIR
;
6334 release_fileio( &fileio
->io
);
6338 #define FILE_NOTIFY_ALL ( \
6339 FILE_NOTIFY_CHANGE_FILE_NAME | \
6340 FILE_NOTIFY_CHANGE_DIR_NAME | \
6341 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
6342 FILE_NOTIFY_CHANGE_SIZE | \
6343 FILE_NOTIFY_CHANGE_LAST_WRITE | \
6344 FILE_NOTIFY_CHANGE_LAST_ACCESS | \
6345 FILE_NOTIFY_CHANGE_CREATION | \
6346 FILE_NOTIFY_CHANGE_SECURITY )
6348 /******************************************************************************
6349 * NtNotifyChangeDirectoryFile (NTDLL.@)
6351 NTSTATUS WINAPI
NtNotifyChangeDirectoryFile( HANDLE handle
, HANDLE event
, PIO_APC_ROUTINE apc
,
6352 void *apc_context
, IO_STATUS_BLOCK
*iosb
, void *buffer
,
6353 ULONG buffer_size
, ULONG filter
, BOOLEAN subtree
)
6355 struct async_fileio_read_changes
*fileio
;
6357 ULONG size
= max( 4096, buffer_size
);
6359 TRACE( "%p %p %p %p %p %p %u %u %d\n",
6360 handle
, event
, apc
, apc_context
, iosb
, buffer
, buffer_size
, filter
, subtree
);
6362 if (!iosb
) return STATUS_ACCESS_VIOLATION
;
6363 if (filter
== 0 || (filter
& ~FILE_NOTIFY_ALL
)) return STATUS_INVALID_PARAMETER
;
6365 fileio
= (struct async_fileio_read_changes
*)alloc_fileio(
6366 offsetof(struct async_fileio_read_changes
, data
[size
]), read_changes_apc
, handle
);
6367 if (!fileio
) return STATUS_NO_MEMORY
;
6369 fileio
->buffer
= buffer
;
6370 fileio
->buffer_size
= buffer_size
;
6371 fileio
->data_size
= size
;
6373 SERVER_START_REQ( read_directory_changes
)
6375 req
->filter
= filter
;
6376 req
->want_data
= (buffer
!= NULL
);
6377 req
->subtree
= subtree
;
6378 req
->async
= server_async( handle
, &fileio
->io
, event
, apc
, apc_context
, iosb_client_ptr(iosb
) );
6379 status
= wine_server_call( req
);
6383 if (status
!= STATUS_PENDING
) free( fileio
);
6388 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6389 /* helper for FILE_GetDeviceInfo to hide some platform differences in fstatfs */
6390 static inline void get_device_info_fstatfs( FILE_FS_DEVICE_INFORMATION
*info
, const char *fstypename
,
6391 unsigned int flags
)
6393 if (!strcmp("cd9660", fstypename
) || !strcmp("udf", fstypename
))
6395 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6396 /* Don't assume read-only, let the mount options set it below */
6397 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6399 else if (!strcmp("nfs", fstypename
) || !strcmp("nwfs", fstypename
) ||
6400 !strcmp("smbfs", fstypename
) || !strcmp("afpfs", fstypename
))
6402 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6403 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6405 else if (!strcmp("procfs", fstypename
))
6406 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6408 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6410 if (flags
& MNT_RDONLY
)
6411 info
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
6413 if (!(flags
& MNT_LOCAL
))
6415 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6416 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6421 static inline BOOL
is_device_placeholder( int fd
)
6423 static const char wine_placeholder
[] = "Wine device placeholder";
6424 char buffer
[sizeof(wine_placeholder
)-1];
6426 if (pread( fd
, buffer
, sizeof(wine_placeholder
) - 1, 0 ) != sizeof(wine_placeholder
) - 1)
6428 return !memcmp( buffer
, wine_placeholder
, sizeof(wine_placeholder
) - 1 );
6431 NTSTATUS
get_device_info( int fd
, FILE_FS_DEVICE_INFORMATION
*info
)
6435 info
->Characteristics
= 0;
6436 if (fstat( fd
, &st
) < 0) return errno_to_status( errno
);
6437 if (S_ISCHR( st
.st_mode
))
6439 info
->DeviceType
= FILE_DEVICE_UNKNOWN
;
6441 switch(major(st
.st_rdev
))
6444 info
->DeviceType
= FILE_DEVICE_NULL
;
6447 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6450 info
->DeviceType
= FILE_DEVICE_PARALLEL_PORT
;
6452 case SCSI_TAPE_MAJOR
:
6453 info
->DeviceType
= FILE_DEVICE_TAPE
;
6456 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
6459 if (ioctl(fd
, FIODTYPE
, &d_type
) == 0)
6464 info
->DeviceType
= FILE_DEVICE_TAPE
;
6467 info
->DeviceType
= FILE_DEVICE_DISK
;
6470 info
->DeviceType
= FILE_DEVICE_SERIAL_PORT
;
6472 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
6474 info
->DeviceType
= FILE_DEVICE_NULL
;
6478 /* no special d_type for parallel ports */
6483 else if (S_ISBLK( st
.st_mode
))
6485 info
->DeviceType
= FILE_DEVICE_DISK
;
6487 else if (S_ISFIFO( st
.st_mode
) || S_ISSOCK( st
.st_mode
))
6489 info
->DeviceType
= FILE_DEVICE_NAMED_PIPE
;
6491 else if (is_device_placeholder( fd
))
6493 info
->DeviceType
= FILE_DEVICE_DISK
;
6495 else /* regular file or directory */
6497 #if defined(linux) && defined(HAVE_FSTATFS)
6500 /* check for floppy disk */
6501 if (major(st
.st_dev
) == FLOPPY_MAJOR
)
6502 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6504 if (fstatfs( fd
, &stfs
) < 0) stfs
.f_type
= 0;
6505 switch (stfs
.f_type
)
6507 case 0x9660: /* iso9660 */
6508 case 0x9fa1: /* supermount */
6509 case 0x15013346: /* udf */
6510 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6511 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6513 case 0x6969: /* nfs */
6514 case 0xff534d42: /* cifs */
6515 case 0xfe534d42: /* smb2 */
6516 case 0x517b: /* smbfs */
6517 case 0x564c: /* ncpfs */
6518 info
->DeviceType
= FILE_DEVICE_NETWORK_FILE_SYSTEM
;
6519 info
->Characteristics
|= FILE_REMOTE_DEVICE
;
6521 case 0x1373: /* devfs */
6522 case 0x9fa0: /* procfs */
6523 info
->DeviceType
= FILE_DEVICE_VIRTUAL_DISK
;
6525 case 0x01021994: /* tmpfs */
6526 case 0x28cd3d45: /* cramfs */
6527 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6528 * filesystems are rare on Windows, and some programs refuse to
6529 * recognize them as valid. */
6531 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6534 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6537 if (fstatfs( fd
, &stfs
) < 0)
6538 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6540 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flags
);
6541 #elif defined(__NetBSD__)
6542 struct statvfs stfs
;
6544 if (fstatvfs( fd
, &stfs
) < 0)
6545 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6547 get_device_info_fstatfs( info
, stfs
.f_fstypename
, stfs
.f_flag
);
6549 /* Use dkio to work out device types */
6551 # include <sys/dkio.h>
6552 # include <sys/vtoc.h>
6553 struct dk_cinfo dkinf
;
6554 int retval
= ioctl(fd
, DKIOCINFO
, &dkinf
);
6556 WARN("Unable to get disk device type information - assuming a disk like device\n");
6557 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6559 switch (dkinf
.dki_ctype
)
6562 info
->DeviceType
= FILE_DEVICE_CD_ROM_FILE_SYSTEM
;
6563 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
|FILE_READ_ONLY_DEVICE
;
6567 case DKC_INTEL82072
:
6568 case DKC_INTEL82077
:
6569 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6570 info
->Characteristics
|= FILE_REMOVABLE_MEDIA
;
6573 /* Don't map these to FILE_DEVICE_VIRTUAL_DISK by default. Virtual
6574 * filesystems are rare on Windows, and some programs refuse to
6575 * recognize them as valid. */
6577 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6582 if (!warned
++) FIXME( "device info not properly supported on this platform\n" );
6583 info
->DeviceType
= FILE_DEVICE_DISK_FILE_SYSTEM
;
6585 info
->Characteristics
|= FILE_DEVICE_IS_MOUNTED
;
6587 return STATUS_SUCCESS
;
6591 /******************************************************************************
6592 * NtQueryVolumeInformationFile (NTDLL.@)
6594 NTSTATUS WINAPI
NtQueryVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
,
6595 void *buffer
, ULONG length
,
6596 FS_INFORMATION_CLASS info_class
)
6598 int fd
, needs_close
;
6601 status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
);
6602 if (status
== STATUS_BAD_DEVICE_TYPE
)
6604 struct async_irp
*async
;
6607 if (!(async
= (struct async_irp
*)alloc_fileio( sizeof(*async
), irp_completion
, handle
)))
6608 return STATUS_NO_MEMORY
;
6609 async
->buffer
= buffer
;
6610 async
->size
= length
;
6612 SERVER_START_REQ( get_volume_info
)
6614 req
->async
= server_async( handle
, &async
->io
, NULL
, NULL
, NULL
, iosb_client_ptr(io
) );
6615 req
->handle
= wine_server_obj_handle( handle
);
6616 req
->info_class
= info_class
;
6617 wine_server_set_reply( req
, buffer
, length
);
6618 status
= wine_server_call( req
);
6619 if (status
!= STATUS_PENDING
)
6621 io
->u
.Status
= status
;
6622 io
->Information
= wine_server_reply_size( reply
);
6624 wait_handle
= wine_server_ptr_handle( reply
->wait
);
6627 if (status
!= STATUS_PENDING
) free( async
);
6628 if (wait_handle
) status
= wait_async( wait_handle
, FALSE
);
6631 else if (status
) return io
->u
.Status
= status
;
6633 io
->Information
= 0;
6635 switch( info_class
)
6637 case FileFsLabelInformation
:
6638 FIXME( "%p: label info not supported\n", handle
);
6639 status
= STATUS_NOT_IMPLEMENTED
;
6642 case FileFsSizeInformation
:
6643 if (length
< sizeof(FILE_FS_SIZE_INFORMATION
))
6644 status
= STATUS_BUFFER_TOO_SMALL
;
6647 FILE_FS_SIZE_INFORMATION
*info
= buffer
;
6648 FILE_FS_FULL_SIZE_INFORMATION full_info
;
6650 if ((status
= get_full_size_info(fd
, &full_info
)) == STATUS_SUCCESS
)
6652 info
->TotalAllocationUnits
= full_info
.TotalAllocationUnits
;
6653 info
->AvailableAllocationUnits
= full_info
.CallerAvailableAllocationUnits
;
6654 info
->SectorsPerAllocationUnit
= full_info
.SectorsPerAllocationUnit
;
6655 info
->BytesPerSector
= full_info
.BytesPerSector
;
6656 io
->Information
= sizeof(*info
);
6661 case FileFsDeviceInformation
:
6662 if (length
< sizeof(FILE_FS_DEVICE_INFORMATION
))
6663 status
= STATUS_BUFFER_TOO_SMALL
;
6666 FILE_FS_DEVICE_INFORMATION
*info
= buffer
;
6668 if ((status
= get_device_info( fd
, info
)) == STATUS_SUCCESS
)
6669 io
->Information
= sizeof(*info
);
6673 case FileFsAttributeInformation
:
6675 static const WCHAR fatW
[] = {'F','A','T'};
6676 static const WCHAR fat32W
[] = {'F','A','T','3','2'};
6677 static const WCHAR ntfsW
[] = {'N','T','F','S'};
6678 static const WCHAR cdfsW
[] = {'C','D','F','S'};
6679 static const WCHAR udfW
[] = {'U','D','F'};
6681 FILE_FS_ATTRIBUTE_INFORMATION
*info
= buffer
;
6682 struct mountmgr_unix_drive drive
;
6683 enum mountmgr_fs_type fs_type
= MOUNTMGR_FS_TYPE_NTFS
;
6685 if (length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
))
6687 status
= STATUS_INFO_LENGTH_MISMATCH
;
6691 if (!get_mountmgr_fs_info( handle
, fd
, &drive
, sizeof(drive
) )) fs_type
= drive
.fs_type
;
6696 if (!fstatfs( fd
, &stfs
))
6698 #if defined(linux) && defined(HAVE_FSTATFS)
6699 switch (stfs
.f_type
)
6702 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6705 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6708 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6711 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
6712 if (!strcmp( stfs
.f_fstypename
, "cd9660" ))
6713 fs_type
= MOUNTMGR_FS_TYPE_ISO9660
;
6714 else if (!strcmp( stfs
.f_fstypename
, "udf" ))
6715 fs_type
= MOUNTMGR_FS_TYPE_UDF
;
6716 else if (!strcmp( stfs
.f_fstypename
, "msdos" )) /* FreeBSD < 5, Apple */
6717 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6718 else if (!strcmp( stfs
.f_fstypename
, "msdosfs" )) /* FreeBSD >= 5 */
6719 fs_type
= MOUNTMGR_FS_TYPE_FAT32
;
6726 case MOUNTMGR_FS_TYPE_ISO9660
:
6727 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
;
6728 info
->MaximumComponentNameLength
= 221;
6729 info
->FileSystemNameLength
= min( sizeof(cdfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6730 memcpy(info
->FileSystemName
, cdfsW
, info
->FileSystemNameLength
);
6732 case MOUNTMGR_FS_TYPE_UDF
:
6733 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
| FILE_UNICODE_ON_DISK
| FILE_CASE_SENSITIVE_SEARCH
;
6734 info
->MaximumComponentNameLength
= 255;
6735 info
->FileSystemNameLength
= min( sizeof(udfW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6736 memcpy(info
->FileSystemName
, udfW
, info
->FileSystemNameLength
);
6738 case MOUNTMGR_FS_TYPE_FAT
:
6739 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
6740 info
->MaximumComponentNameLength
= 255;
6741 info
->FileSystemNameLength
= min( sizeof(fatW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6742 memcpy(info
->FileSystemName
, fatW
, info
->FileSystemNameLength
);
6744 case MOUNTMGR_FS_TYPE_FAT32
:
6745 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
6746 info
->MaximumComponentNameLength
= 255;
6747 info
->FileSystemNameLength
= min( sizeof(fat32W
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6748 memcpy(info
->FileSystemName
, fat32W
, info
->FileSystemNameLength
);
6751 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_PERSISTENT_ACLS
;
6752 info
->MaximumComponentNameLength
= 255;
6753 info
->FileSystemNameLength
= min( sizeof(ntfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
6754 memcpy(info
->FileSystemName
, ntfsW
, info
->FileSystemNameLength
);
6758 io
->Information
= offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) + info
->FileSystemNameLength
;
6759 status
= STATUS_SUCCESS
;
6763 case FileFsVolumeInformation
:
6765 FILE_FS_VOLUME_INFORMATION
*info
= buffer
;
6767 struct mountmgr_unix_drive
*drive
= (struct mountmgr_unix_drive
*)data
;
6770 if (length
< sizeof(FILE_FS_VOLUME_INFORMATION
))
6772 status
= STATUS_INFO_LENGTH_MISMATCH
;
6776 if (get_mountmgr_fs_info( handle
, fd
, drive
, sizeof(data
) ))
6778 status
= STATUS_NOT_IMPLEMENTED
;
6782 label
= (WCHAR
*)((char *)drive
+ drive
->label_offset
);
6783 info
->VolumeCreationTime
.QuadPart
= 0; /* FIXME */
6784 info
->VolumeSerialNumber
= drive
->serial
;
6785 info
->VolumeLabelLength
= min( wcslen( label
) * sizeof(WCHAR
),
6786 length
- offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) );
6787 info
->SupportsObjects
= (drive
->fs_type
== MOUNTMGR_FS_TYPE_NTFS
);
6788 memcpy( info
->VolumeLabel
, label
, info
->VolumeLabelLength
);
6789 io
->Information
= offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) + info
->VolumeLabelLength
;
6790 status
= STATUS_SUCCESS
;
6794 case FileFsControlInformation
:
6795 FIXME( "%p: control info not supported\n", handle
);
6796 status
= STATUS_NOT_IMPLEMENTED
;
6799 case FileFsFullSizeInformation
:
6800 if (length
< sizeof(FILE_FS_FULL_SIZE_INFORMATION
))
6801 status
= STATUS_BUFFER_TOO_SMALL
;
6804 FILE_FS_FULL_SIZE_INFORMATION
*info
= buffer
;
6805 if ((status
= get_full_size_info(fd
, info
)) == STATUS_SUCCESS
)
6806 io
->Information
= sizeof(*info
);
6810 case FileFsObjectIdInformation
:
6811 FIXME( "%p: object id info not supported\n", handle
);
6812 status
= STATUS_NOT_IMPLEMENTED
;
6815 case FileFsMaximumInformation
:
6816 FIXME( "%p: maximum info not supported\n", handle
);
6817 status
= STATUS_NOT_IMPLEMENTED
;
6821 status
= STATUS_INVALID_PARAMETER
;
6824 if (needs_close
) close( fd
);
6825 return io
->u
.Status
= status
;
6829 /******************************************************************************
6830 * NtSetVolumeInformationFile (NTDLL.@)
6832 NTSTATUS WINAPI
NtSetVolumeInformationFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *info
,
6833 ULONG length
, FS_INFORMATION_CLASS
class )
6835 FIXME( "(%p,%p,%p,0x%08x,0x%08x) stub\n", handle
, io
, info
, length
, class );
6836 return STATUS_SUCCESS
;
6840 /******************************************************************
6841 * NtQueryEaFile (NTDLL.@)
6843 NTSTATUS WINAPI
NtQueryEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
,
6844 BOOLEAN single_entry
, void *list
, ULONG list_len
,
6845 ULONG
*index
, BOOLEAN restart
)
6847 int fd
, needs_close
;
6850 FIXME( "(%p,%p,%p,%d,%d,%p,%d,%p,%d) semi-stub\n",
6851 handle
, io
, buffer
, length
, single_entry
, list
, list_len
, index
, restart
);
6853 if ((status
= server_get_unix_fd( handle
, 0, &fd
, &needs_close
, NULL
, NULL
)))
6856 if (buffer
&& length
)
6857 memset( buffer
, 0, length
);
6859 if (needs_close
) close( fd
);
6860 return STATUS_NO_EAS_ON_FILE
;
6864 /******************************************************************
6865 * NtSetEaFile (NTDLL.@)
6867 NTSTATUS WINAPI
NtSetEaFile( HANDLE handle
, IO_STATUS_BLOCK
*io
, void *buffer
, ULONG length
)
6869 FIXME( "(%p,%p,%p,%d) stub\n", handle
, io
, buffer
, length
);
6870 return STATUS_ACCESS_DENIED
;
6874 /* convert type information from server format; helper for NtQueryObject */
6875 static void *put_object_type_info( OBJECT_TYPE_INFORMATION
*p
, struct object_type_info
*info
)
6877 const ULONG align
= sizeof(DWORD_PTR
) - 1;
6879 memset( p
, 0, sizeof(*p
) );
6880 p
->TypeName
.Buffer
= (WCHAR
*)(p
+ 1);
6881 p
->TypeName
.Length
= info
->name_len
;
6882 p
->TypeName
.MaximumLength
= info
->name_len
+ sizeof(WCHAR
);
6883 p
->TotalNumberOfObjects
= info
->obj_count
;
6884 p
->TotalNumberOfHandles
= info
->handle_count
;
6885 p
->HighWaterNumberOfObjects
= info
->obj_max
;
6886 p
->HighWaterNumberOfHandles
= info
->handle_max
;
6887 p
->TypeIndex
= info
->index
+ 2;
6888 p
->GenericMapping
.GenericRead
= info
->mapping
.read
;
6889 p
->GenericMapping
.GenericWrite
= info
->mapping
.write
;
6890 p
->GenericMapping
.GenericExecute
= info
->mapping
.exec
;
6891 p
->GenericMapping
.GenericAll
= info
->mapping
.all
;
6892 p
->ValidAccessMask
= info
->valid_access
;
6893 memcpy( p
->TypeName
.Buffer
, info
+ 1, info
->name_len
);
6894 p
->TypeName
.Buffer
[info
->name_len
/ sizeof(WCHAR
)] = 0;
6895 return (char *)(p
+ 1) + ((p
->TypeName
.MaximumLength
+ align
) & ~align
);
6898 /**************************************************************************
6899 * NtQueryObject (NTDLL.@)
6901 NTSTATUS WINAPI
NtQueryObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
6902 void *ptr
, ULONG len
, ULONG
*used_len
)
6906 TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", handle
, info_class
, ptr
, len
, used_len
);
6908 if (used_len
) *used_len
= 0;
6912 case ObjectBasicInformation
:
6914 OBJECT_BASIC_INFORMATION
*p
= ptr
;
6916 if (len
< sizeof(*p
)) return STATUS_INFO_LENGTH_MISMATCH
;
6918 SERVER_START_REQ( get_object_info
)
6920 req
->handle
= wine_server_obj_handle( handle
);
6921 status
= wine_server_call( req
);
6922 if (status
== STATUS_SUCCESS
)
6924 memset( p
, 0, sizeof(*p
) );
6925 p
->GrantedAccess
= reply
->access
;
6926 p
->PointerCount
= reply
->ref_count
;
6927 p
->HandleCount
= reply
->handle_count
;
6928 if (used_len
) *used_len
= sizeof(*p
);
6935 case ObjectNameInformation
:
6937 OBJECT_NAME_INFORMATION
*p
= ptr
;
6941 /* first try as a file object */
6943 if (!(status
= server_get_unix_name( handle
, &unix_name
)))
6945 if (!(status
= unix_to_nt_file_name( unix_name
, &nt_name
)))
6947 ULONG size
= (wcslen(nt_name
) + 1) * sizeof(WCHAR
);
6948 if (len
< sizeof(*p
)) status
= STATUS_INFO_LENGTH_MISMATCH
;
6949 else if (len
< sizeof(*p
) + size
) status
= STATUS_BUFFER_OVERFLOW
;
6952 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
6953 p
->Name
.Length
= size
- sizeof(WCHAR
);
6954 p
->Name
.MaximumLength
= size
;
6955 wcscpy( p
->Name
.Buffer
, nt_name
);
6957 if (used_len
) *used_len
= sizeof(*p
) + size
;
6963 else if (status
!= STATUS_OBJECT_TYPE_MISMATCH
) break;
6965 /* not a file, treat as a generic object */
6967 SERVER_START_REQ( get_object_name
)
6969 req
->handle
= wine_server_obj_handle( handle
);
6970 if (len
> sizeof(*p
)) wine_server_set_reply( req
, p
+ 1, len
- sizeof(*p
) );
6971 status
= wine_server_call( req
);
6972 if (status
== STATUS_SUCCESS
)
6974 if (!reply
->total
) /* no name */
6976 if (sizeof(*p
) > len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
6977 else memset( p
, 0, sizeof(*p
) );
6978 if (used_len
) *used_len
= sizeof(*p
);
6980 else if (sizeof(*p
) + reply
->total
+ sizeof(WCHAR
) > len
)
6982 if (used_len
) *used_len
= sizeof(*p
) + reply
->total
+ sizeof(WCHAR
);
6983 status
= STATUS_INFO_LENGTH_MISMATCH
;
6987 ULONG res
= wine_server_reply_size( reply
);
6988 p
->Name
.Buffer
= (WCHAR
*)(p
+ 1);
6989 p
->Name
.Length
= res
;
6990 p
->Name
.MaximumLength
= res
+ sizeof(WCHAR
);
6991 p
->Name
.Buffer
[res
/ sizeof(WCHAR
)] = 0;
6992 if (used_len
) *used_len
= sizeof(*p
) + p
->Name
.MaximumLength
;
7000 case ObjectTypeInformation
:
7002 OBJECT_TYPE_INFORMATION
*p
= ptr
;
7003 char buffer
[sizeof(struct object_type_info
) + 64];
7004 struct object_type_info
*info
= (struct object_type_info
*)buffer
;
7006 SERVER_START_REQ( get_object_type
)
7008 req
->handle
= wine_server_obj_handle( handle
);
7009 wine_server_set_reply( req
, buffer
, sizeof(buffer
) );
7010 status
= wine_server_call( req
);
7014 if (sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
) <= len
)
7016 put_object_type_info( p
, info
);
7017 if (used_len
) *used_len
= sizeof(*p
) + p
->TypeName
.MaximumLength
;
7021 if (used_len
) *used_len
= sizeof(*p
) + info
->name_len
+ sizeof(WCHAR
);
7022 status
= STATUS_INFO_LENGTH_MISMATCH
;
7027 case ObjectTypesInformation
:
7029 OBJECT_TYPES_INFORMATION
*types
= ptr
;
7030 OBJECT_TYPE_INFORMATION
*p
;
7031 struct object_type_info
*buffer
;
7032 /* assume at most 32 types, with an average 16-char name */
7033 ULONG size
= 32 * (sizeof(struct object_type_info
) + 16 * sizeof(WCHAR
));
7034 ULONG i
, count
, pos
, total
, align
= sizeof(DWORD_PTR
) - 1;
7036 buffer
= malloc( size
);
7037 SERVER_START_REQ( get_object_types
)
7039 wine_server_set_reply( req
, buffer
, size
);
7040 status
= wine_server_call( req
);
7041 count
= reply
->count
;
7046 if (len
>= sizeof(*types
)) types
->NumberOfTypes
= count
;
7047 total
= (sizeof(*types
) + align
) & ~align
;
7048 p
= (OBJECT_TYPE_INFORMATION
*)((char *)ptr
+ total
);
7049 for (i
= pos
= 0; i
< count
; i
++)
7051 struct object_type_info
*info
= (struct object_type_info
*)((char *)buffer
+ pos
);
7052 pos
+= sizeof(*info
) + ((info
->name_len
+ 3) & ~3);
7053 total
+= sizeof(*p
) + ((info
->name_len
+ sizeof(WCHAR
) + align
) & ~align
);
7054 if (total
<= len
) p
= put_object_type_info( p
, info
);
7056 if (used_len
) *used_len
= total
;
7057 if (total
> len
) status
= STATUS_INFO_LENGTH_MISMATCH
;
7059 else if (status
== STATUS_BUFFER_OVERFLOW
) FIXME( "size %u too small\n", size
);
7065 case ObjectDataInformation
:
7067 OBJECT_DATA_INFORMATION
* p
= ptr
;
7069 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
7071 SERVER_START_REQ( set_handle_info
)
7073 req
->handle
= wine_server_obj_handle( handle
);
7076 status
= wine_server_call( req
);
7077 if (status
== STATUS_SUCCESS
)
7079 p
->InheritHandle
= (reply
->old_flags
& HANDLE_FLAG_INHERIT
) != 0;
7080 p
->ProtectFromClose
= (reply
->old_flags
& HANDLE_FLAG_PROTECT_FROM_CLOSE
) != 0;
7081 if (used_len
) *used_len
= sizeof(*p
);
7089 FIXME("Unsupported information class %u\n", info_class
);
7090 status
= STATUS_NOT_IMPLEMENTED
;
7097 /**************************************************************************
7098 * NtSetInformationObject (NTDLL.@)
7100 NTSTATUS WINAPI
NtSetInformationObject( HANDLE handle
, OBJECT_INFORMATION_CLASS info_class
,
7101 void *ptr
, ULONG len
)
7105 TRACE("(%p,0x%08x,%p,0x%08x)\n", handle
, info_class
, ptr
, len
);
7109 case ObjectDataInformation
:
7111 OBJECT_DATA_INFORMATION
* p
= ptr
;
7113 if (len
< sizeof(*p
)) return STATUS_INVALID_BUFFER_SIZE
;
7115 SERVER_START_REQ( set_handle_info
)
7117 req
->handle
= wine_server_obj_handle( handle
);
7118 req
->mask
= HANDLE_FLAG_INHERIT
| HANDLE_FLAG_PROTECT_FROM_CLOSE
;
7119 if (p
->InheritHandle
) req
->flags
|= HANDLE_FLAG_INHERIT
;
7120 if (p
->ProtectFromClose
) req
->flags
|= HANDLE_FLAG_PROTECT_FROM_CLOSE
;
7121 status
= wine_server_call( req
);
7128 FIXME("Unsupported information class %u\n", info_class
);
7129 status
= STATUS_NOT_IMPLEMENTED
;