Files
wasmtime/crates/wasi-common/src/sys/windows/mod.rs

189 lines
6.3 KiB
Rust

pub(crate) mod clock;
pub(crate) mod fd;
pub(crate) mod osdir;
pub(crate) mod osfile;
pub(crate) mod oshandle;
pub(crate) mod osother;
pub(crate) mod path;
pub(crate) mod poll;
pub(crate) mod stdio;
use crate::handle::HandleRights;
use crate::sys::AsFile;
use crate::wasi::{types, RightsExt};
use crate::{Error, Result};
use std::convert::{TryFrom, TryInto};
use std::fs::File;
use std::mem::ManuallyDrop;
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};
use std::{io, string};
use winx::file::{CreationDisposition, Flags};
impl<T: AsRawHandle> AsFile for T {
fn as_file(&self) -> io::Result<ManuallyDrop<File>> {
let file = unsafe { File::from_raw_handle(self.as_raw_handle()) };
Ok(ManuallyDrop::new(file))
}
}
pub(super) fn get_file_type(file: &File) -> io::Result<types::Filetype> {
let file_type = unsafe { winx::file::get_file_type(file.as_raw_handle())? };
let file_type = if file_type.is_char() {
// character file: LPT device or console
// TODO: rule out LPT device
types::Filetype::CharacterDevice
} else if file_type.is_disk() {
// disk file: file, dir or disk device
let meta = file.metadata()?;
if meta.is_dir() {
types::Filetype::Directory
} else if meta.is_file() {
types::Filetype::RegularFile
} else {
return Err(io::Error::from_raw_os_error(libc::EINVAL));
}
} else if file_type.is_pipe() {
// pipe object: socket, named pipe or anonymous pipe
// TODO: what about pipes, etc?
types::Filetype::SocketStream
} else {
return Err(io::Error::from_raw_os_error(libc::EINVAL));
};
Ok(file_type)
}
pub(super) fn get_rights(file_type: &types::Filetype) -> io::Result<HandleRights> {
let (base, inheriting) = match file_type {
types::Filetype::BlockDevice => (
types::Rights::block_device_base(),
types::Rights::block_device_inheriting(),
),
types::Filetype::CharacterDevice => (types::Rights::tty_base(), types::Rights::tty_base()),
types::Filetype::SocketDgram | types::Filetype::SocketStream => (
types::Rights::socket_base(),
types::Rights::socket_inheriting(),
),
types::Filetype::SymbolicLink | types::Filetype::Unknown => (
types::Rights::regular_file_base(),
types::Rights::regular_file_inheriting(),
),
types::Filetype::Directory => (
types::Rights::directory_base(),
types::Rights::directory_inheriting(),
),
types::Filetype::RegularFile => (
types::Rights::regular_file_base(),
types::Rights::regular_file_inheriting(),
),
};
let rights = HandleRights::new(base, inheriting);
Ok(rights)
}
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> io::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)
}
pub(crate) fn file_serial_no(file: &File) -> io::Result<u64> {
let info = winx::file::get_fileinfo(file)?;
let high = info.nFileIndexHigh;
let low = info.nFileIndexLow;
let no = (u64::from(high) << 32) | u64::from(low);
Ok(no)
}
impl From<string::FromUtf16Error> for Error {
fn from(_err: string::FromUtf16Error) -> Self {
Self::Ilseq
}
}
fn num_hardlinks(file: &File) -> io::Result<u64> {
Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into())
}
fn device_id(file: &File) -> io::Result<u64> {
Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into())
}
fn change_time(file: &File) -> io::Result<i64> {
winx::file::change_time(file)
}
fn systemtime_to_timestamp(st: SystemTime) -> Result<u64> {
st.duration_since(UNIX_EPOCH)
.map_err(|_| Error::Inval)? // date earlier than UNIX_EPOCH
.as_nanos()
.try_into()
.map_err(Into::into) // u128 doesn't fit into u64
}
impl TryFrom<&File> for types::Filestat {
type Error = Error;
fn try_from(file: &File) -> Result<Self> {
let metadata = file.metadata()?;
Ok(types::Filestat {
dev: device_id(file)?,
ino: file_serial_no(file)?,
nlink: num_hardlinks(file)?.try_into()?, // u64 doesn't fit into u32
size: metadata.len(),
atim: systemtime_to_timestamp(metadata.accessed()?)?,
ctim: change_time(file)?.try_into()?, // i64 doesn't fit into u64
mtim: systemtime_to_timestamp(metadata.modified()?)?,
filetype: metadata.file_type().into(),
})
}
}
impl From<types::Oflags> for CreationDisposition {
fn from(oflags: types::Oflags) -> Self {
if oflags.contains(&types::Oflags::CREAT) {
if oflags.contains(&types::Oflags::EXCL) {
CreationDisposition::CREATE_NEW
} else {
CreationDisposition::CREATE_ALWAYS
}
} else if oflags.contains(&types::Oflags::TRUNC) {
CreationDisposition::TRUNCATE_EXISTING
} else {
CreationDisposition::OPEN_EXISTING
}
}
}
impl From<types::Fdflags> for Flags {
fn from(fdflags: types::Fdflags) -> Self {
// 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.contains(&types::Fdflags::DSYNC)
|| fdflags.contains(&types::Fdflags::RSYNC)
|| fdflags.contains(&types::Fdflags::SYNC)
{
flags.insert(Flags::FILE_FLAG_WRITE_THROUGH);
}
flags
}
}