Implement __wasi_fd_fdstat_get for Windows.

This commit fully implements `__wasi_fd_fdstat_get` on Windows so that
the descriptor flags can be determined.

It does this by calling into `NtQueryInformationFile` (safe to call from
user mode) to get the open mode and access of the underlying OS handle.

`NtQueryInformationFile` isn't included in the `winapi` crate, so it is
manually being linked against.

This commit also fixes several bugs on Windows:

* Ignore `__WASI_FDFLAG_NONBLOCK` by not setting `FILE_FLAG_OVERLAPPED`
  on file handles (the POSIX behavior for `O_NONBLOCK` on files).
* Use `FILE_FLAG_WRITE_THROUGH` for the `__WASI_FDFLAG_?SYNC` flags.
* `__WASI_FDFLAG_APPEND` should disallow `FILE_WRITE_DATA` access to
  force append-only on write operations.
* Use `GENERIC_READ` and `GENERIC_WRITE` access flags.  The
  latter is required when opening a file for truncation.
This commit is contained in:
Peter Huene
2019-11-12 16:41:54 -08:00
committed by Jakub Konka
parent 3e7bc745a2
commit 0cf54ffeba
11 changed files with 245 additions and 172 deletions

View File

@@ -1,4 +1,9 @@
#![allow(non_camel_case_types)]
use crate::ntdll::{
NtQueryInformationFile, RtlNtStatusToDosError, FILE_ACCESS_INFORMATION, FILE_INFORMATION_CLASS,
FILE_MODE_INFORMATION, IO_STATUS_BLOCK,
};
use crate::{winerror, Result};
use bitflags::bitflags;
use cvt::cvt;
@@ -6,7 +11,10 @@ use std::ffi::{c_void, OsString};
use std::fs::File;
use std::io;
use std::os::windows::prelude::{AsRawHandle, OsStringExt, RawHandle};
use winapi::shared::minwindef::{self, DWORD};
use winapi::shared::{
minwindef::{self, DWORD},
ntstatus,
};
use winapi::um::{fileapi, fileapi::GetFileType, minwinbase, winbase, winnt};
/// Maximum total path length for Unicode in Windows.
@@ -291,70 +299,39 @@ bitflags! {
/// This is convenience flag which is translated by the OS into actual [`FILE_GENERIC_READ`] union.
const GENERIC_READ = winnt::GENERIC_READ;
/// Provides read access.
/// This flag is a union of: FILE_READ_ATTRIBUTES, FILE_READ_DATA, FILE_READ_EA, READ_CONTROL, SYNCHRONIZE
const FILE_GENERIC_READ = AccessMode::FILE_READ_ATTRIBUTES.bits
| AccessMode::FILE_READ_DATA.bits
| AccessMode::FILE_READ_EA.bits
| AccessMode::READ_CONTROL.bits
| AccessMode::SYNCHRONIZE.bits;
const FILE_GENERIC_READ = winnt::FILE_GENERIC_READ;
/// Provides write access.
/// This flag is a union of: FILE_WRITE_ATTRIBUTES, FILE_WRITE_DATA, FILE_WRITE_EA, READ_CONTROL, SYNCHRONIZE
const FILE_GENERIC_WRITE = AccessMode::FILE_WRITE_ATTRIBUTES.bits
| AccessMode::FILE_WRITE_DATA.bits
| AccessMode::FILE_WRITE_EA.bits
| AccessMode::READ_CONTROL.bits
| AccessMode::SYNCHRONIZE.bits;
const FILE_GENERIC_WRITE = winnt::FILE_GENERIC_WRITE;
/// Provides execute access.
/// This flag is a union of: FILE_WRITE_ATTRIBUTES, FILE_WRITE_DATA, FILE_WRITE_EA, READ_CONTROL, SYNCHRONIZE
const FILE_GENERIC_EXECUTE = AccessMode::FILE_EXECUTE.bits
| AccessMode::FILE_READ_ATTRIBUTES.bits
| AccessMode::READ_CONTROL.bits
| AccessMode::SYNCHRONIZE.bits;
const FILE_GENERIC_EXECUTE = winnt::FILE_GENERIC_EXECUTE;
/// Provides all accesses.
/// This flag is a union of: FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE
const FILE_GENERIC_ALL = AccessMode::FILE_GENERIC_READ.bits | AccessMode::FILE_GENERIC_WRITE.bits | AccessMode::FILE_GENERIC_EXECUTE.bits;
const FILE_ALL_ACCESS = winnt::FILE_ALL_ACCESS;
}
}
pub unsafe fn get_file_access_mode(handle: RawHandle) -> Result<AccessMode> {
use winapi::shared::minwindef::FALSE;
use winapi::um::accctrl;
use winapi::um::aclapi::GetSecurityInfo;
use winapi::um::securitybaseapi::{GetAce, IsValidAcl};
let mut dacl = 0 as winnt::PACL;
let mut sec_desc = 0 as winnt::PSECURITY_DESCRIPTOR;
let err = winerror::WinError::from_u32(GetSecurityInfo(
handle,
accctrl::SE_FILE_OBJECT,
winnt::DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut dacl,
std::ptr::null_mut(),
&mut sec_desc,
));
if err != winerror::WinError::ERROR_SUCCESS {
return Err(err);
bitflags! {
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/52df7798-8330-474b-ac31-9afe8075640c
pub struct FileModeInformation: minwindef::DWORD {
/// When set, any system services, file system drivers (FSDs), and drivers that write data to
/// the file are required to actually transfer the data into the file before any requested write
/// operation is considered complete.
const FILE_WRITE_THROUGH = 0x2;
/// This is a hint that informs the cache that it SHOULD optimize for sequential access.
/// Non-sequential access of the file can result in performance degradation.
const FILE_SEQUENTIAL_ONLY = 0x4;
/// When set, the file cannot be cached or buffered in a driver's internal buffers.
const FILE_NO_INTERMEDIATE_BUFFERING = 0x8;
/// When set, all operations on the file are performed synchronously.
/// Any wait on behalf of the caller is subject to premature termination from alerts.
/// This flag also causes the I/O system to maintain the file position context.
const FILE_SYNCHRONOUS_IO_ALERT = 0x10;
/// When set, all operations on the file are performed synchronously.
/// Wait requests in the system to synchronize I/O queuing and completion are not subject to alerts.
/// This flag also causes the I/O system to maintain the file position context.
const FILE_SYNCHRONOUS_IO_NONALERT = 0x20;
/// This flag is not implemented and is always returned as not set.
const FILE_DELETE_ON_CLOSE = 0x1000;
}
if IsValidAcl(dacl) == FALSE {
return Err(winerror::WinError::last());
}
// let count = (*dacl).AceCount;
let mut ace = 0 as winnt::PVOID;
if GetAce(dacl, 0, &mut ace) == FALSE {
return Err(winerror::WinError::last());
}
// TODO: check for PACCESS_ALLOWED_ACE in Ace before accessing
// let header = (*(ace as winnt::PACCESS_ALLOWED_ACE)).Header.AceType;
Ok(AccessMode::from_bits_truncate(
(*(ace as winnt::PACCESS_ALLOWED_ACE)).Mask,
))
}
pub fn get_file_path(file: &File) -> Result<OsString> {
@@ -415,3 +392,45 @@ pub fn change_time(file: &File) -> io::Result<i64> {
Ok(tm)
}
pub fn query_access_information(handle: RawHandle) -> Result<AccessMode> {
let mut io_status_block = IO_STATUS_BLOCK::default();
let mut info = FILE_ACCESS_INFORMATION::default();
unsafe {
let status = NtQueryInformationFile(
handle,
&mut io_status_block,
&mut info as *mut _ as *mut c_void,
std::mem::size_of::<FILE_ACCESS_INFORMATION>() as u32,
FILE_INFORMATION_CLASS::FileAccessInformation,
);
if status != ntstatus::STATUS_SUCCESS {
return Err(winerror::WinError::from_u32(RtlNtStatusToDosError(status)));
}
}
Ok(AccessMode::from_bits_truncate(info.AccessFlags))
}
pub fn query_mode_information(handle: RawHandle) -> Result<FileModeInformation> {
let mut io_status_block = IO_STATUS_BLOCK::default();
let mut info = FILE_MODE_INFORMATION::default();
unsafe {
let status = NtQueryInformationFile(
handle,
&mut io_status_block,
&mut info as *mut _ as *mut c_void,
std::mem::size_of::<FILE_MODE_INFORMATION>() as u32,
FILE_INFORMATION_CLASS::FileModeInformation,
);
if status != ntstatus::STATUS_SUCCESS {
return Err(winerror::WinError::from_u32(RtlNtStatusToDosError(status)));
}
}
Ok(FileModeInformation::from_bits_truncate(info.Mode))
}