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:
@@ -35,7 +35,8 @@ unsafe fn test_path_filestat(dir_fd: wasi_unstable::Fd) {
|
||||
| wasi_unstable::RIGHT_FD_WRITE
|
||||
| wasi_unstable::RIGHT_PATH_FILESTAT_GET,
|
||||
0,
|
||||
0,
|
||||
// Pass some flags for later retrieval
|
||||
wasi_unstable::FDFLAG_APPEND | wasi_unstable::FDFLAG_SYNC,
|
||||
&mut file_fd,
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -62,6 +63,11 @@ unsafe fn test_path_filestat(dir_fd: wasi_unstable::Fd) {
|
||||
0,
|
||||
"files shouldn't have rights for path_* syscalls even if manually given",
|
||||
);
|
||||
assert_ne!(
|
||||
fdstat.fs_flags & (wasi_unstable::FDFLAG_APPEND | wasi_unstable::FDFLAG_SYNC),
|
||||
0,
|
||||
"file should have the same flags used to create the file"
|
||||
);
|
||||
|
||||
// Check file size
|
||||
let mut stat = wasi_unstable::FileStat {
|
||||
|
||||
@@ -12,7 +12,6 @@ cfg_if! {
|
||||
} else if #[cfg(windows)] {
|
||||
mod windows;
|
||||
pub(crate) use self::windows::*;
|
||||
pub use self::windows::preopen_dir;
|
||||
|
||||
pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t {
|
||||
host_impl::errno_from_win(winx::winerror::WinError::from_u32(err as u32))
|
||||
|
||||
@@ -53,13 +53,13 @@ pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
)> {
|
||||
use winx::file::{get_file_access_mode, AccessMode};
|
||||
use winx::file::{query_access_information, AccessMode};
|
||||
|
||||
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(handle)?;
|
||||
|
||||
match file_type {
|
||||
wasi::__WASI_FILETYPE_DIRECTORY | wasi::__WASI_FILETYPE_REGULAR_FILE => {
|
||||
let mode = get_file_access_mode(handle.as_raw_handle())?;
|
||||
let mode = query_access_information(handle.as_raw_handle())?;
|
||||
if mode.contains(AccessMode::FILE_GENERIC_READ) {
|
||||
rights_base |= wasi::__WASI_RIGHTS_FD_READ;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t
|
||||
|
||||
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
|
||||
use winx::file::AccessMode;
|
||||
unsafe { winx::file::get_file_access_mode(fd.as_raw_handle()) }
|
||||
unsafe { winx::file::query_access_information(fd.as_raw_handle()) }
|
||||
.map(host_impl::fdflags_from_win)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ pub(crate) mod hostcalls_impl;
|
||||
|
||||
use crate::old::snapshot_0::Result;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn dev_null() -> Result<File> {
|
||||
OpenOptions::new()
|
||||
@@ -13,20 +12,3 @@ pub(crate) fn dev_null() -> Result<File> {
|
||||
.open("NUL")
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS;
|
||||
|
||||
// To open a directory using CreateFile, specify the
|
||||
// FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFileFlags...
|
||||
// cf. https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfile2
|
||||
OpenOptions::new()
|
||||
.create(false)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.attributes(FILE_FLAG_BACKUP_SEMANTICS)
|
||||
.open(path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -53,13 +53,13 @@ pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
)> {
|
||||
use winx::file::{get_file_access_mode, AccessMode};
|
||||
use winx::file::{query_access_information, AccessMode};
|
||||
|
||||
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(handle)?;
|
||||
|
||||
match file_type {
|
||||
wasi::__WASI_FILETYPE_DIRECTORY | wasi::__WASI_FILETYPE_REGULAR_FILE => {
|
||||
let mode = get_file_access_mode(handle.as_raw_handle())?;
|
||||
let mode = query_access_information(handle.as_raw_handle())?;
|
||||
if mode.contains(AccessMode::FILE_GENERIC_READ) {
|
||||
rights_base |= wasi::__WASI_RIGHTS_FD_READ;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
//! WASI host types specific to Windows host.
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
use crate::{wasi, Error, Result};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
use winx::file::{AccessMode, Attributes, CreationDisposition, Flags};
|
||||
|
||||
pub(crate) fn errno_from_win(error: winx::winerror::WinError) -> wasi::__wasi_errno_t {
|
||||
// TODO: implement error mapping between Windows and WASI
|
||||
@@ -40,64 +34,6 @@ pub(crate) fn errno_from_win(error: winx::winerror::WinError) -> wasi::__wasi_er
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fdflags_from_win(mode: AccessMode) -> wasi::__wasi_fdflags_t {
|
||||
let mut fdflags = 0;
|
||||
// TODO verify this!
|
||||
if mode.contains(AccessMode::FILE_APPEND_DATA) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
|
||||
}
|
||||
if mode.contains(AccessMode::SYNCHRONIZE) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
|
||||
fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
|
||||
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
|
||||
}
|
||||
// The NONBLOCK equivalent is FILE_FLAG_OVERLAPPED
|
||||
// but it seems winapi doesn't provide a mechanism
|
||||
// for checking whether the handle supports async IO.
|
||||
// On the contrary, I've found some dicsussion online
|
||||
// which suggests that on Windows all handles should
|
||||
// generally be assumed to be opened with async support
|
||||
// and then the program should fallback should that **not**
|
||||
// be the case at the time of the operation.
|
||||
// TODO: this requires further investigation
|
||||
fdflags
|
||||
}
|
||||
|
||||
pub(crate) fn win_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> (AccessMode, Flags) {
|
||||
let mut access_mode = AccessMode::empty();
|
||||
let mut flags = Flags::empty();
|
||||
|
||||
// TODO verify this!
|
||||
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
|
||||
flags.insert(Flags::FILE_FLAG_OVERLAPPED);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
|
||||
access_mode.insert(AccessMode::FILE_APPEND_DATA);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
|
||||
{
|
||||
access_mode.insert(AccessMode::SYNCHRONIZE);
|
||||
}
|
||||
|
||||
(access_mode, flags)
|
||||
}
|
||||
|
||||
pub(crate) fn win_from_oflags(oflags: wasi::__wasi_oflags_t) -> CreationDisposition {
|
||||
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
|
||||
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
|
||||
CreationDisposition::CREATE_NEW
|
||||
} else {
|
||||
CreationDisposition::CREATE_ALWAYS
|
||||
}
|
||||
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
|
||||
CreationDisposition::TRUNCATE_EXISTING
|
||||
} else {
|
||||
CreationDisposition::OPEN_EXISTING
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates owned WASI path from OS string.
|
||||
///
|
||||
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
|
||||
|
||||
@@ -16,7 +16,7 @@ use std::io::{self, Seek, SeekFrom};
|
||||
use std::os::windows::fs::{FileExt, OpenOptionsExt};
|
||||
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
|
||||
use std::path::{Path, PathBuf};
|
||||
use winx::file::{AccessMode, Flags};
|
||||
use winx::file::{AccessMode, CreationDisposition, FileModeInformation, Flags};
|
||||
|
||||
fn read_at(mut file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
// get current cursor position
|
||||
@@ -53,10 +53,29 @@ pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
|
||||
use winx::file::AccessMode;
|
||||
unsafe { winx::file::get_file_access_mode(fd.as_raw_handle()) }
|
||||
.map(host_impl::fdflags_from_win)
|
||||
.map_err(Into::into)
|
||||
let mut fdflags = 0;
|
||||
|
||||
let handle = unsafe { fd.as_raw_handle() };
|
||||
|
||||
let access_mode = winx::file::query_access_information(handle)?;
|
||||
let mode = winx::file::query_mode_information(handle)?;
|
||||
|
||||
// Append without write implies append-only (__WASI_FDFLAGS_APPEND)
|
||||
if access_mode.contains(AccessMode::FILE_APPEND_DATA)
|
||||
&& !access_mode.contains(AccessMode::FILE_WRITE_DATA)
|
||||
{
|
||||
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
|
||||
}
|
||||
|
||||
if mode.contains(FileModeInformation::FILE_WRITE_THROUGH) {
|
||||
// Only report __WASI_FDFLAGS_SYNC
|
||||
// This is technically the only one of the O_?SYNC flags Windows supports.
|
||||
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
|
||||
}
|
||||
|
||||
// Files do not support the `__WASI_FDFLAGS_NONBLOCK` flag
|
||||
|
||||
Ok(fdflags)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> {
|
||||
@@ -100,36 +119,23 @@ pub(crate) fn path_open(
|
||||
) -> Result<File> {
|
||||
use winx::file::{AccessMode, CreationDisposition, Flags};
|
||||
|
||||
let mut access_mode = AccessMode::READ_CONTROL;
|
||||
if read {
|
||||
access_mode.insert(AccessMode::FILE_GENERIC_READ);
|
||||
}
|
||||
if write {
|
||||
access_mode.insert(AccessMode::FILE_GENERIC_WRITE);
|
||||
}
|
||||
|
||||
let mut flags = Flags::FILE_FLAG_BACKUP_SEMANTICS;
|
||||
|
||||
// convert open flags
|
||||
// note: the calls to `write(true)` are to bypass an internal OpenOption check
|
||||
// the write flag will ultimately be ignored when `access_mode` is called below.
|
||||
let mut opts = OpenOptions::new();
|
||||
match host_impl::win_from_oflags(oflags) {
|
||||
match creation_disposition_from_oflags(oflags) {
|
||||
CreationDisposition::CREATE_ALWAYS => {
|
||||
opts.create(true).append(true);
|
||||
opts.create(true).write(true);
|
||||
}
|
||||
CreationDisposition::CREATE_NEW => {
|
||||
opts.create_new(true).write(true);
|
||||
}
|
||||
CreationDisposition::TRUNCATE_EXISTING => {
|
||||
opts.truncate(true);
|
||||
opts.truncate(true).write(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// convert file descriptor flags
|
||||
let (add_access_mode, add_flags) = host_impl::win_from_fdflags(fdflags);
|
||||
access_mode.insert(add_access_mode);
|
||||
flags.insert(add_flags);
|
||||
|
||||
let path = resolved.concatenate()?;
|
||||
|
||||
match path.symlink_metadata().map(|metadata| metadata.file_type()) {
|
||||
@@ -162,12 +168,71 @@ pub(crate) fn path_open(
|
||||
},
|
||||
}
|
||||
|
||||
opts.access_mode(access_mode.bits())
|
||||
.custom_flags(flags.bits())
|
||||
opts.access_mode(file_access_mode_from_fdflags(fdflags, read, write).bits())
|
||||
.custom_flags(file_flags_from_fdflags(fdflags).bits())
|
||||
.open(&path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn creation_disposition_from_oflags(oflags: wasi::__wasi_oflags_t) -> CreationDisposition {
|
||||
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
|
||||
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
|
||||
CreationDisposition::CREATE_NEW
|
||||
} else {
|
||||
CreationDisposition::CREATE_ALWAYS
|
||||
}
|
||||
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
|
||||
CreationDisposition::TRUNCATE_EXISTING
|
||||
} else {
|
||||
CreationDisposition::OPEN_EXISTING
|
||||
}
|
||||
}
|
||||
|
||||
fn file_access_mode_from_fdflags(
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
read: bool,
|
||||
write: bool,
|
||||
) -> AccessMode {
|
||||
let mut access_mode = AccessMode::READ_CONTROL;
|
||||
|
||||
if read {
|
||||
access_mode.insert(AccessMode::GENERIC_READ);
|
||||
}
|
||||
|
||||
if write {
|
||||
access_mode.insert(AccessMode::GENERIC_WRITE);
|
||||
}
|
||||
|
||||
// For append, grant the handle FILE_APPEND_DATA access but *not* FILE_WRITE_DATA.
|
||||
// This makes the handle "append only".
|
||||
// Changes to the file pointer will be ignored (like POSIX's O_APPEND behavior).
|
||||
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
|
||||
access_mode.insert(AccessMode::FILE_APPEND_DATA);
|
||||
access_mode.remove(AccessMode::FILE_WRITE_DATA);
|
||||
}
|
||||
|
||||
access_mode
|
||||
}
|
||||
|
||||
fn file_flags_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> Flags {
|
||||
// Enable backup semantics so directories can be opened as files
|
||||
let mut flags = Flags::FILE_FLAG_BACKUP_SEMANTICS;
|
||||
|
||||
// Note: __WASI_FDFLAGS_NONBLOCK is purposely being ignored for files
|
||||
// While Windows does inherently support a non-blocking mode on files, the WASI API will
|
||||
// treat I/O operations on files as synchronous. WASI might have an async-io API in the future.
|
||||
|
||||
// Technically, Windows only supports __WASI_FDFLAGS_SYNC, but treat all the flags as the same.
|
||||
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
|
||||
{
|
||||
flags.insert(Flags::FILE_FLAG_WRITE_THROUGH);
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
fn dirent_from_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
name: &str,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#![cfg(windows)]
|
||||
|
||||
pub mod file;
|
||||
mod ntdll;
|
||||
pub mod time;
|
||||
pub mod winerror;
|
||||
|
||||
|
||||
65
crates/wasi-common/winx/src/ntdll.rs
Normal file
65
crates/wasi-common/winx/src/ntdll.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
//! Module for importing functions from ntdll.dll.
|
||||
//! The winapi crate does not expose these Windows API functions.
|
||||
|
||||
#![allow(nonstandard_style)]
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::c_ulong;
|
||||
use std::os::windows::prelude::RawHandle;
|
||||
use winapi::shared::ntdef::NTSTATUS;
|
||||
use winapi::um::winnt::ACCESS_MASK;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum FILE_INFORMATION_CLASS {
|
||||
FileAccessInformation = 8,
|
||||
FileModeInformation = 16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) union IO_STATUS_BLOCK_u {
|
||||
pub Status: NTSTATUS,
|
||||
pub Pointer: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct IO_STATUS_BLOCK {
|
||||
pub u: IO_STATUS_BLOCK_u,
|
||||
pub Information: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub(crate) struct FILE_ACCESS_INFORMATION {
|
||||
pub AccessFlags: ACCESS_MASK,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub(crate) struct FILE_MODE_INFORMATION {
|
||||
pub Mode: c_ulong,
|
||||
}
|
||||
|
||||
impl Default for IO_STATUS_BLOCK {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { std::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
#[link(name = "ntdll")]
|
||||
extern "C" {
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntqueryinformationfile
|
||||
pub(crate) fn NtQueryInformationFile(
|
||||
FileHandle: RawHandle,
|
||||
IoStatusBlock: *mut IO_STATUS_BLOCK,
|
||||
FileInformation: *mut c_void,
|
||||
Length: c_ulong,
|
||||
FileInformationClass: FILE_INFORMATION_CLASS,
|
||||
) -> NTSTATUS;
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlntstatustodoserror
|
||||
pub(crate) fn RtlNtStatusToDosError(status: NTSTATUS) -> c_ulong;
|
||||
}
|
||||
Reference in New Issue
Block a user