diff --git a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs b/crates/test-programs/wasi-tests/src/bin/path_filestat.rs index 3ff999c3c5..006e25c772 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_filestat.rs @@ -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 { diff --git a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs index 468996239f..960c6337e4 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs @@ -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)) diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/fdentry_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/fdentry_impl.rs index 7bb2c90c50..e903e1a34b 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/fdentry_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/fdentry_impl.rs @@ -53,13 +53,13 @@ pub(crate) unsafe fn determine_type_and_access_rights( 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; } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs index e53f52fe44..3f3b9da7ac 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs @@ -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 { 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) } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs index b733f14920..35d00ac935 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs @@ -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 { OpenOptions::new() @@ -13,20 +12,3 @@ pub(crate) fn dev_null() -> Result { .open("NUL") .map_err(Into::into) } - -pub fn preopen_dir>(path: P) -> Result { - 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) -} diff --git a/crates/wasi-common/src/sys/windows/fdentry_impl.rs b/crates/wasi-common/src/sys/windows/fdentry_impl.rs index 88eda6ae57..18cf9914fe 100644 --- a/crates/wasi-common/src/sys/windows/fdentry_impl.rs +++ b/crates/wasi-common/src/sys/windows/fdentry_impl.rs @@ -53,13 +53,13 @@ pub(crate) unsafe fn determine_type_and_access_rights( 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; } diff --git a/crates/wasi-common/src/sys/windows/host_impl.rs b/crates/wasi-common/src/sys/windows/host_impl.rs index e2eea28497..2816bcd66b 100644 --- a/crates/wasi-common/src/sys/windows/host_impl.rs +++ b/crates/wasi-common/src/sys/windows/host_impl.rs @@ -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, diff --git a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs index 561a50befc..d98c2a17aa 100644 --- a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs @@ -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 { // 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 { - 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 { 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>( path: P, name: &str, diff --git a/crates/wasi-common/winx/src/file.rs b/crates/wasi-common/winx/src/file.rs index b72b813813..a5fd7537b3 100644 --- a/crates/wasi-common/winx/src/file.rs +++ b/crates/wasi-common/winx/src/file.rs @@ -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 { - 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 { @@ -415,3 +392,45 @@ pub fn change_time(file: &File) -> io::Result { Ok(tm) } + +pub fn query_access_information(handle: RawHandle) -> Result { + 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::() 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 { + 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::() 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)) +} diff --git a/crates/wasi-common/winx/src/lib.rs b/crates/wasi-common/winx/src/lib.rs index eeaf80d7bc..779447e786 100644 --- a/crates/wasi-common/winx/src/lib.rs +++ b/crates/wasi-common/winx/src/lib.rs @@ -22,6 +22,7 @@ #![cfg(windows)] pub mod file; +mod ntdll; pub mod time; pub mod winerror; diff --git a/crates/wasi-common/winx/src/ntdll.rs b/crates/wasi-common/winx/src/ntdll.rs new file mode 100644 index 0000000000..2948aee79a --- /dev/null +++ b/crates/wasi-common/winx/src/ntdll.rs @@ -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; +}