Virtual file support (#701)
* Add support for virtual files (eg, not backed by an OS file). Virtual files are implemented through trait objects, with a default implementation that tries to behave like on-disk files, but entirely backed by in-memory structures. Co-authored-by: Dan Gohman <sunfish@mozilla.com>
This commit is contained in:
@@ -11,6 +11,7 @@ impl AsRawFd for Descriptor {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
match self {
|
||||
Self::OsHandle(file) => file.as_raw_fd(),
|
||||
Self::VirtualFile(_) => panic!("virtual files do not have a raw fd"),
|
||||
Self::Stdin => io::stdin().as_raw_fd(),
|
||||
Self::Stdout => io::stdout().as_raw_fd(),
|
||||
Self::Stderr => io::stderr().as_raw_fd(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_unsafe)]
|
||||
use crate::fdentry::Descriptor;
|
||||
use crate::host::Dirent;
|
||||
use crate::hostcalls_impl::PathGet;
|
||||
use crate::sys::{fdentry_impl::OsHandle, host_impl, unix::sys_impl};
|
||||
@@ -60,16 +61,9 @@ pub(crate) fn fd_advise(
|
||||
unsafe { posix_fadvise(file.as_raw_fd(), offset, len, host_advice) }.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> {
|
||||
pub(crate) fn path_create_directory(base: &File, path: &str) -> Result<()> {
|
||||
use yanix::file::{mkdirat, Mode};
|
||||
unsafe {
|
||||
mkdirat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
Mode::from_bits_truncate(0o777),
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
unsafe { mkdirat(base.as_raw_fd(), path, Mode::from_bits_truncate(0o777)) }.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
|
||||
@@ -92,7 +86,7 @@ pub(crate) fn path_open(
|
||||
write: bool,
|
||||
oflags: wasi::__wasi_oflags_t,
|
||||
fs_flags: wasi::__wasi_fdflags_t,
|
||||
) -> Result<File> {
|
||||
) -> Result<Descriptor> {
|
||||
use yanix::file::{fstatat, openat, AtFlag, FileType, Mode, OFlag};
|
||||
|
||||
let mut nix_all_oflags = if read && write {
|
||||
@@ -119,14 +113,15 @@ pub(crate) fn path_open(
|
||||
log::debug!("path_open resolved = {:?}", resolved);
|
||||
log::debug!("path_open oflags = {:?}", nix_all_oflags);
|
||||
|
||||
let new_fd = match unsafe {
|
||||
let fd_no = unsafe {
|
||||
openat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
nix_all_oflags,
|
||||
Mode::from_bits_truncate(0o666),
|
||||
)
|
||||
} {
|
||||
};
|
||||
let new_fd = match fd_no {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
if let yanix::Error::Io(ref err) = e {
|
||||
@@ -188,7 +183,7 @@ pub(crate) fn path_open(
|
||||
log::debug!("path_open (host) new_fd = {:?}", new_fd);
|
||||
|
||||
// Determine the type of the new file descriptor and which rights contradict with this type
|
||||
Ok(unsafe { File::from_raw_fd(new_fd) })
|
||||
Ok(OsHandle::from(unsafe { File::from_raw_fd(new_fd) }).into())
|
||||
}
|
||||
|
||||
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> {
|
||||
@@ -263,7 +258,7 @@ pub(crate) fn path_filestat_set_times(
|
||||
};
|
||||
|
||||
utimensat(
|
||||
resolved.dirfd(),
|
||||
&resolved.dirfd().as_os_handle(),
|
||||
resolved.path(),
|
||||
atim,
|
||||
mtim,
|
||||
@@ -274,6 +269,7 @@ pub(crate) fn path_filestat_set_times(
|
||||
|
||||
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
|
||||
use yanix::file::{unlinkat, AtFlag};
|
||||
|
||||
unsafe {
|
||||
unlinkat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::fdentry::Descriptor;
|
||||
use crate::hostcalls_impl::PathGet;
|
||||
use crate::Result;
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
@@ -26,15 +27,22 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
|
||||
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
|
||||
use yanix::file::renameat;
|
||||
unsafe {
|
||||
renameat(
|
||||
resolved_old.dirfd().as_raw_fd(),
|
||||
resolved_old.path(),
|
||||
resolved_new.dirfd().as_raw_fd(),
|
||||
resolved_new.path(),
|
||||
)
|
||||
match (resolved_old.dirfd(), resolved_new.dirfd()) {
|
||||
(Descriptor::OsHandle(resolved_old_file), Descriptor::OsHandle(resolved_new_file)) => {
|
||||
unsafe {
|
||||
renameat(
|
||||
resolved_old_file.as_raw_fd(),
|
||||
resolved_old.path(),
|
||||
resolved_new_file.as_raw_fd(),
|
||||
resolved_new.path(),
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("path_link with one or more virtual files");
|
||||
}
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) mod fd_readdir_impl {
|
||||
|
||||
@@ -39,6 +39,9 @@ impl AsRawHandle for Descriptor {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
match self {
|
||||
Self::OsHandle(file) => file.as_raw_handle(),
|
||||
Self::VirtualFile(_file) => {
|
||||
unimplemented!("virtual as_raw_handle");
|
||||
}
|
||||
Self::Stdin => io::stdin().as_raw_handle(),
|
||||
Self::Stdout => io::stdout().as_raw_handle(),
|
||||
Self::Stderr => io::stderr().as_raw_handle(),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#![allow(unused)]
|
||||
use super::fs_helpers::*;
|
||||
use crate::ctx::WasiCtx;
|
||||
use crate::fdentry::FdEntry;
|
||||
use crate::fdentry::{Descriptor, FdEntry};
|
||||
use crate::host::{Dirent, FileType};
|
||||
use crate::hostcalls_impl::{fd_filestat_set_times_impl, PathGet};
|
||||
use crate::sys::fdentry_impl::{determine_type_rights, OsHandle};
|
||||
@@ -119,8 +119,8 @@ pub(crate) fn fd_advise(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> {
|
||||
let path = resolved.concatenate()?;
|
||||
pub(crate) fn path_create_directory(file: &File, path: &str) -> Result<()> {
|
||||
let path = concatenate(file, path)?;
|
||||
std::fs::create_dir(&path).map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ pub(crate) fn path_open(
|
||||
write: bool,
|
||||
oflags: wasi::__wasi_oflags_t,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> Result<File> {
|
||||
) -> Result<Descriptor> {
|
||||
use winx::file::{AccessMode, CreationDisposition, Flags};
|
||||
|
||||
let is_trunc = oflags & wasi::__WASI_OFLAGS_TRUNC != 0;
|
||||
@@ -207,6 +207,7 @@ pub(crate) fn path_open(
|
||||
opts.access_mode(access_mode.bits())
|
||||
.custom_flags(file_flags_from_fdflags(fdflags).bits())
|
||||
.open(&path)
|
||||
.map(|f| OsHandle::from(f).into())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -371,7 +372,7 @@ pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize>
|
||||
// we need to strip the prefix from the absolute path
|
||||
// as otherwise we will error out since WASI is not capable
|
||||
// of dealing with absolute paths
|
||||
let dir_path = get_file_path(resolved.dirfd())?;
|
||||
let dir_path = get_file_path(&resolved.dirfd().as_os_handle())?;
|
||||
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
|
||||
let target_path = target_path
|
||||
.strip_prefix(dir_path)
|
||||
@@ -401,7 +402,7 @@ pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize>
|
||||
fn strip_trailing_slashes_and_concatenate(resolved: &PathGet) -> Result<Option<PathBuf>> {
|
||||
if resolved.path().ends_with('/') {
|
||||
let suffix = resolved.path().trim_end_matches('/');
|
||||
concatenate(resolved.dirfd(), Path::new(suffix)).map(Some)
|
||||
concatenate(&resolved.dirfd().as_os_handle(), Path::new(suffix)).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@@ -492,14 +493,15 @@ pub(crate) fn path_filestat_set_times(
|
||||
let file = OpenOptions::new()
|
||||
.access_mode(AccessMode::FILE_WRITE_ATTRIBUTES.bits())
|
||||
.open(path)?;
|
||||
fd_filestat_set_times_impl(&file, st_atim, st_mtim, fst_flags)
|
||||
let modifiable_fd = Descriptor::OsHandle(OsHandle::from(file));
|
||||
fd_filestat_set_times_impl(&modifiable_fd, st_atim, st_mtim, fst_flags)
|
||||
}
|
||||
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
|
||||
use std::os::windows::fs::{symlink_dir, symlink_file};
|
||||
use winx::winerror::WinError;
|
||||
|
||||
let old_path = concatenate(resolved.dirfd(), Path::new(old_path))?;
|
||||
let old_path = concatenate(&resolved.dirfd().as_os_handle(), Path::new(old_path))?;
|
||||
let new_path = resolved.concatenate()?;
|
||||
|
||||
// try creating a file symlink
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
use crate::fdentry::Descriptor;
|
||||
use crate::hostcalls_impl::PathGet;
|
||||
use crate::{wasi, Error, Result};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
@@ -12,7 +13,15 @@ pub(crate) trait PathGetExt {
|
||||
|
||||
impl PathGetExt for PathGet {
|
||||
fn concatenate(&self) -> Result<PathBuf> {
|
||||
concatenate(self.dirfd(), Path::new(self.path()))
|
||||
match self.dirfd() {
|
||||
Descriptor::OsHandle(file) => concatenate(file, Path::new(self.path())),
|
||||
Descriptor::VirtualFile(_virt) => {
|
||||
panic!("concatenate on a virtual base");
|
||||
}
|
||||
Descriptor::Stdin | Descriptor::Stdout | Descriptor::Stderr => {
|
||||
unreachable!("streams do not have paths and should not be accessible via PathGet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +135,7 @@ pub(crate) fn strip_extended_prefix<P: AsRef<OsStr>>(path: P) -> OsString {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn concatenate<P: AsRef<Path>>(dirfd: &File, path: P) -> Result<PathBuf> {
|
||||
pub(crate) fn concatenate<P: AsRef<Path>>(file: &File, path: P) -> Result<PathBuf> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
// WASI is not able to deal with absolute paths
|
||||
@@ -135,7 +144,7 @@ pub(crate) fn concatenate<P: AsRef<Path>>(dirfd: &File, path: P) -> Result<PathB
|
||||
return Err(Error::ENOTCAPABLE);
|
||||
}
|
||||
|
||||
let dir_path = get_file_path(dirfd)?;
|
||||
let dir_path = get_file_path(file)?;
|
||||
// concatenate paths
|
||||
let mut out_path = PathBuf::from(dir_path);
|
||||
out_path.push(path.as_ref());
|
||||
|
||||
@@ -222,6 +222,9 @@ fn handle_rw_event(event: FdEventData, out_events: &mut Vec<wasi::__wasi_event_t
|
||||
Descriptor::Stdin => Ok(1),
|
||||
// On Unix, ioctl(FIONREAD) will return 0 for stdout/stderr. Emulate the same behavior on Windows.
|
||||
Descriptor::Stdout | Descriptor::Stderr => Ok(0),
|
||||
Descriptor::VirtualFile(_) => {
|
||||
panic!("virtual files do not get rw events");
|
||||
}
|
||||
};
|
||||
|
||||
let new_event = make_rw_event(&event, size);
|
||||
@@ -295,6 +298,9 @@ pub(crate) fn poll_oneoff(
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
Descriptor::VirtualFile(_) => {
|
||||
panic!("virtual files do not get rw events");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user