Commit c3e2013b authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Move the get/set file information functions to the Unix library.

parent 07248fc5
......@@ -112,82 +112,9 @@
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
mode_t FILE_umask = 0;
#define SECSPERDAY 86400
#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
#define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1)
#define FILE_USE_FILE_POINTER_POSITION ((LONGLONG)-2)
/* fetch the attributes of a file */
static inline ULONG get_file_attributes( const struct stat *st )
{
ULONG attr;
if (S_ISDIR(st->st_mode))
attr = FILE_ATTRIBUTE_DIRECTORY;
else
attr = FILE_ATTRIBUTE_ARCHIVE;
if (!(st->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
attr |= FILE_ATTRIBUTE_READONLY;
return attr;
}
static BOOL fd_is_mount_point( int fd, const struct stat *st )
{
struct stat parent;
return S_ISDIR( st->st_mode ) && !fstatat( fd, "..", &parent, 0 )
&& (parent.st_dev != st->st_dev || parent.st_ino == st->st_ino);
}
/* get the stat info and file attributes for a file (by file descriptor) */
int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr )
{
int ret;
*attr = 0;
ret = fstat( fd, st );
if (ret == -1) return ret;
*attr |= get_file_attributes( st );
/* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st ))
*attr |= FILE_ATTRIBUTE_REPARSE_POINT;
return ret;
}
static int get_file_info( const char *path, struct stat *st, ULONG *attr )
{
char *parent_path;
int ret;
*attr = 0;
ret = lstat( path, st );
if (ret == -1) return ret;
if (S_ISLNK( st->st_mode ))
{
ret = stat( path, st );
if (ret == -1) return ret;
/* is a symbolic link and a directory, consider these "reparse points" */
if (S_ISDIR( st->st_mode )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
}
else if (S_ISDIR( st->st_mode ) && (parent_path = RtlAllocateHeap( GetProcessHeap(), 0, strlen(path) + 4 )))
{
struct stat parent_st;
/* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
strcpy( parent_path, path );
strcat( parent_path, "/.." );
if (!stat( parent_path, &parent_st )
&& (st->st_dev != parent_st.st_dev || st->st_ino == parent_st.st_ino))
*attr |= FILE_ATTRIBUTE_REPARSE_POINT;
RtlFreeHeap( GetProcessHeap(), 0, parent_path );
}
*attr |= get_file_attributes( st );
return ret;
}
/**************************************************************************
* NtOpenFile [NTDLL.@]
......@@ -1802,244 +1729,6 @@ NTSTATUS WINAPI NtSetVolumeInformationFile(
return 0;
}
#if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
static int futimens( int fd, const struct timespec spec[2] )
{
return syscall( __NR_utimensat, fd, NULL, spec, 0 );
}
#define HAVE_FUTIMENS
#endif /* __ANDROID__ */
#ifndef UTIME_OMIT
#define UTIME_OMIT ((1 << 30) - 2)
#endif
static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
const LARGE_INTEGER *atime, NTSTATUS *status )
{
#ifdef HAVE_FUTIMENS
struct timespec tv[2];
tv[0].tv_sec = tv[1].tv_sec = 0;
tv[0].tv_nsec = tv[1].tv_nsec = UTIME_OMIT;
if (atime->QuadPart)
{
tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[0].tv_nsec = (atime->QuadPart % 10000000) * 100;
}
if (mtime->QuadPart)
{
tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[1].tv_nsec = (mtime->QuadPart % 10000000) * 100;
}
#ifdef __APPLE__
if (!&futimens) return FALSE;
#endif
if (futimens( fd, tv ) == -1) *status = FILE_GetNtStatus();
else *status = STATUS_SUCCESS;
return TRUE;
#else
return FALSE;
#endif
}
static NTSTATUS set_file_times( int fd, const LARGE_INTEGER *mtime, const LARGE_INTEGER *atime )
{
NTSTATUS status = STATUS_SUCCESS;
#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
struct timeval tv[2];
struct stat st;
#endif
if (set_file_times_precise( fd, mtime, atime, &status ))
return status;
#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
if (!atime->QuadPart || !mtime->QuadPart)
{
tv[0].tv_sec = tv[0].tv_usec = 0;
tv[1].tv_sec = tv[1].tv_usec = 0;
if (!fstat( fd, &st ))
{
tv[0].tv_sec = st.st_atime;
tv[1].tv_sec = st.st_mtime;
#ifdef HAVE_STRUCT_STAT_ST_ATIM
tv[0].tv_usec = st.st_atim.tv_nsec / 1000;
#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
tv[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
#endif
#ifdef HAVE_STRUCT_STAT_ST_MTIM
tv[1].tv_usec = st.st_mtim.tv_nsec / 1000;
#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
tv[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
#endif
}
}
if (atime->QuadPart)
{
tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[0].tv_usec = (atime->QuadPart % 10000000) / 10;
}
if (mtime->QuadPart)
{
tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[1].tv_usec = (mtime->QuadPart % 10000000) / 10;
}
#ifdef HAVE_FUTIMES
if (futimes( fd, tv ) == -1) status = FILE_GetNtStatus();
#elif defined(HAVE_FUTIMESAT)
if (futimesat( fd, NULL, tv ) == -1) status = FILE_GetNtStatus();
#endif
#else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
FIXME( "setting file times not supported\n" );
status = STATUS_NOT_IMPLEMENTED;
#endif
return status;
}
static inline void get_file_times( const struct stat *st, LARGE_INTEGER *mtime, LARGE_INTEGER *ctime,
LARGE_INTEGER *atime, LARGE_INTEGER *creation )
{
RtlSecondsSince1970ToTime( st->st_mtime, mtime );
RtlSecondsSince1970ToTime( st->st_ctime, ctime );
RtlSecondsSince1970ToTime( st->st_atime, atime );
#ifdef HAVE_STRUCT_STAT_ST_MTIM
mtime->QuadPart += st->st_mtim.tv_nsec / 100;
#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
mtime->QuadPart += st->st_mtimespec.tv_nsec / 100;
#endif
#ifdef HAVE_STRUCT_STAT_ST_CTIM
ctime->QuadPart += st->st_ctim.tv_nsec / 100;
#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
ctime->QuadPart += st->st_ctimespec.tv_nsec / 100;
#endif
#ifdef HAVE_STRUCT_STAT_ST_ATIM
atime->QuadPart += st->st_atim.tv_nsec / 100;
#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
atime->QuadPart += st->st_atimespec.tv_nsec / 100;
#endif
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
RtlSecondsSince1970ToTime( st->st_birthtime, creation );
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIM
creation->QuadPart += st->st_birthtim.tv_nsec / 100;
#elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
creation->QuadPart += st->st_birthtimespec.tv_nsec / 100;
#endif
#elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
RtlSecondsSince1970ToTime( st->__st_birthtime, creation );
#ifdef HAVE_STRUCT_STAT___ST_BIRTHTIM
creation->QuadPart += st->__st_birthtim.tv_nsec / 100;
#endif
#else
*creation = *mtime;
#endif
}
/* fill in the file information that depends on the stat and attribute info */
NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr,
FILE_INFORMATION_CLASS class )
{
switch (class)
{
case FileBasicInformation:
{
FILE_BASIC_INFORMATION *info = ptr;
get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
&info->LastAccessTime, &info->CreationTime );
info->FileAttributes = attr;
}
break;
case FileStandardInformation:
{
FILE_STANDARD_INFORMATION *info = ptr;
if ((info->Directory = S_ISDIR(st->st_mode)))
{
info->AllocationSize.QuadPart = 0;
info->EndOfFile.QuadPart = 0;
info->NumberOfLinks = 1;
}
else
{
info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
info->EndOfFile.QuadPart = st->st_size;
info->NumberOfLinks = st->st_nlink;
}
}
break;
case FileInternalInformation:
{
FILE_INTERNAL_INFORMATION *info = ptr;
info->IndexNumber.QuadPart = st->st_ino;
}
break;
case FileEndOfFileInformation:
{
FILE_END_OF_FILE_INFORMATION *info = ptr;
info->EndOfFile.QuadPart = S_ISDIR(st->st_mode) ? 0 : st->st_size;
}
break;
case FileAllInformation:
{
FILE_ALL_INFORMATION *info = ptr;
fill_file_info( st, attr, &info->BasicInformation, FileBasicInformation );
fill_file_info( st, attr, &info->StandardInformation, FileStandardInformation );
fill_file_info( st, attr, &info->InternalInformation, FileInternalInformation );
}
break;
/* all directory structures start with the FileDirectoryInformation layout */
case FileBothDirectoryInformation:
case FileFullDirectoryInformation:
case FileDirectoryInformation:
{
FILE_DIRECTORY_INFORMATION *info = ptr;
get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
&info->LastAccessTime, &info->CreationTime );
if (S_ISDIR(st->st_mode))
{
info->AllocationSize.QuadPart = 0;
info->EndOfFile.QuadPart = 0;
}
else
{
info->AllocationSize.QuadPart = (ULONGLONG)st->st_blocks * 512;
info->EndOfFile.QuadPart = st->st_size;
}
info->FileAttributes = attr;
}
break;
case FileIdFullDirectoryInformation:
{
FILE_ID_FULL_DIRECTORY_INFORMATION *info = ptr;
info->FileId.QuadPart = st->st_ino;
fill_file_info( st, attr, info, FileDirectoryInformation );
}
break;
case FileIdBothDirectoryInformation:
{
FILE_ID_BOTH_DIRECTORY_INFORMATION *info = ptr;
info->FileId.QuadPart = st->st_ino;
fill_file_info( st, attr, info, FileDirectoryInformation );
}
break;
case FileIdGlobalTxDirectoryInformation:
{
FILE_ID_GLOBAL_TX_DIR_INFORMATION *info = ptr;
info->FileId.QuadPart = st->st_ino;
fill_file_info( st, attr, info, FileDirectoryInformation );
}
break;
default:
return STATUS_INVALID_INFO_CLASS;
}
return STATUS_SUCCESS;
}
NTSTATUS server_get_unix_name( HANDLE handle, ANSI_STRING *unix_name )
{
data_size_t size = 1024;
......@@ -2074,51 +1763,6 @@ NTSTATUS server_get_unix_name( HANDLE handle, ANSI_STRING *unix_name )
return ret;
}
static NTSTATUS fill_name_info( const ANSI_STRING *unix_name, FILE_NAME_INFORMATION *info, LONG *name_len )
{
UNICODE_STRING nt_name;
NTSTATUS status;
if (!(status = wine_unix_to_nt_file_name( unix_name, &nt_name )))
{
const WCHAR *ptr = nt_name.Buffer;
const WCHAR *end = ptr + (nt_name.Length / sizeof(WCHAR));
/* Skip the volume mount point. */
while (ptr != end && *ptr == '\\') ++ptr;
while (ptr != end && *ptr != '\\') ++ptr;
while (ptr != end && *ptr == '\\') ++ptr;
while (ptr != end && *ptr != '\\') ++ptr;
info->FileNameLength = (end - ptr) * sizeof(WCHAR);
if (*name_len < info->FileNameLength) status = STATUS_BUFFER_OVERFLOW;
else *name_len = info->FileNameLength;
memcpy( info->FileName, ptr, *name_len );
RtlFreeUnicodeString( &nt_name );
}
return status;
}
static NTSTATUS server_get_file_info( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer,
ULONG length, FILE_INFORMATION_CLASS info_class )
{
SERVER_START_REQ( get_file_info )
{
req->handle = wine_server_obj_handle( handle );
req->info_class = info_class;
wine_server_set_reply( req, buffer, length );
io->u.Status = wine_server_call( req );
io->Information = wine_server_reply_size( reply );
}
SERVER_END_REQ;
if (io->u.Status == STATUS_NOT_IMPLEMENTED)
FIXME( "Unsupported info class %x\n", info_class );
return io->u.Status;
}
/* Find a DOS device which can act as the root of "path".
* Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
static int find_dos_device( const char *path )
......@@ -2260,288 +1904,7 @@ static struct mountmgr_unix_drive *get_mountmgr_fs_info( HANDLE handle, int fd )
NTSTATUS WINAPI NtQueryInformationFile( HANDLE hFile, PIO_STATUS_BLOCK io,
PVOID ptr, LONG len, FILE_INFORMATION_CLASS class )
{
static const size_t info_sizes[] =
{
0,
sizeof(FILE_DIRECTORY_INFORMATION), /* FileDirectoryInformation */
sizeof(FILE_FULL_DIRECTORY_INFORMATION), /* FileFullDirectoryInformation */
sizeof(FILE_BOTH_DIRECTORY_INFORMATION), /* FileBothDirectoryInformation */
sizeof(FILE_BASIC_INFORMATION), /* FileBasicInformation */
sizeof(FILE_STANDARD_INFORMATION), /* FileStandardInformation */
sizeof(FILE_INTERNAL_INFORMATION), /* FileInternalInformation */
sizeof(FILE_EA_INFORMATION), /* FileEaInformation */
0, /* FileAccessInformation */
sizeof(FILE_NAME_INFORMATION), /* FileNameInformation */
sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
0, /* FileLinkInformation */
sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR), /* FileNamesInformation */
sizeof(FILE_DISPOSITION_INFORMATION), /* FileDispositionInformation */
sizeof(FILE_POSITION_INFORMATION), /* FilePositionInformation */
sizeof(FILE_FULL_EA_INFORMATION), /* FileFullEaInformation */
0, /* FileModeInformation */
sizeof(FILE_ALIGNMENT_INFORMATION), /* FileAlignmentInformation */
sizeof(FILE_ALL_INFORMATION), /* FileAllInformation */
sizeof(FILE_ALLOCATION_INFORMATION), /* FileAllocationInformation */
sizeof(FILE_END_OF_FILE_INFORMATION), /* FileEndOfFileInformation */
0, /* FileAlternateNameInformation */
sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
sizeof(FILE_PIPE_INFORMATION), /* FilePipeInformation */
sizeof(FILE_PIPE_LOCAL_INFORMATION), /* FilePipeLocalInformation */
0, /* FilePipeRemoteInformation */
sizeof(FILE_MAILSLOT_QUERY_INFORMATION), /* FileMailslotQueryInformation */
0, /* FileMailslotSetInformation */
0, /* FileCompressionInformation */
0, /* FileObjectIdInformation */
0, /* FileCompletionInformation */
0, /* FileMoveClusterInformation */
0, /* FileQuotaInformation */
0, /* FileReparsePointInformation */
sizeof(FILE_NETWORK_OPEN_INFORMATION), /* FileNetworkOpenInformation */
sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), /* FileAttributeTagInformation */
0, /* FileTrackingInformation */
0, /* FileIdBothDirectoryInformation */
0, /* FileIdFullDirectoryInformation */
0, /* FileValidDataLengthInformation */
0, /* FileShortNameInformation */
0, /* FileIoCompletionNotificationInformation, */
0, /* FileIoStatusBlockRangeInformation */
0, /* FileIoPriorityHintInformation */
0, /* FileSfioReserveInformation */
0, /* FileSfioVolumeInformation */
0, /* FileHardLinkInformation */
0, /* FileProcessIdsUsingFileInformation */
0, /* FileNormalizedNameInformation */
0, /* FileNetworkPhysicalNameInformation */
0, /* FileIdGlobalTxDirectoryInformation */
0, /* FileIsRemoteDeviceInformation */
0, /* FileAttributeCacheInformation */
0, /* FileNumaNodeInformation */
0, /* FileStandardLinkInformation */
0, /* FileRemoteProtocolInformation */
0, /* FileRenameInformationBypassAccessCheck */
0, /* FileLinkInformationBypassAccessCheck */
0, /* FileVolumeNameInformation */
sizeof(FILE_ID_INFORMATION), /* FileIdInformation */
0, /* FileIdExtdDirectoryInformation */
0, /* FileReplaceCompletionInformation */
0, /* FileHardLinkFullIdInformation */
0, /* FileIdExtdBothDirectoryInformation */
};
struct stat st;
int fd, needs_close = FALSE;
ULONG attr;
unsigned int options;
TRACE("(%p,%p,%p,0x%08x,0x%08x)\n", hFile, io, ptr, len, class);
io->Information = 0;
if (class <= 0 || class >= FileMaximumInformation)
return io->u.Status = STATUS_INVALID_INFO_CLASS;
if (!info_sizes[class])
return server_get_file_info( hFile, io, ptr, len, class );
if (len < info_sizes[class])
return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
if ((io->u.Status = unix_funcs->server_get_unix_fd( hFile, 0, &fd, &needs_close, NULL, &options )))
{
if (io->u.Status != STATUS_BAD_DEVICE_TYPE) return io->u.Status;
return server_get_file_info( hFile, io, ptr, len, class );
}
switch (class)
{
case FileBasicInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1)
io->u.Status = FILE_GetNtStatus();
else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
io->u.Status = STATUS_INVALID_INFO_CLASS;
else
fill_file_info( &st, attr, ptr, class );
break;
case FileStandardInformation:
{
FILE_STANDARD_INFORMATION *info = ptr;
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = FILE_GetNtStatus();
else
{
fill_file_info( &st, attr, info, class );
info->DeletePending = FALSE; /* FIXME */
}
}
break;
case FilePositionInformation:
{
FILE_POSITION_INFORMATION *info = ptr;
off_t res = lseek( fd, 0, SEEK_CUR );
if (res == (off_t)-1) io->u.Status = FILE_GetNtStatus();
else info->CurrentByteOffset.QuadPart = res;
}
break;
case FileInternalInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = FILE_GetNtStatus();
else fill_file_info( &st, attr, ptr, class );
break;
case FileEaInformation:
{
FILE_EA_INFORMATION *info = ptr;
info->EaSize = 0;
}
break;
case FileEndOfFileInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = FILE_GetNtStatus();
else fill_file_info( &st, attr, ptr, class );
break;
case FileAllInformation:
{
FILE_ALL_INFORMATION *info = ptr;
ANSI_STRING unix_name;
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = FILE_GetNtStatus();
else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
io->u.Status = STATUS_INVALID_INFO_CLASS;
else if (!(io->u.Status = server_get_unix_name( hFile, &unix_name )))
{
LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
fill_file_info( &st, attr, info, FileAllInformation );
info->StandardInformation.DeletePending = FALSE; /* FIXME */
info->EaInformation.EaSize = 0;
info->AccessInformation.AccessFlags = 0; /* FIXME */
info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
info->ModeInformation.Mode = 0; /* FIXME */
info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
io->u.Status = fill_name_info( &unix_name, &info->NameInformation, &name_len );
RtlFreeAnsiString( &unix_name );
io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len;
}
}
break;
case FileMailslotQueryInformation:
{
FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;
SERVER_START_REQ( set_mailslot_info )
{
req->handle = wine_server_obj_handle( hFile );
req->flags = 0;
io->u.Status = wine_server_call( req );
if( io->u.Status == STATUS_SUCCESS )
{
info->MaximumMessageSize = reply->max_msgsize;
info->MailslotQuota = 0;
info->NextMessageSize = 0;
info->MessagesAvailable = 0;
info->ReadTimeout.QuadPart = reply->read_timeout;
}
}
SERVER_END_REQ;
if (!io->u.Status)
{
char *tmpbuf;
ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
if (size > 0x10000) size = 0x10000;
if ((tmpbuf = RtlAllocateHeap( GetProcessHeap(), 0, size )))
{
if (!unix_funcs->server_get_unix_fd( hFile, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
{
int res = recv( fd, tmpbuf, size, MSG_PEEK );
info->MessagesAvailable = (res > 0);
info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
if (needs_close) close( fd );
}
RtlFreeHeap( GetProcessHeap(), 0, tmpbuf );
}
}
}
break;
case FileNameInformation:
{
FILE_NAME_INFORMATION *info = ptr;
ANSI_STRING unix_name;
if (!(io->u.Status = server_get_unix_name( hFile, &unix_name )))
{
LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
io->u.Status = fill_name_info( &unix_name, info, &name_len );
RtlFreeAnsiString( &unix_name );
io->Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + name_len;
}
}
break;
case FileNetworkOpenInformation:
{
FILE_NETWORK_OPEN_INFORMATION *info = ptr;
ANSI_STRING unix_name;
if (!(io->u.Status = server_get_unix_name( hFile, &unix_name )))
{
ULONG attributes;
struct stat st;
if (get_file_info( unix_name.Buffer, &st, &attributes ) == -1)
io->u.Status = FILE_GetNtStatus();
else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
io->u.Status = STATUS_INVALID_INFO_CLASS;
else
{
FILE_BASIC_INFORMATION basic;
FILE_STANDARD_INFORMATION std;
fill_file_info( &st, attributes, &basic, FileBasicInformation );
fill_file_info( &st, attributes, &std, FileStandardInformation );
info->CreationTime = basic.CreationTime;
info->LastAccessTime = basic.LastAccessTime;
info->LastWriteTime = basic.LastWriteTime;
info->ChangeTime = basic.ChangeTime;
info->AllocationSize = std.AllocationSize;
info->EndOfFile = std.EndOfFile;
info->FileAttributes = basic.FileAttributes;
}
RtlFreeAnsiString( &unix_name );
}
}
break;
case FileIdInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = FILE_GetNtStatus();
else
{
struct mountmgr_unix_drive *drive;
FILE_ID_INFORMATION *info = ptr;
info->VolumeSerialNumber = 0;
if ((drive = get_mountmgr_fs_info( hFile, fd )))
{
info->VolumeSerialNumber = drive->serial;
RtlFreeHeap( GetProcessHeap(), 0, drive );
}
memset( &info->FileId, 0, sizeof(info->FileId) );
*(ULONGLONG *)&info->FileId = st.st_ino;
}
break;
case FileAttributeTagInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = FILE_GetNtStatus();
else
{
FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
info->FileAttributes = attr;
info->ReparseTag = 0; /* FIXME */
if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
}
break;
default:
FIXME("Unsupported class (%d)\n", class);
io->u.Status = STATUS_NOT_IMPLEMENTED;
break;
}
if (needs_close) close( fd );
if (io->u.Status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
return io->u.Status;
return unix_funcs->NtQueryInformationFile( hFile, io, ptr, len, class );
}
/******************************************************************************
......@@ -2564,317 +1927,7 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE hFile, PIO_STATUS_BLOCK io,
NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io,
PVOID ptr, ULONG len, FILE_INFORMATION_CLASS class)
{
int fd, needs_close;
TRACE("(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class);
io->u.Status = STATUS_SUCCESS;
switch (class)
{
case FileBasicInformation:
if (len >= sizeof(FILE_BASIC_INFORMATION))
{
struct stat st;
const FILE_BASIC_INFORMATION *info = ptr;
LARGE_INTEGER mtime, atime;
if ((io->u.Status = unix_funcs->server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart;
atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
if (atime.QuadPart || mtime.QuadPart)
io->u.Status = set_file_times( fd, &mtime, &atime );
if (io->u.Status == STATUS_SUCCESS && info->FileAttributes)
{
if (fstat( fd, &st ) == -1) io->u.Status = FILE_GetNtStatus();
else
{
if (info->FileAttributes & FILE_ATTRIBUTE_READONLY)
{
if (S_ISDIR( st.st_mode))
WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
else
st.st_mode &= ~0222; /* clear write permission bits */
}
else
{
/* add write permission only where we already have read permission */
st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~FILE_umask);
}
if (fchmod( fd, st.st_mode ) == -1) io->u.Status = FILE_GetNtStatus();
}
}
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FilePositionInformation:
if (len >= sizeof(FILE_POSITION_INFORMATION))
{
const FILE_POSITION_INFORMATION *info = ptr;
if ((io->u.Status = unix_funcs->server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
io->u.Status = FILE_GetNtStatus();
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileEndOfFileInformation:
if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
{
struct stat st;
const FILE_END_OF_FILE_INFORMATION *info = ptr;
if ((io->u.Status = unix_funcs->server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
/* first try normal truncate */
if (ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
/* now check for the need to extend the file */
if (fstat( fd, &st ) != -1 && (off_t)info->EndOfFile.QuadPart > st.st_size)
{
static const char zero;
/* extend the file one byte beyond the requested size and then truncate it */
/* this should work around ftruncate implementations that can't extend files */
if (pwrite( fd, &zero, 1, (off_t)info->EndOfFile.QuadPart ) != -1 &&
ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
}
io->u.Status = FILE_GetNtStatus();
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FilePipeInformation:
if (len >= sizeof(FILE_PIPE_INFORMATION))
{
FILE_PIPE_INFORMATION *info = ptr;
if ((info->CompletionMode | info->ReadMode) & ~1)
{
io->u.Status = STATUS_INVALID_PARAMETER;
break;
}
SERVER_START_REQ( set_named_pipe_info )
{
req->handle = wine_server_obj_handle( handle );
req->flags = (info->CompletionMode ? NAMED_PIPE_NONBLOCKING_MODE : 0) |
(info->ReadMode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0);
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileMailslotSetInformation:
{
FILE_MAILSLOT_SET_INFORMATION *info = ptr;
SERVER_START_REQ( set_mailslot_info )
{
req->handle = wine_server_obj_handle( handle );
req->flags = MAILSLOT_SET_READ_TIMEOUT;
req->read_timeout = info->ReadTimeout.QuadPart;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
}
break;
case FileCompletionInformation:
if (len >= sizeof(FILE_COMPLETION_INFORMATION))
{
FILE_COMPLETION_INFORMATION *info = ptr;
SERVER_START_REQ( set_completion_info )
{
req->handle = wine_server_obj_handle( handle );
req->chandle = wine_server_obj_handle( info->CompletionPort );
req->ckey = info->CompletionKey;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
} else
io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileIoCompletionNotificationInformation:
if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION))
{
FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr;
if (info->Flags & FILE_SKIP_SET_USER_EVENT_ON_FAST_IO)
FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
SERVER_START_REQ( set_fd_completion_mode )
{
req->handle = wine_server_obj_handle( handle );
req->flags = info->Flags;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
} else
io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
break;
case FileIoPriorityHintInformation:
if (len >= sizeof(FILE_IO_PRIORITY_HINT_INFO))
{
FILE_IO_PRIORITY_HINT_INFO *info = ptr;
if (info->PriorityHint < MaximumIoPriorityHintType)
TRACE( "ignoring FileIoPriorityHintInformation %u\n", info->PriorityHint );
else
io->u.Status = STATUS_INVALID_PARAMETER;
}
else io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
break;
case FileAllInformation:
io->u.Status = STATUS_INVALID_INFO_CLASS;
break;
case FileValidDataLengthInformation:
if (len >= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION))
{
struct stat st;
const FILE_VALID_DATA_LENGTH_INFORMATION *info = ptr;
if ((io->u.Status = unix_funcs->server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
if (fstat( fd, &st ) == -1) io->u.Status = FILE_GetNtStatus();
else if (info->ValidDataLength.QuadPart <= 0 || (off_t)info->ValidDataLength.QuadPart > st.st_size)
io->u.Status = STATUS_INVALID_PARAMETER;
else
{
#ifdef HAVE_FALLOCATE
if (fallocate( fd, 0, 0, (off_t)info->ValidDataLength.QuadPart ) == -1)
{
NTSTATUS status = FILE_GetNtStatus();
if (status == STATUS_NOT_SUPPORTED) WARN( "fallocate not supported on this filesystem\n" );
else io->u.Status = status;
}
#else
FIXME( "setting valid data length not supported\n" );
#endif
}
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileDispositionInformation:
if (len >= sizeof(FILE_DISPOSITION_INFORMATION))
{
FILE_DISPOSITION_INFORMATION *info = ptr;
SERVER_START_REQ( set_fd_disp_info )
{
req->handle = wine_server_obj_handle( handle );
req->unlink = info->DoDeleteFile;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
} else
io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileRenameInformation:
if (len >= sizeof(FILE_RENAME_INFORMATION))
{
FILE_RENAME_INFORMATION *info = ptr;
UNICODE_STRING name_str;
OBJECT_ATTRIBUTES attr;
ANSI_STRING unix_name;
name_str.Buffer = info->FileName;
name_str.Length = info->FileNameLength;
name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
attr.Length = sizeof(attr);
attr.ObjectName = &name_str;
attr.RootDirectory = info->RootDirectory;
attr.Attributes = OBJ_CASE_INSENSITIVE;
io->u.Status = unix_funcs->nt_to_unix_file_name_attr( &attr, &unix_name, FILE_OPEN_IF );
if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
break;
SERVER_START_REQ( set_fd_name_info )
{
req->handle = wine_server_obj_handle( handle );
req->rootdir = wine_server_obj_handle( attr.RootDirectory );
req->link = FALSE;
req->replace = info->ReplaceIfExists;
wine_server_add_data( req, unix_name.Buffer, unix_name.Length );
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
RtlFreeAnsiString( &unix_name );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileLinkInformation:
if (len >= sizeof(FILE_LINK_INFORMATION))
{
FILE_LINK_INFORMATION *info = ptr;
UNICODE_STRING name_str;
OBJECT_ATTRIBUTES attr;
ANSI_STRING unix_name;
name_str.Buffer = info->FileName;
name_str.Length = info->FileNameLength;
name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
attr.Length = sizeof(attr);
attr.ObjectName = &name_str;
attr.RootDirectory = info->RootDirectory;
attr.Attributes = OBJ_CASE_INSENSITIVE;
io->u.Status = unix_funcs->nt_to_unix_file_name_attr( &attr, &unix_name, FILE_OPEN_IF );
if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
break;
SERVER_START_REQ( set_fd_name_info )
{
req->handle = wine_server_obj_handle( handle );
req->rootdir = wine_server_obj_handle( attr.RootDirectory );
req->link = TRUE;
req->replace = info->ReplaceIfExists;
wine_server_add_data( req, unix_name.Buffer, unix_name.Length );
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
RtlFreeAnsiString( &unix_name );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
default:
FIXME("Unsupported class (%d)\n", class);
io->u.Status = STATUS_NOT_IMPLEMENTED;
break;
}
io->Information = 0;
return io->u.Status;
return unix_funcs->NtSetInformationFile( handle, io, ptr, len, class );
}
......
......@@ -4384,10 +4384,6 @@ void __wine_process_init(void)
init_user_process_params( info_size );
params = peb->ProcessParameters;
/* retrieve current umask */
FILE_umask = umask(0777);
umask( FILE_umask );
load_global_options();
version_init();
......
......@@ -203,7 +203,6 @@ static inline int get_unix_exit_code( NTSTATUS status )
return status;
}
extern mode_t FILE_umask DECLSPEC_HIDDEN;
extern SYSTEM_CPU_INFORMATION cpu_info DECLSPEC_HIDDEN;
#define HASH_STRING_ALGORITHM_DEFAULT 0
......
......@@ -46,6 +46,12 @@
#ifdef HAVE_SYS_SYSCALL_H
# include <sys/syscall.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_ATTR_H
#include <sys/attr.h>
#endif
......@@ -103,8 +109,12 @@
#define NONAMELESSUNION
#include "windef.h"
#include "winnt.h"
#include "winioctl.h"
#include "winternl.h"
#include "ddk/ntddk.h"
#include "ddk/wdm.h"
#define WINE_MOUNTMGR_EXTENSIONS
#include "ddk/mountmgr.h"
#include "wine/server.h"
#include "wine/list.h"
#include "wine/debug.h"
......@@ -213,6 +223,7 @@ static struct dir_data **dir_data_cache;
static unsigned int dir_data_cache_size;
static BOOL show_dot_files;
static mode_t start_umask;
/* at some point we may want to allow Winelib apps to set this */
static const BOOL is_case_sensitive = FALSE;
......@@ -1446,6 +1457,30 @@ static inline ULONG get_file_attributes( const struct stat *st )
}
static BOOL fd_is_mount_point( int fd, const struct stat *st )
{
struct stat parent;
return S_ISDIR( st->st_mode ) && !fstatat( fd, "..", &parent, 0 )
&& (parent.st_dev != st->st_dev || parent.st_ino == st->st_ino);
}
/* get the stat info and file attributes for a file (by file descriptor) */
static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr )
{
int ret;
*attr = 0;
ret = fstat( fd, st );
if (ret == -1) return ret;
*attr |= get_file_attributes( st );
/* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st ))
*attr |= FILE_ATTRIBUTE_REPARSE_POINT;
return ret;
}
/* get the stat info and file attributes for a file (by name) */
static int get_file_info( const char *path, struct stat *st, ULONG *attr )
{
......@@ -1480,6 +1515,105 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr )
}
#if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
static int futimens( int fd, const struct timespec spec[2] )
{
return syscall( __NR_utimensat, fd, NULL, spec, 0 );
}
#define HAVE_FUTIMENS
#endif /* __ANDROID__ */
#ifndef UTIME_OMIT
#define UTIME_OMIT ((1 << 30) - 2)
#endif
static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
const LARGE_INTEGER *atime, NTSTATUS *status )
{
#ifdef HAVE_FUTIMENS
struct timespec tv[2];
tv[0].tv_sec = tv[1].tv_sec = 0;
tv[0].tv_nsec = tv[1].tv_nsec = UTIME_OMIT;
if (atime->QuadPart)
{
tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[0].tv_nsec = (atime->QuadPart % 10000000) * 100;
}
if (mtime->QuadPart)
{
tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[1].tv_nsec = (mtime->QuadPart % 10000000) * 100;
}
#ifdef __APPLE__
if (!&futimens) return FALSE;
#endif
if (futimens( fd, tv ) == -1) *status = errno_to_status( errno );
else *status = STATUS_SUCCESS;
return TRUE;
#else
return FALSE;
#endif
}
static NTSTATUS set_file_times( int fd, const LARGE_INTEGER *mtime, const LARGE_INTEGER *atime )
{
NTSTATUS status = STATUS_SUCCESS;
#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
struct timeval tv[2];
struct stat st;
#endif
if (set_file_times_precise( fd, mtime, atime, &status ))
return status;
#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMESAT)
if (!atime->QuadPart || !mtime->QuadPart)
{
tv[0].tv_sec = tv[0].tv_usec = 0;
tv[1].tv_sec = tv[1].tv_usec = 0;
if (!fstat( fd, &st ))
{
tv[0].tv_sec = st.st_atime;
tv[1].tv_sec = st.st_mtime;
#ifdef HAVE_STRUCT_STAT_ST_ATIM
tv[0].tv_usec = st.st_atim.tv_nsec / 1000;
#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
tv[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
#endif
#ifdef HAVE_STRUCT_STAT_ST_MTIM
tv[1].tv_usec = st.st_mtim.tv_nsec / 1000;
#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
tv[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
#endif
}
}
if (atime->QuadPart)
{
tv[0].tv_sec = atime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[0].tv_usec = (atime->QuadPart % 10000000) / 10;
}
if (mtime->QuadPart)
{
tv[1].tv_sec = mtime->QuadPart / 10000000 - SECS_1601_TO_1970;
tv[1].tv_usec = (mtime->QuadPart % 10000000) / 10;
}
#ifdef HAVE_FUTIMES
if (futimes( fd, tv ) == -1) status = errno_to_status( errno );
#elif defined(HAVE_FUTIMESAT)
if (futimesat( fd, NULL, tv ) == -1) status = FILE_GetNtStatus();
#endif
#else /* HAVE_FUTIMES || HAVE_FUTIMESAT */
FIXME( "setting file times not supported\n" );
status = STATUS_NOT_IMPLEMENTED;
#endif
return status;
}
static inline void get_file_times( const struct stat *st, LARGE_INTEGER *mtime, LARGE_INTEGER *ctime,
LARGE_INTEGER *atime, LARGE_INTEGER *creation )
{
......@@ -1623,6 +1757,258 @@ static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr,
}
static NTSTATUS server_get_unix_name( HANDLE handle, ANSI_STRING *unix_name )
{
data_size_t size = 1024;
NTSTATUS ret;
char *name;
for (;;)
{
name = RtlAllocateHeap( GetProcessHeap(), 0, size + 1 );
if (!name) return STATUS_NO_MEMORY;
unix_name->MaximumLength = size + 1;
SERVER_START_REQ( get_handle_unix_name )
{
req->handle = wine_server_obj_handle( handle );
wine_server_set_reply( req, name, size );
ret = wine_server_call( req );
size = reply->name_len;
}
SERVER_END_REQ;
if (!ret)
{
name[size] = 0;
unix_name->Buffer = name;
unix_name->Length = size;
break;
}
RtlFreeHeap( GetProcessHeap(), 0, name );
if (ret != STATUS_BUFFER_OVERFLOW) break;
}
return ret;
}
static NTSTATUS fill_name_info( const ANSI_STRING *unix_name, FILE_NAME_INFORMATION *info, LONG *name_len )
{
UNICODE_STRING nt_name;
NTSTATUS status;
if (!(status = wine_unix_to_nt_file_name( unix_name, &nt_name )))
{
const WCHAR *ptr = nt_name.Buffer;
const WCHAR *end = ptr + (nt_name.Length / sizeof(WCHAR));
/* Skip the volume mount point. */
while (ptr != end && *ptr == '\\') ++ptr;
while (ptr != end && *ptr != '\\') ++ptr;
while (ptr != end && *ptr == '\\') ++ptr;
while (ptr != end && *ptr != '\\') ++ptr;
info->FileNameLength = (end - ptr) * sizeof(WCHAR);
if (*name_len < info->FileNameLength) status = STATUS_BUFFER_OVERFLOW;
else *name_len = info->FileNameLength;
memcpy( info->FileName, ptr, *name_len );
RtlFreeUnicodeString( &nt_name );
}
return status;
}
static NTSTATUS server_get_file_info( HANDLE handle, IO_STATUS_BLOCK *io, void *buffer,
ULONG length, FILE_INFORMATION_CLASS info_class )
{
SERVER_START_REQ( get_file_info )
{
req->handle = wine_server_obj_handle( handle );
req->info_class = info_class;
wine_server_set_reply( req, buffer, length );
io->u.Status = wine_server_call( req );
io->Information = wine_server_reply_size( reply );
}
SERVER_END_REQ;
if (io->u.Status == STATUS_NOT_IMPLEMENTED)
FIXME( "Unsupported info class %x\n", info_class );
return io->u.Status;
}
/* retrieve device/inode number for all the drives */
static unsigned int get_drives_info( struct file_identity info[MAX_DOS_DRIVES] )
{
static struct file_identity cache[MAX_DOS_DRIVES];
static time_t last_update;
static unsigned int nb_drives;
unsigned int ret;
time_t now = time(NULL);
RtlEnterCriticalSection( &dir_section );
if (now != last_update)
{
char *buffer, *p;
struct stat st;
unsigned int i;
if ((buffer = RtlAllocateHeap( GetProcessHeap(), 0,
strlen(config_dir) + sizeof("/dosdevices/a:") )))
{
strcpy( buffer, config_dir );
strcat( buffer, "/dosdevices/a:" );
p = buffer + strlen(buffer) - 2;
for (i = nb_drives = 0; i < MAX_DOS_DRIVES; i++)
{
*p = 'a' + i;
if (!stat( buffer, &st ))
{
cache[i].dev = st.st_dev;
cache[i].ino = st.st_ino;
nb_drives++;
}
else
{
cache[i].dev = 0;
cache[i].ino = 0;
}
}
RtlFreeHeap( GetProcessHeap(), 0, buffer );
}
last_update = now;
}
memcpy( info, cache, sizeof(cache) );
ret = nb_drives;
RtlLeaveCriticalSection( &dir_section );
return ret;
}
/* Find a DOS device which can act as the root of "path".
* Similar to find_drive_root(), but returns -1 instead of crossing volumes. */
static int find_dos_device( const char *path )
{
int len = strlen(path);
int drive;
char *buffer;
struct stat st;
struct file_identity info[MAX_DOS_DRIVES];
dev_t dev_id;
if (!get_drives_info( info )) return -1;
if (stat( path, &st ) < 0) return -1;
dev_id = st.st_dev;
/* strip off trailing slashes */
while (len > 1 && path[len - 1] == '/') len--;
/* make a copy of the path */
if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, len + 1 ))) return -1;
memcpy( buffer, path, len );
buffer[len] = 0;
for (;;)
{
if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
{
if (st.st_dev != dev_id) break;
for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
{
if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
{
if (len == 1) len = 0; /* preserve root slash in returned path */
TRACE( "%s -> drive %c:, root=%s, name=%s\n",
debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len));
RtlFreeHeap( GetProcessHeap(), 0, buffer );
return drive;
}
}
}
if (len <= 1) break; /* reached root */
while (path[len - 1] != '/') len--;
while (path[len - 1] == '/') len--;
buffer[len] = 0;
}
RtlFreeHeap( GetProcessHeap(), 0, buffer );
return -1;
}
static struct mountmgr_unix_drive *get_mountmgr_fs_info( HANDLE handle, int fd )
{
struct mountmgr_unix_drive *drive;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING string;
ANSI_STRING unix_name;
IO_STATUS_BLOCK io;
HANDLE mountmgr;
NTSTATUS status;
int letter;
if (server_get_unix_name( handle, &unix_name ))
return NULL;
letter = find_dos_device( unix_name.Buffer );
RtlFreeAnsiString( &unix_name );
if (!(drive = RtlAllocateHeap( GetProcessHeap(), 0, 1024 )))
return NULL;
if (letter == -1)
{
struct stat st;
if (fstat( fd, &st ) == -1)
{
RtlFreeHeap( GetProcessHeap(), 0, drive );
return NULL;
}
drive->unix_dev = st.st_dev;
drive->letter = 0;
}
else
drive->letter = 'a' + letter;
RtlInitUnicodeString( &string, MOUNTMGR_DEVICE_NAME );
InitializeObjectAttributes( &attr, &string, 0, NULL, NULL );
if (NtOpenFile( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr, &io,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT ))
{
RtlFreeHeap( GetProcessHeap(), 0, drive );
return NULL;
}
status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE,
drive, sizeof(*drive), drive, 1024 );
if (status == STATUS_BUFFER_OVERFLOW)
{
if (!(drive = RtlReAllocateHeap( GetProcessHeap(), 0, drive, drive->size )))
{
RtlFreeHeap( GetProcessHeap(), 0, drive );
NtClose( mountmgr );
return NULL;
}
status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE,
drive, sizeof(*drive), drive, drive->size );
}
NtClose( mountmgr );
if (status)
{
WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status);
RtlFreeHeap( GetProcessHeap(), 0, drive );
return NULL;
}
return drive;
}
/***********************************************************************
* get_dir_data_entry
*
......@@ -2399,6 +2785,9 @@ void init_files(void)
#ifdef linux
ignore_file( "/sys" );
#endif
/* retrieve initial umask */
start_umask = umask( 0777 );
umask( start_umask );
}
......@@ -2558,7 +2947,7 @@ static NTSTATUS find_file_id( ANSI_STRING *unix_name, ULONGLONG file_id, dev_t d
*
* Lookup a file from its file id instead of its name.
*/
NTSTATUS CDECL file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name )
static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name )
{
enum server_fd_type type;
int old_cwd, root_fd, needs_close;
......@@ -2748,8 +3137,8 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
/******************************************************************************
* nt_to_unix_file_name_attr
*/
NTSTATUS CDECL nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name_ret,
UINT disposition )
static NTSTATUS nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name_ret,
UINT disposition )
{
static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
enum server_fd_type type;
......@@ -3282,3 +3671,614 @@ NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC
else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
return status;
}
/******************************************************************************
* NtQueryInformationFile (NTDLL.@)
*/
NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
void *ptr, LONG len, FILE_INFORMATION_CLASS class )
{
static const size_t info_sizes[] =
{
0,
sizeof(FILE_DIRECTORY_INFORMATION), /* FileDirectoryInformation */
sizeof(FILE_FULL_DIRECTORY_INFORMATION), /* FileFullDirectoryInformation */
sizeof(FILE_BOTH_DIRECTORY_INFORMATION), /* FileBothDirectoryInformation */
sizeof(FILE_BASIC_INFORMATION), /* FileBasicInformation */
sizeof(FILE_STANDARD_INFORMATION), /* FileStandardInformation */
sizeof(FILE_INTERNAL_INFORMATION), /* FileInternalInformation */
sizeof(FILE_EA_INFORMATION), /* FileEaInformation */
0, /* FileAccessInformation */
sizeof(FILE_NAME_INFORMATION), /* FileNameInformation */
sizeof(FILE_RENAME_INFORMATION)-sizeof(WCHAR), /* FileRenameInformation */
0, /* FileLinkInformation */
sizeof(FILE_NAMES_INFORMATION)-sizeof(WCHAR), /* FileNamesInformation */
sizeof(FILE_DISPOSITION_INFORMATION), /* FileDispositionInformation */
sizeof(FILE_POSITION_INFORMATION), /* FilePositionInformation */
sizeof(FILE_FULL_EA_INFORMATION), /* FileFullEaInformation */
0, /* FileModeInformation */
sizeof(FILE_ALIGNMENT_INFORMATION), /* FileAlignmentInformation */
sizeof(FILE_ALL_INFORMATION), /* FileAllInformation */
sizeof(FILE_ALLOCATION_INFORMATION), /* FileAllocationInformation */
sizeof(FILE_END_OF_FILE_INFORMATION), /* FileEndOfFileInformation */
0, /* FileAlternateNameInformation */
sizeof(FILE_STREAM_INFORMATION)-sizeof(WCHAR), /* FileStreamInformation */
sizeof(FILE_PIPE_INFORMATION), /* FilePipeInformation */
sizeof(FILE_PIPE_LOCAL_INFORMATION), /* FilePipeLocalInformation */
0, /* FilePipeRemoteInformation */
sizeof(FILE_MAILSLOT_QUERY_INFORMATION), /* FileMailslotQueryInformation */
0, /* FileMailslotSetInformation */
0, /* FileCompressionInformation */
0, /* FileObjectIdInformation */
0, /* FileCompletionInformation */
0, /* FileMoveClusterInformation */
0, /* FileQuotaInformation */
0, /* FileReparsePointInformation */
sizeof(FILE_NETWORK_OPEN_INFORMATION), /* FileNetworkOpenInformation */
sizeof(FILE_ATTRIBUTE_TAG_INFORMATION), /* FileAttributeTagInformation */
0, /* FileTrackingInformation */
0, /* FileIdBothDirectoryInformation */
0, /* FileIdFullDirectoryInformation */
0, /* FileValidDataLengthInformation */
0, /* FileShortNameInformation */
0, /* FileIoCompletionNotificationInformation, */
0, /* FileIoStatusBlockRangeInformation */
0, /* FileIoPriorityHintInformation */
0, /* FileSfioReserveInformation */
0, /* FileSfioVolumeInformation */
0, /* FileHardLinkInformation */
0, /* FileProcessIdsUsingFileInformation */
0, /* FileNormalizedNameInformation */
0, /* FileNetworkPhysicalNameInformation */
0, /* FileIdGlobalTxDirectoryInformation */
0, /* FileIsRemoteDeviceInformation */
0, /* FileAttributeCacheInformation */
0, /* FileNumaNodeInformation */
0, /* FileStandardLinkInformation */
0, /* FileRemoteProtocolInformation */
0, /* FileRenameInformationBypassAccessCheck */
0, /* FileLinkInformationBypassAccessCheck */
0, /* FileVolumeNameInformation */
sizeof(FILE_ID_INFORMATION), /* FileIdInformation */
0, /* FileIdExtdDirectoryInformation */
0, /* FileReplaceCompletionInformation */
0, /* FileHardLinkFullIdInformation */
0, /* FileIdExtdBothDirectoryInformation */
};
struct stat st;
int fd, needs_close = FALSE;
ULONG attr;
unsigned int options;
TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class);
io->Information = 0;
if (class <= 0 || class >= FileMaximumInformation)
return io->u.Status = STATUS_INVALID_INFO_CLASS;
if (!info_sizes[class])
return server_get_file_info( handle, io, ptr, len, class );
if (len < info_sizes[class])
return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options )))
{
if (io->u.Status != STATUS_BAD_DEVICE_TYPE) return io->u.Status;
return server_get_file_info( handle, io, ptr, len, class );
}
switch (class)
{
case FileBasicInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1)
io->u.Status = errno_to_status( errno );
else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
io->u.Status = STATUS_INVALID_INFO_CLASS;
else
fill_file_info( &st, attr, ptr, class );
break;
case FileStandardInformation:
{
FILE_STANDARD_INFORMATION *info = ptr;
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
else
{
fill_file_info( &st, attr, info, class );
info->DeletePending = FALSE; /* FIXME */
}
}
break;
case FilePositionInformation:
{
FILE_POSITION_INFORMATION *info = ptr;
off_t res = lseek( fd, 0, SEEK_CUR );
if (res == (off_t)-1) io->u.Status = errno_to_status( errno );
else info->CurrentByteOffset.QuadPart = res;
}
break;
case FileInternalInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
else fill_file_info( &st, attr, ptr, class );
break;
case FileEaInformation:
{
FILE_EA_INFORMATION *info = ptr;
info->EaSize = 0;
}
break;
case FileEndOfFileInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
else fill_file_info( &st, attr, ptr, class );
break;
case FileAllInformation:
{
FILE_ALL_INFORMATION *info = ptr;
ANSI_STRING unix_name;
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
io->u.Status = STATUS_INVALID_INFO_CLASS;
else if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
{
LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
fill_file_info( &st, attr, info, FileAllInformation );
info->StandardInformation.DeletePending = FALSE; /* FIXME */
info->EaInformation.EaSize = 0;
info->AccessInformation.AccessFlags = 0; /* FIXME */
info->PositionInformation.CurrentByteOffset.QuadPart = lseek( fd, 0, SEEK_CUR );
info->ModeInformation.Mode = 0; /* FIXME */
info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
io->u.Status = fill_name_info( &unix_name, &info->NameInformation, &name_len );
RtlFreeAnsiString( &unix_name );
io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len;
}
}
break;
case FileMailslotQueryInformation:
{
FILE_MAILSLOT_QUERY_INFORMATION *info = ptr;
SERVER_START_REQ( set_mailslot_info )
{
req->handle = wine_server_obj_handle( handle );
req->flags = 0;
io->u.Status = wine_server_call( req );
if( io->u.Status == STATUS_SUCCESS )
{
info->MaximumMessageSize = reply->max_msgsize;
info->MailslotQuota = 0;
info->NextMessageSize = 0;
info->MessagesAvailable = 0;
info->ReadTimeout.QuadPart = reply->read_timeout;
}
}
SERVER_END_REQ;
if (!io->u.Status)
{
char *tmpbuf;
ULONG size = info->MaximumMessageSize ? info->MaximumMessageSize : 0x10000;
if (size > 0x10000) size = 0x10000;
if ((tmpbuf = RtlAllocateHeap( GetProcessHeap(), 0, size )))
{
if (!server_get_unix_fd( handle, FILE_READ_DATA, &fd, &needs_close, NULL, NULL ))
{
int res = recv( fd, tmpbuf, size, MSG_PEEK );
info->MessagesAvailable = (res > 0);
info->NextMessageSize = (res >= 0) ? res : MAILSLOT_NO_MESSAGE;
if (needs_close) close( fd );
}
RtlFreeHeap( GetProcessHeap(), 0, tmpbuf );
}
}
}
break;
case FileNameInformation:
{
FILE_NAME_INFORMATION *info = ptr;
ANSI_STRING unix_name;
if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
{
LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
io->u.Status = fill_name_info( &unix_name, info, &name_len );
RtlFreeAnsiString( &unix_name );
io->Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + name_len;
}
}
break;
case FileNetworkOpenInformation:
{
FILE_NETWORK_OPEN_INFORMATION *info = ptr;
ANSI_STRING unix_name;
if (!(io->u.Status = server_get_unix_name( handle, &unix_name )))
{
ULONG attributes;
struct stat st;
if (get_file_info( unix_name.Buffer, &st, &attributes ) == -1)
io->u.Status = errno_to_status( errno );
else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
io->u.Status = STATUS_INVALID_INFO_CLASS;
else
{
FILE_BASIC_INFORMATION basic;
FILE_STANDARD_INFORMATION std;
fill_file_info( &st, attributes, &basic, FileBasicInformation );
fill_file_info( &st, attributes, &std, FileStandardInformation );
info->CreationTime = basic.CreationTime;
info->LastAccessTime = basic.LastAccessTime;
info->LastWriteTime = basic.LastWriteTime;
info->ChangeTime = basic.ChangeTime;
info->AllocationSize = std.AllocationSize;
info->EndOfFile = std.EndOfFile;
info->FileAttributes = basic.FileAttributes;
}
RtlFreeAnsiString( &unix_name );
}
}
break;
case FileIdInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
else
{
struct mountmgr_unix_drive *drive;
FILE_ID_INFORMATION *info = ptr;
info->VolumeSerialNumber = 0;
if ((drive = get_mountmgr_fs_info( handle, fd )))
{
info->VolumeSerialNumber = drive->serial;
RtlFreeHeap( GetProcessHeap(), 0, drive );
}
memset( &info->FileId, 0, sizeof(info->FileId) );
*(ULONGLONG *)&info->FileId = st.st_ino;
}
break;
case FileAttributeTagInformation:
if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = errno_to_status( errno );
else
{
FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
info->FileAttributes = attr;
info->ReparseTag = 0; /* FIXME */
if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
}
break;
default:
FIXME("Unsupported class (%d)\n", class);
io->u.Status = STATUS_NOT_IMPLEMENTED;
break;
}
if (needs_close) close( fd );
if (io->u.Status == STATUS_SUCCESS && !io->Information) io->Information = info_sizes[class];
return io->u.Status;
}
/******************************************************************************
* NtSetInformationFile (NTDLL.@)
*/
NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
void *ptr, ULONG len, FILE_INFORMATION_CLASS class )
{
int fd, needs_close;
TRACE( "(%p,%p,%p,0x%08x,0x%08x)\n", handle, io, ptr, len, class );
io->u.Status = STATUS_SUCCESS;
switch (class)
{
case FileBasicInformation:
if (len >= sizeof(FILE_BASIC_INFORMATION))
{
struct stat st;
const FILE_BASIC_INFORMATION *info = ptr;
LARGE_INTEGER mtime, atime;
if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart;
atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
if (atime.QuadPart || mtime.QuadPart)
io->u.Status = set_file_times( fd, &mtime, &atime );
if (io->u.Status == STATUS_SUCCESS && info->FileAttributes)
{
if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
else
{
if (info->FileAttributes & FILE_ATTRIBUTE_READONLY)
{
if (S_ISDIR( st.st_mode))
WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
else
st.st_mode &= ~0222; /* clear write permission bits */
}
else
{
/* add write permission only where we already have read permission */
st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~start_umask);
}
if (fchmod( fd, st.st_mode ) == -1) io->u.Status = errno_to_status( errno );
}
}
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FilePositionInformation:
if (len >= sizeof(FILE_POSITION_INFORMATION))
{
const FILE_POSITION_INFORMATION *info = ptr;
if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
if (lseek( fd, info->CurrentByteOffset.QuadPart, SEEK_SET ) == (off_t)-1)
io->u.Status = errno_to_status( errno );
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileEndOfFileInformation:
if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
{
struct stat st;
const FILE_END_OF_FILE_INFORMATION *info = ptr;
if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
/* first try normal truncate */
if (ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
/* now check for the need to extend the file */
if (fstat( fd, &st ) != -1 && (off_t)info->EndOfFile.QuadPart > st.st_size)
{
static const char zero;
/* extend the file one byte beyond the requested size and then truncate it */
/* this should work around ftruncate implementations that can't extend files */
if (pwrite( fd, &zero, 1, (off_t)info->EndOfFile.QuadPart ) != -1 &&
ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
}
io->u.Status = errno_to_status( errno );
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FilePipeInformation:
if (len >= sizeof(FILE_PIPE_INFORMATION))
{
FILE_PIPE_INFORMATION *info = ptr;
if ((info->CompletionMode | info->ReadMode) & ~1)
{
io->u.Status = STATUS_INVALID_PARAMETER;
break;
}
SERVER_START_REQ( set_named_pipe_info )
{
req->handle = wine_server_obj_handle( handle );
req->flags = (info->CompletionMode ? NAMED_PIPE_NONBLOCKING_MODE : 0) |
(info->ReadMode ? NAMED_PIPE_MESSAGE_STREAM_READ : 0);
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileMailslotSetInformation:
{
FILE_MAILSLOT_SET_INFORMATION *info = ptr;
SERVER_START_REQ( set_mailslot_info )
{
req->handle = wine_server_obj_handle( handle );
req->flags = MAILSLOT_SET_READ_TIMEOUT;
req->read_timeout = info->ReadTimeout.QuadPart;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
}
break;
case FileCompletionInformation:
if (len >= sizeof(FILE_COMPLETION_INFORMATION))
{
FILE_COMPLETION_INFORMATION *info = ptr;
SERVER_START_REQ( set_completion_info )
{
req->handle = wine_server_obj_handle( handle );
req->chandle = wine_server_obj_handle( info->CompletionPort );
req->ckey = info->CompletionKey;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
} else
io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileIoCompletionNotificationInformation:
if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION))
{
FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr;
if (info->Flags & FILE_SKIP_SET_USER_EVENT_ON_FAST_IO)
FIXME( "FILE_SKIP_SET_USER_EVENT_ON_FAST_IO not supported\n" );
SERVER_START_REQ( set_fd_completion_mode )
{
req->handle = wine_server_obj_handle( handle );
req->flags = info->Flags;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
} else
io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
break;
case FileIoPriorityHintInformation:
if (len >= sizeof(FILE_IO_PRIORITY_HINT_INFO))
{
FILE_IO_PRIORITY_HINT_INFO *info = ptr;
if (info->PriorityHint < MaximumIoPriorityHintType)
TRACE( "ignoring FileIoPriorityHintInformation %u\n", info->PriorityHint );
else
io->u.Status = STATUS_INVALID_PARAMETER;
}
else io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
break;
case FileAllInformation:
io->u.Status = STATUS_INVALID_INFO_CLASS;
break;
case FileValidDataLengthInformation:
if (len >= sizeof(FILE_VALID_DATA_LENGTH_INFORMATION))
{
struct stat st;
const FILE_VALID_DATA_LENGTH_INFORMATION *info = ptr;
if ((io->u.Status = server_get_unix_fd( handle, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL )))
return io->u.Status;
if (fstat( fd, &st ) == -1) io->u.Status = errno_to_status( errno );
else if (info->ValidDataLength.QuadPart <= 0 || (off_t)info->ValidDataLength.QuadPart > st.st_size)
io->u.Status = STATUS_INVALID_PARAMETER;
else
{
#ifdef HAVE_FALLOCATE
if (fallocate( fd, 0, 0, (off_t)info->ValidDataLength.QuadPart ) == -1)
{
NTSTATUS status = errno_to_status( errno );
if (status == STATUS_NOT_SUPPORTED) WARN( "fallocate not supported on this filesystem\n" );
else io->u.Status = status;
}
#else
FIXME( "setting valid data length not supported\n" );
#endif
}
if (needs_close) close( fd );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileDispositionInformation:
if (len >= sizeof(FILE_DISPOSITION_INFORMATION))
{
FILE_DISPOSITION_INFORMATION *info = ptr;
SERVER_START_REQ( set_fd_disp_info )
{
req->handle = wine_server_obj_handle( handle );
req->unlink = info->DoDeleteFile;
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
} else
io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileRenameInformation:
if (len >= sizeof(FILE_RENAME_INFORMATION))
{
FILE_RENAME_INFORMATION *info = ptr;
UNICODE_STRING name_str;
OBJECT_ATTRIBUTES attr;
ANSI_STRING unix_name;
name_str.Buffer = info->FileName;
name_str.Length = info->FileNameLength;
name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
attr.Length = sizeof(attr);
attr.ObjectName = &name_str;
attr.RootDirectory = info->RootDirectory;
attr.Attributes = OBJ_CASE_INSENSITIVE;
io->u.Status = nt_to_unix_file_name_attr( &attr, &unix_name, FILE_OPEN_IF );
if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
break;
SERVER_START_REQ( set_fd_name_info )
{
req->handle = wine_server_obj_handle( handle );
req->rootdir = wine_server_obj_handle( attr.RootDirectory );
req->link = FALSE;
req->replace = info->ReplaceIfExists;
wine_server_add_data( req, unix_name.Buffer, unix_name.Length );
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
RtlFreeAnsiString( &unix_name );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
case FileLinkInformation:
if (len >= sizeof(FILE_LINK_INFORMATION))
{
FILE_LINK_INFORMATION *info = ptr;
UNICODE_STRING name_str;
OBJECT_ATTRIBUTES attr;
ANSI_STRING unix_name;
name_str.Buffer = info->FileName;
name_str.Length = info->FileNameLength;
name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
attr.Length = sizeof(attr);
attr.ObjectName = &name_str;
attr.RootDirectory = info->RootDirectory;
attr.Attributes = OBJ_CASE_INSENSITIVE;
io->u.Status = nt_to_unix_file_name_attr( &attr, &unix_name, FILE_OPEN_IF );
if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
break;
SERVER_START_REQ( set_fd_name_info )
{
req->handle = wine_server_obj_handle( handle );
req->rootdir = wine_server_obj_handle( attr.RootDirectory );
req->link = TRUE;
req->replace = info->ReplaceIfExists;
wine_server_add_data( req, unix_name.Buffer, unix_name.Length );
io->u.Status = wine_server_call( req );
}
SERVER_END_REQ;
RtlFreeAnsiString( &unix_name );
}
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
default:
FIXME("Unsupported class (%d)\n", class);
io->u.Status = STATUS_NOT_IMPLEMENTED;
break;
}
io->Information = 0;
return io->u.Status;
}
......@@ -864,6 +864,7 @@ static struct unix_funcs unix_funcs =
NtQueryDirectoryFile,
NtQueryEvent,
NtQueryFullAttributesFile,
NtQueryInformationFile,
NtQueryInformationJobObject,
NtQueryIoCompletion,
NtQueryMutant,
......@@ -886,6 +887,7 @@ static struct unix_funcs unix_funcs =
NtResumeThread,
NtSetContextThread,
NtSetEvent,
NtSetInformationFile,
NtSetInformationJobObject,
NtSetIoCompletion,
NtSetLdtEntries,
......@@ -955,8 +957,6 @@ static struct unix_funcs unix_funcs =
server_handle_to_fd,
server_release_fd,
server_init_process_done,
file_id_to_unix_file_name,
nt_to_unix_file_name_attr,
nt_to_unix_file_name,
unmount_device,
set_show_dot_files,
......
......@@ -120,9 +120,6 @@ extern NTSTATUS CDECL exec_process( const UNICODE_STRING *cmdline, const pe_imag
extern NTSTATUS CDECL fork_and_exec( const char *unix_name, const char *unix_dir,
const RTL_USER_PROCESS_PARAMETERS *params ) DECLSPEC_HIDDEN;
extern NTSTATUS CDECL file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name ) DECLSPEC_HIDDEN;
extern NTSTATUS CDECL nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name_ret,
UINT disposition ) DECLSPEC_HIDDEN;
extern NTSTATUS CDECL nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
UINT disposition, BOOLEAN check_case ) DECLSPEC_HIDDEN;
extern NTSTATUS CDECL unmount_device( HANDLE handle ) DECLSPEC_HIDDEN;
......
......@@ -28,7 +28,7 @@ struct ldt_copy;
struct msghdr;
/* increment this when you change the function table */
#define NTDLL_UNIXLIB_VERSION 45
#define NTDLL_UNIXLIB_VERSION 46
struct unix_funcs
{
......@@ -129,6 +129,8 @@ struct unix_funcs
void *info, ULONG len, ULONG *ret_len );
NTSTATUS (WINAPI *NtQueryFullAttributesFile)( const OBJECT_ATTRIBUTES *attr,
FILE_NETWORK_OPEN_INFORMATION *info );
NTSTATUS (WINAPI *NtQueryInformationFile)( HANDLE hFile, IO_STATUS_BLOCK *io,
void *ptr, LONG len, FILE_INFORMATION_CLASS class );
NTSTATUS (WINAPI *NtQueryInformationJobObject)( HANDLE handle, JOBOBJECTINFOCLASS class,
void *info, ULONG len, ULONG *ret_len );
NTSTATUS (WINAPI *NtQueryIoCompletion)( HANDLE handle, IO_COMPLETION_INFORMATION_CLASS class,
......@@ -165,6 +167,8 @@ struct unix_funcs
NTSTATUS (WINAPI *NtResumeThread)( HANDLE handle, ULONG *count );
NTSTATUS (WINAPI *NtSetContextThread)( HANDLE handle, const CONTEXT *context );
NTSTATUS (WINAPI *NtSetEvent)( HANDLE handle, LONG *prev_state );
NTSTATUS (WINAPI *NtSetInformationFile)( HANDLE handle, IO_STATUS_BLOCK *io,
void *ptr, ULONG len, FILE_INFORMATION_CLASS class );
NTSTATUS (WINAPI *NtSetInformationJobObject)( HANDLE handle, JOBOBJECTINFOCLASS class,
void *info, ULONG len );
NTSTATUS (WINAPI *NtSetIoCompletion)( HANDLE handle, ULONG_PTR key, ULONG_PTR value,
......@@ -273,10 +277,6 @@ struct unix_funcs
void (CDECL *server_init_process_done)( void *relay );
/* file functions */
NTSTATUS (CDECL *file_id_to_unix_file_name)( const OBJECT_ATTRIBUTES *attr,
ANSI_STRING *unix_name );
NTSTATUS (CDECL *nt_to_unix_file_name_attr)( const OBJECT_ATTRIBUTES *attr,
ANSI_STRING *unix_name_ret, UINT disposition );
NTSTATUS (CDECL *nt_to_unix_file_name)( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
UINT disposition, BOOLEAN check_case );
NTSTATUS (CDECL *unmount_device)( HANDLE handle );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment