#![allow(non_camel_case_types)]
use crate::old::snapshot_0::hostcalls_impl::PathGet;
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::{Path, PathBuf};
use winapi::shared::winerror;
pub(crate) trait PathGetExt {
fn concatenate(&self) -> WasiResult<PathBuf>;
}
impl PathGetExt for PathGet {
fn concatenate(&self) -> WasiResult<PathBuf> {
concatenate(self.dirfd(), Path::new(self.path()))
}
}
pub(crate) fn path_open_rights(
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
oflags: wasi::__wasi_oflags_t,
fdflags: wasi::__wasi_fdflags_t,
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
let mut needed_inheriting = rights_base | rights_inheriting;
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
}
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
{
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
}
(needed_base, needed_inheriting)
}
pub(crate) fn openat(dirfd: &File, path: &str) -> WasiResult<File> {
use std::fs::OpenOptions;
use std::os::windows::fs::OpenOptionsExt;
use winx::file::Flags;
let path = concatenate(dirfd, Path::new(path))?;
let err = match OpenOptions::new()
.read(true)
.custom_flags(Flags::FILE_FLAG_BACKUP_SEMANTICS.bits())
.open(&path)
{
Ok(file) => return Ok(file),
Err(e) => e,
};
if let Some(code) = err.raw_os_error() {
tracing::debug!("openat error={:?}", code);
if code as u32 == winerror::ERROR_INVALID_NAME {
return Err(WasiError::ENOTDIR);
}
}
Err(err.into())
}
pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> WasiResult<String> {
use winx::file::get_file_path;
let path = concatenate(dirfd, Path::new(s_path))?;
let err = match path.read_link() {
Ok(target_path) => {
let dir_path = get_file_path(dirfd)?;
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
let target_path = target_path
.strip_prefix(dir_path)
.map_err(|_| WasiError::ENOTCAPABLE)?;
let target_path = target_path.to_str().ok_or(WasiError::EILSEQ)?;
return Ok(target_path.to_owned());
}
Err(e) => e,
};
if let Some(code) = err.raw_os_error() {
tracing::debug!("readlinkat error={:?}", code);
if code as u32 == winerror::ERROR_INVALID_NAME {
if s_path.ends_with('/') {
let path = concatenate(dirfd, Path::new(s_path.trim_end_matches('/')))?;
if path.exists() && !path.is_dir() {
return Err(WasiError::ENOTDIR);
}
}
}
}
Err(err.into())
}
pub(crate) fn strip_extended_prefix<P: AsRef<OsStr>>(path: P) -> OsString {
let path: Vec<u16> = path.as_ref().encode_wide().collect();
if &[92, 92, 63, 92] == &path[0..4] {
OsString::from_wide(&path[4..])
} else {
OsString::from_wide(&path)
}
}
pub(crate) fn concatenate<P: AsRef<Path>>(dirfd: &File, path: P) -> WasiResult<PathBuf> {
use winx::file::get_file_path;
if path.as_ref().is_absolute() {
return Err(WasiError::ENOTCAPABLE);
}
let dir_path = get_file_path(dirfd)?;
let mut out_path = PathBuf::from(dir_path);
out_path.push(path.as_ref());
let out_path = PathBuf::from(strip_extended_prefix(out_path));
tracing::debug!("out_path={:?}", out_path);
Ok(out_path)
}