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

@@ -35,7 +35,8 @@ unsafe fn test_path_filestat(dir_fd: wasi_unstable::Fd) {
| wasi_unstable::RIGHT_FD_WRITE | wasi_unstable::RIGHT_FD_WRITE
| wasi_unstable::RIGHT_PATH_FILESTAT_GET, | wasi_unstable::RIGHT_PATH_FILESTAT_GET,
0, 0,
0, // Pass some flags for later retrieval
wasi_unstable::FDFLAG_APPEND | wasi_unstable::FDFLAG_SYNC,
&mut file_fd, &mut file_fd,
); );
assert_eq!( assert_eq!(
@@ -62,6 +63,11 @@ unsafe fn test_path_filestat(dir_fd: wasi_unstable::Fd) {
0, 0,
"files shouldn't have rights for path_* syscalls even if manually given", "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 // Check file size
let mut stat = wasi_unstable::FileStat { let mut stat = wasi_unstable::FileStat {

View File

@@ -12,7 +12,6 @@ cfg_if! {
} else if #[cfg(windows)] { } else if #[cfg(windows)] {
mod windows; mod windows;
pub(crate) use self::windows::*; pub(crate) use self::windows::*;
pub use self::windows::preopen_dir;
pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { 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)) host_impl::errno_from_win(winx::winerror::WinError::from_u32(err as u32))

View File

@@ -53,13 +53,13 @@ pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
wasi::__wasi_rights_t, wasi::__wasi_rights_t,
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)?; let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(handle)?;
match file_type { match file_type {
wasi::__WASI_FILETYPE_DIRECTORY | wasi::__WASI_FILETYPE_REGULAR_FILE => { 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) { if mode.contains(AccessMode::FILE_GENERIC_READ) {
rights_base |= wasi::__WASI_RIGHTS_FD_READ; rights_base |= wasi::__WASI_RIGHTS_FD_READ;
} }

View File

@@ -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> { pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
use winx::file::AccessMode; 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(host_impl::fdflags_from_win)
.map_err(Into::into) .map_err(Into::into)
} }

View File

@@ -4,7 +4,6 @@ pub(crate) mod hostcalls_impl;
use crate::old::snapshot_0::Result; use crate::old::snapshot_0::Result;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::path::Path;
pub(crate) fn dev_null() -> Result<File> { pub(crate) fn dev_null() -> Result<File> {
OpenOptions::new() OpenOptions::new()
@@ -13,20 +12,3 @@ pub(crate) fn dev_null() -> Result<File> {
.open("NUL") .open("NUL")
.map_err(Into::into) .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)
}

View File

@@ -53,13 +53,13 @@ pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
wasi::__wasi_rights_t, wasi::__wasi_rights_t,
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)?; let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(handle)?;
match file_type { match file_type {
wasi::__WASI_FILETYPE_DIRECTORY | wasi::__WASI_FILETYPE_REGULAR_FILE => { 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) { if mode.contains(AccessMode::FILE_GENERIC_READ) {
rights_base |= wasi::__WASI_RIGHTS_FD_READ; rights_base |= wasi::__WASI_RIGHTS_FD_READ;
} }

View File

@@ -1,13 +1,7 @@
//! WASI host types specific to Windows host. //! WASI host types specific to Windows host.
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
use crate::{wasi, Error, Result}; use crate::{wasi, Error, Result};
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs::OpenOptions;
use std::os::windows::ffi::OsStrExt; 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 { pub(crate) fn errno_from_win(error: winx::winerror::WinError) -> wasi::__wasi_errno_t {
// TODO: implement error mapping between Windows and WASI // 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. /// Creates owned WASI path from OS string.
/// ///
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise, /// NB WASI spec requires OS string to be valid UTF-8. Otherwise,

View File

@@ -16,7 +16,7 @@ use std::io::{self, Seek, SeekFrom};
use std::os::windows::fs::{FileExt, OpenOptionsExt}; use std::os::windows::fs::{FileExt, OpenOptionsExt};
use std::os::windows::prelude::{AsRawHandle, FromRawHandle}; use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
use std::path::{Path, PathBuf}; 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> { fn read_at(mut file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
// get current cursor position // 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> { pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
use winx::file::AccessMode; let mut fdflags = 0;
unsafe { winx::file::get_file_access_mode(fd.as_raw_handle()) }
.map(host_impl::fdflags_from_win) let handle = unsafe { fd.as_raw_handle() };
.map_err(Into::into)
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<()> { 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> { ) -> Result<File> {
use winx::file::{AccessMode, CreationDisposition, Flags}; 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 // 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(); let mut opts = OpenOptions::new();
match host_impl::win_from_oflags(oflags) { match creation_disposition_from_oflags(oflags) {
CreationDisposition::CREATE_ALWAYS => { CreationDisposition::CREATE_ALWAYS => {
opts.create(true).append(true); opts.create(true).write(true);
} }
CreationDisposition::CREATE_NEW => { CreationDisposition::CREATE_NEW => {
opts.create_new(true).write(true); opts.create_new(true).write(true);
} }
CreationDisposition::TRUNCATE_EXISTING => { 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()?; let path = resolved.concatenate()?;
match path.symlink_metadata().map(|metadata| metadata.file_type()) { match path.symlink_metadata().map(|metadata| metadata.file_type()) {
@@ -162,12 +168,71 @@ pub(crate) fn path_open(
}, },
} }
opts.access_mode(access_mode.bits()) opts.access_mode(file_access_mode_from_fdflags(fdflags, read, write).bits())
.custom_flags(flags.bits()) .custom_flags(file_flags_from_fdflags(fdflags).bits())
.open(&path) .open(&path)
.map_err(Into::into) .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>>( fn dirent_from_path<P: AsRef<Path>>(
path: P, path: P,
name: &str, name: &str,

View File

@@ -1,4 +1,9 @@
#![allow(non_camel_case_types)] #![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 crate::{winerror, Result};
use bitflags::bitflags; use bitflags::bitflags;
use cvt::cvt; use cvt::cvt;
@@ -6,7 +11,10 @@ use std::ffi::{c_void, OsString};
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::os::windows::prelude::{AsRawHandle, OsStringExt, RawHandle}; 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}; use winapi::um::{fileapi, fileapi::GetFileType, minwinbase, winbase, winnt};
/// Maximum total path length for Unicode in Windows. /// 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. /// This is convenience flag which is translated by the OS into actual [`FILE_GENERIC_READ`] union.
const GENERIC_READ = winnt::GENERIC_READ; const GENERIC_READ = winnt::GENERIC_READ;
/// Provides read access. /// 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 = winnt::FILE_GENERIC_READ;
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;
/// Provides write access. /// 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 = winnt::FILE_GENERIC_WRITE;
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;
/// Provides execute access. /// 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 = winnt::FILE_GENERIC_EXECUTE;
const FILE_GENERIC_EXECUTE = AccessMode::FILE_EXECUTE.bits
| AccessMode::FILE_READ_ATTRIBUTES.bits
| AccessMode::READ_CONTROL.bits
| AccessMode::SYNCHRONIZE.bits;
/// Provides all accesses. /// Provides all accesses.
/// This flag is a union of: FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE const FILE_ALL_ACCESS = winnt::FILE_ALL_ACCESS;
const FILE_GENERIC_ALL = AccessMode::FILE_GENERIC_READ.bits | AccessMode::FILE_GENERIC_WRITE.bits | AccessMode::FILE_GENERIC_EXECUTE.bits;
} }
} }
pub unsafe fn get_file_access_mode(handle: RawHandle) -> Result<AccessMode> { bitflags! {
use winapi::shared::minwindef::FALSE; // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/52df7798-8330-474b-ac31-9afe8075640c
use winapi::um::accctrl; pub struct FileModeInformation: minwindef::DWORD {
use winapi::um::aclapi::GetSecurityInfo; /// When set, any system services, file system drivers (FSDs), and drivers that write data to
use winapi::um::securitybaseapi::{GetAce, IsValidAcl}; /// the file are required to actually transfer the data into the file before any requested write
let mut dacl = 0 as winnt::PACL; /// operation is considered complete.
let mut sec_desc = 0 as winnt::PSECURITY_DESCRIPTOR; const FILE_WRITE_THROUGH = 0x2;
/// This is a hint that informs the cache that it SHOULD optimize for sequential access.
let err = winerror::WinError::from_u32(GetSecurityInfo( /// Non-sequential access of the file can result in performance degradation.
handle, const FILE_SEQUENTIAL_ONLY = 0x4;
accctrl::SE_FILE_OBJECT, /// When set, the file cannot be cached or buffered in a driver's internal buffers.
winnt::DACL_SECURITY_INFORMATION, const FILE_NO_INTERMEDIATE_BUFFERING = 0x8;
std::ptr::null_mut(), /// When set, all operations on the file are performed synchronously.
std::ptr::null_mut(), /// Any wait on behalf of the caller is subject to premature termination from alerts.
&mut dacl, /// This flag also causes the I/O system to maintain the file position context.
std::ptr::null_mut(), const FILE_SYNCHRONOUS_IO_ALERT = 0x10;
&mut sec_desc, /// 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.
if err != winerror::WinError::ERROR_SUCCESS { const FILE_SYNCHRONOUS_IO_NONALERT = 0x20;
return Err(err); /// 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> { pub fn get_file_path(file: &File) -> Result<OsString> {
@@ -415,3 +392,45 @@ pub fn change_time(file: &File) -> io::Result<i64> {
Ok(tm) 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))
}

View File

@@ -22,6 +22,7 @@
#![cfg(windows)] #![cfg(windows)]
pub mod file; pub mod file;
mod ntdll;
pub mod time; pub mod time;
pub mod winerror; pub mod winerror;

View 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;
}