From 8cbeb48c8a419ac0fede992fb6d2889381d82ebf Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Tue, 23 Jul 2019 19:14:44 +0200 Subject: [PATCH 1/7] Implement fd_filestat_get for all platforms --- src/hostcalls_impl/fs.rs | 37 +++++++++++++++++-- src/sys/unix/hostcalls_impl/fs.rs | 49 ++++++++++++++++++++++--- src/sys/windows/hostcalls_impl/fs.rs | 35 ++++++++++++++++-- winx/src/file.rs | 54 ++++++++++++++++++++++++++-- 4 files changed, 163 insertions(+), 12 deletions(-) diff --git a/src/hostcalls_impl/fs.rs b/src/hostcalls_impl/fs.rs index 3f5a9b1..6352e84 100644 --- a/src/hostcalls_impl/fs.rs +++ b/src/hostcalls_impl/fs.rs @@ -4,7 +4,7 @@ use crate::fdentry::{Descriptor, FdEntry}; use crate::memory::*; use crate::sys::{errno_from_host, host_impl, hostcalls_impl}; use crate::{host, wasm32, Result}; -use log::trace; +use log::{debug, trace}; use std::io::{self, Read, Seek, SeekFrom, Write}; pub(crate) fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> Result<()> { @@ -730,13 +730,46 @@ pub(crate) fn fd_filestat_get( .get_fd_entry(fd, 0, 0) .and_then(|fe| fe.fd_object.descriptor.as_file())?; - let host_filestat = hostcalls_impl::fd_filestat_get(fd)?; + let host_filestat = fd_filestat_get_impl(fd).map_err(|e| { + debug!("fd_filestat_get: error: {}", e); + e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) + })?; trace!(" | *filestat_ptr={:?}", host_filestat); enc_filestat_byref(memory, filestat_ptr, host_filestat) } +fn fd_filestat_get_impl(file: &std::fs::File) -> io::Result { + use std::convert::TryInto; + use std::time::{SystemTime, UNIX_EPOCH}; + fn timestamp(st: SystemTime) -> io::Result { + st.duration_since(UNIX_EPOCH) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))? + .as_nanos() + .try_into() + .map_err(|e: std::num::TryFromIntError| { + io::Error::new(io::ErrorKind::Other, e.to_string()) + }) + } + let metadata = file.metadata()?; + // On Windows all the information needed is either in libstd or provided by + // GetFileInformationByHandle winapi call. All of it is much easier to implement + // in libstd + Ok(host::__wasi_filestat_t { + st_dev: hostcalls_impl::device_id(file)?, + st_ino: hostcalls_impl::file_serial_no(file)?, + st_nlink: hostcalls_impl::num_hardlinks(file)? + .try_into() + .expect("overflow"), + st_size: metadata.len(), + st_atim: metadata.accessed().and_then(timestamp)?, + st_ctim: hostcalls_impl::change_time(file)?, + st_mtim: metadata.modified().and_then(timestamp)?, + st_filetype: hostcalls_impl::filetype(file)?, + }) +} + pub(crate) fn fd_filestat_set_times( wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t, diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs index 5842cee..73c6aee 100644 --- a/src/sys/unix/hostcalls_impl/fs.rs +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -12,6 +12,7 @@ use std::ffi::CString; use std::fs::File; use std::os::unix::fs::FileExt; use std::os::unix::prelude::{AsRawFd, FromRawFd}; +use std::io; pub(crate) fn fd_pread( file: &File, @@ -356,11 +357,49 @@ pub(crate) fn path_rename( } } -pub(crate) fn fd_filestat_get(fd: &File) -> Result { - use nix::sys::stat::fstat; - let filestat = - fstat(fd.as_raw_fd()).map_err(|err| host_impl::errno_from_nix(err.as_errno().unwrap()))?; - host_impl::filestat_from_nix(filestat) +pub(crate) fn num_hardlinks(file: &File) -> io::Result { + use std::os::unix::fs::MetadataExt; + Ok(file.metadata()?.nlink()) +} + +pub(crate) fn device_id(file: &File) -> io::Result { + use std::os::unix::fs::MetadataExt; + Ok(file.metadata()?.dev()) +} + +pub(crate) fn file_serial_no(file: &File) -> io::Result { + use std::os::unix::fs::MetadataExt; + Ok(file.metadata()?.ino()) +} + +pub(crate) fn filetype(file: &File) -> io::Result { + use std::os::unix::fs::FileTypeExt; + let ftype = file.metadata()?.file_type(); + let ret = if ftype.is_file() { + host::__WASI_FILETYPE_REGULAR_FILE + } else if ftype.is_dir() { + host::__WASI_FILETYPE_DIRECTORY + } else if ftype.is_symlink() { + host::__WASI_FILETYPE_SYMBOLIC_LINK + } else if ftype.is_char_device() { + host::__WASI_FILETYPE_CHARACTER_DEVICE + } else if ftype.is_block_device() { + host::__WASI_FILETYPE_BLOCK_DEVICE + } else if ftype.is_socket() || ftype.is_fifo() { + // TODO we should use getsockopt to find out if it's + // SOCKET_STREAM or SOCKET_DGRAM + host::__WASI_FILETYPE_SOCKET_STREAM + } else { + host::__WASI_FILETYPE_UNKNOWN + }; + + Ok(ret) +} + +pub(crate) fn change_time(file: &File) -> io::Result { + use std::os::unix::fs::MetadataExt; + use std::convert::TryInto; + Ok(file.metadata()?.ctime().try_into().unwrap()) } pub(crate) fn fd_filestat_set_times( diff --git a/src/sys/windows/hostcalls_impl/fs.rs b/src/sys/windows/hostcalls_impl/fs.rs index 942086b..00dea25 100644 --- a/src/sys/windows/hostcalls_impl/fs.rs +++ b/src/sys/windows/hostcalls_impl/fs.rs @@ -173,8 +173,39 @@ pub(crate) fn path_rename( unimplemented!("path_rename") } -pub(crate) fn fd_filestat_get(fd: &File) -> Result { - unimplemented!("fd_filestat_get") +pub(crate) fn num_hardlinks(file: &File) -> io::Result { + Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into()) +} + +pub(crate) fn device_id(file: &File) -> io::Result { + Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into()) +} + +pub(crate) fn file_serial_no(file: &File) -> io::Result { + let info = winx::file::get_fileinfo(file)?; + let high = info.nFileIndexHigh; + let low = info.nFileIndexLow; + let no = ((high as u64) << 32) | (low as u64); + Ok(no) +} + +pub(crate) fn change_time(file: &File) -> io::Result { + winx::file::change_time(file) +} + +pub(crate) fn filetype(file: &File) -> io::Result { + let ftype = file.metadata()?.file_type(); + let ret = if ftype.is_file() { + host::__WASI_FILETYPE_REGULAR_FILE + } else if ftype.is_dir() { + host::__WASI_FILETYPE_DIRECTORY + } else if ftype.is_symlink() { + host::__WASI_FILETYPE_SYMBOLIC_LINK + } else { + host::__WASI_FILETYPE_UNKNOWN + }; + + Ok(ret) } pub(crate) fn fd_filestat_set_times( diff --git a/winx/src/file.rs b/winx/src/file.rs index 3033976..e20d69f 100644 --- a/winx/src/file.rs +++ b/winx/src/file.rs @@ -1,9 +1,11 @@ #![allow(non_camel_case_types)] use crate::{winerror, Result}; -use std::ffi::{OsStr, OsString}; -use std::os::windows::prelude::{OsStrExt, OsStringExt, RawHandle}; +use std::ffi::{c_void, OsStr, OsString}; +use std::fs::File; +use std::io; +use std::os::windows::prelude::{AsRawHandle, OsStrExt, OsStringExt, RawHandle}; use winapi::shared::minwindef::{self, DWORD}; -use winapi::um::{fileapi, fileapi::GetFileType, winbase, winnt}; +use winapi::um::{fileapi, fileapi::GetFileType, minwinbase, winbase, winnt}; /// Maximum total path length for Unicode in Windows. /// [Maximum path length limitation]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation @@ -457,3 +459,49 @@ pub fn openat>( Ok(handle) } } + +// Taken from Rust libstd, file libstd/sys/windows/fs.rs +fn cvt(i: winapi::shared::minwindef::BOOL) -> io::Result<()> { + if i == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +pub fn get_fileinfo(file: &File) -> io::Result { + use fileapi::{GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION}; + use std::mem; + + let handle = file.as_raw_handle(); + let info = unsafe { + let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed(); + cvt(GetFileInformationByHandle(handle, &mut info))?; + info + }; + + Ok(info) +} + +pub fn change_time(file: &File) -> io::Result { + use fileapi::FILE_BASIC_INFO; + use minwinbase::FileBasicInfo; + use std::convert::TryInto; + use std::mem; + use winbase::GetFileInformationByHandleEx; + + let handle = file.as_raw_handle(); + let tm = unsafe { + let mut info: FILE_BASIC_INFO = mem::zeroed(); + let infosize = mem::size_of_val(&info); + cvt(GetFileInformationByHandleEx( + handle, + FileBasicInfo, + &mut info as *mut FILE_BASIC_INFO as *mut c_void, + infosize as u32, + ))?; + *info.ChangeTime.QuadPart() + }; + + Ok(tm.try_into().expect("overflow")) +} From b286fd6ad5646feb93b5aba30478481a61463f0e Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Wed, 24 Jul 2019 19:29:33 +0200 Subject: [PATCH 2/7] Remove an old comment --- src/hostcalls_impl/fs.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hostcalls_impl/fs.rs b/src/hostcalls_impl/fs.rs index 6352e84..e747876 100644 --- a/src/hostcalls_impl/fs.rs +++ b/src/hostcalls_impl/fs.rs @@ -753,9 +753,6 @@ fn fd_filestat_get_impl(file: &std::fs::File) -> io::Result Date: Wed, 24 Jul 2019 20:34:49 +0200 Subject: [PATCH 3/7] Remove panics from the syscall wrappers --- src/hostcalls_impl/fs.rs | 2 +- src/sys/unix/hostcalls_impl/fs.rs | 8 ++++---- src/sys/windows/hostcalls_impl/fs.rs | 2 +- winx/src/file.rs | 5 ++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/hostcalls_impl/fs.rs b/src/hostcalls_impl/fs.rs index e747876..05689e6 100644 --- a/src/hostcalls_impl/fs.rs +++ b/src/hostcalls_impl/fs.rs @@ -761,7 +761,7 @@ fn fd_filestat_get_impl(file: &std::fs::File) -> io::Result io::Result { Ok(ret) } -pub(crate) fn change_time(file: &File) -> io::Result { - use std::os::unix::fs::MetadataExt; +pub(crate) fn change_time(file: &File) -> io::Result { use std::convert::TryInto; - Ok(file.metadata()?.ctime().try_into().unwrap()) + use std::os::unix::fs::MetadataExt; + Ok(file.metadata()?.ctime()) } pub(crate) fn fd_filestat_set_times( diff --git a/src/sys/windows/hostcalls_impl/fs.rs b/src/sys/windows/hostcalls_impl/fs.rs index 00dea25..5f161f4 100644 --- a/src/sys/windows/hostcalls_impl/fs.rs +++ b/src/sys/windows/hostcalls_impl/fs.rs @@ -189,7 +189,7 @@ pub(crate) fn file_serial_no(file: &File) -> io::Result { Ok(no) } -pub(crate) fn change_time(file: &File) -> io::Result { +pub(crate) fn change_time(file: &File) -> io::Result { winx::file::change_time(file) } diff --git a/winx/src/file.rs b/winx/src/file.rs index e20d69f..de7f7ed 100644 --- a/winx/src/file.rs +++ b/winx/src/file.rs @@ -483,10 +483,9 @@ pub fn get_fileinfo(file: &File) -> io::Result io::Result { +pub fn change_time(file: &File) -> io::Result { use fileapi::FILE_BASIC_INFO; use minwinbase::FileBasicInfo; - use std::convert::TryInto; use std::mem; use winbase::GetFileInformationByHandleEx; @@ -503,5 +502,5 @@ pub fn change_time(file: &File) -> io::Result { *info.ChangeTime.QuadPart() }; - Ok(tm.try_into().expect("overflow")) + Ok(tm) } From 1b21b055c44ab7cb559bd97efae8c7bb27e56839 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Thu, 25 Jul 2019 09:15:47 +0200 Subject: [PATCH 4/7] Return WASI error type --- src/hostcalls_impl/fs.rs | 50 +++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/hostcalls_impl/fs.rs b/src/hostcalls_impl/fs.rs index 05689e6..8ef153a 100644 --- a/src/hostcalls_impl/fs.rs +++ b/src/hostcalls_impl/fs.rs @@ -730,40 +730,52 @@ pub(crate) fn fd_filestat_get( .get_fd_entry(fd, 0, 0) .and_then(|fe| fe.fd_object.descriptor.as_file())?; - let host_filestat = fd_filestat_get_impl(fd).map_err(|e| { - debug!("fd_filestat_get: error: {}", e); - e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) - })?; + let host_filestat = fd_filestat_get_impl(fd)?; trace!(" | *filestat_ptr={:?}", host_filestat); enc_filestat_byref(memory, filestat_ptr, host_filestat) } -fn fd_filestat_get_impl(file: &std::fs::File) -> io::Result { +fn fd_filestat_get_impl(file: &std::fs::File) -> Result { use std::convert::TryInto; use std::time::{SystemTime, UNIX_EPOCH}; - fn timestamp(st: SystemTime) -> io::Result { + + fn convert_err(e: io::Error) -> host::__wasi_errno_t { + debug!("fd_filestat_get: os error: {}", e); + e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) + } + + fn timestamp(st: SystemTime) -> Result { st.duration_since(UNIX_EPOCH) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))? + .map_err(|_| host::__WASI_EINVAL)? // date earlier than UNIX_EPOCH .as_nanos() .try_into() - .map_err(|e: std::num::TryFromIntError| { - io::Error::new(io::ErrorKind::Other, e.to_string()) - }) + .map_err(|_| host::__WASI_EOVERFLOW) // u128 doesn't fit into u64 } - let metadata = file.metadata()?; + + let metadata = file.metadata().map_err(convert_err)?; Ok(host::__wasi_filestat_t { - st_dev: hostcalls_impl::device_id(file)?, - st_ino: hostcalls_impl::file_serial_no(file)?, - st_nlink: hostcalls_impl::num_hardlinks(file)? + st_dev: hostcalls_impl::device_id(file).map_err(convert_err)?, + st_ino: hostcalls_impl::file_serial_no(file).map_err(convert_err)?, + st_nlink: hostcalls_impl::num_hardlinks(file) + .map_err(convert_err)? .try_into() - .expect("overflow"), + .map_err(|_| host::__WASI_EOVERFLOW)?, // u64 doesn't fit into u32 st_size: metadata.len(), - st_atim: metadata.accessed().and_then(timestamp)?, - st_ctim: hostcalls_impl::change_time(file)?.try_into().expect("overflow"), - st_mtim: metadata.modified().and_then(timestamp)?, - st_filetype: hostcalls_impl::filetype(file)?, + st_atim: metadata + .accessed() + .map_err(convert_err) + .and_then(timestamp)?, + st_ctim: hostcalls_impl::change_time(file) + .map_err(convert_err)? + .try_into() + .map_err(|_| host::__WASI_EOVERFLOW)?, // i64 doesn't fit into u64 + st_mtim: metadata + .modified() + .map_err(convert_err) + .and_then(timestamp)?, + st_filetype: hostcalls_impl::filetype(file).map_err(convert_err)?, }) } From 27bbb501d1ea1cfacb83baeff148321e4c677ce2 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Thu, 25 Jul 2019 16:51:18 +0200 Subject: [PATCH 5/7] Reuse Metadata if possible to save syscalls. --- src/hostcalls_impl/fs.rs | 10 +++++----- src/sys/unix/hostcalls_impl/fs.rs | 23 +++++++++++------------ src/sys/windows/hostcalls_impl/fs.rs | 14 +++++++------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/hostcalls_impl/fs.rs b/src/hostcalls_impl/fs.rs index 8ef153a..c4c96a5 100644 --- a/src/hostcalls_impl/fs.rs +++ b/src/hostcalls_impl/fs.rs @@ -756,9 +756,9 @@ fn fd_filestat_get_impl(file: &std::fs::File) -> Result let metadata = file.metadata().map_err(convert_err)?; Ok(host::__wasi_filestat_t { - st_dev: hostcalls_impl::device_id(file).map_err(convert_err)?, - st_ino: hostcalls_impl::file_serial_no(file).map_err(convert_err)?, - st_nlink: hostcalls_impl::num_hardlinks(file) + st_dev: hostcalls_impl::device_id(file, &metadata).map_err(convert_err)?, + st_ino: hostcalls_impl::file_serial_no(file, &metadata).map_err(convert_err)?, + st_nlink: hostcalls_impl::num_hardlinks(file, &metadata) .map_err(convert_err)? .try_into() .map_err(|_| host::__WASI_EOVERFLOW)?, // u64 doesn't fit into u32 @@ -767,7 +767,7 @@ fn fd_filestat_get_impl(file: &std::fs::File) -> Result .accessed() .map_err(convert_err) .and_then(timestamp)?, - st_ctim: hostcalls_impl::change_time(file) + st_ctim: hostcalls_impl::change_time(file, &metadata) .map_err(convert_err)? .try_into() .map_err(|_| host::__WASI_EOVERFLOW)?, // i64 doesn't fit into u64 @@ -775,7 +775,7 @@ fn fd_filestat_get_impl(file: &std::fs::File) -> Result .modified() .map_err(convert_err) .and_then(timestamp)?, - st_filetype: hostcalls_impl::filetype(file).map_err(convert_err)?, + st_filetype: hostcalls_impl::filetype(&metadata).map_err(convert_err)?, }) } diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs index 89975e3..a0b0a9f 100644 --- a/src/sys/unix/hostcalls_impl/fs.rs +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -9,7 +9,7 @@ use crate::sys::host_impl; use crate::{host, wasm32, Result}; use nix::libc::{self, c_long, c_void, off_t}; use std::ffi::CString; -use std::fs::File; +use std::fs::{File, Metadata}; use std::io; use std::os::unix::fs::FileExt; use std::os::unix::prelude::{AsRawFd, FromRawFd}; @@ -357,24 +357,24 @@ pub(crate) fn path_rename( } } -pub(crate) fn num_hardlinks(file: &File) -> io::Result { +pub(crate) fn num_hardlinks(_file: &File, metadata: &Metadata) -> io::Result { use std::os::unix::fs::MetadataExt; - Ok(file.metadata()?.nlink()) + Ok(metadata.nlink()) } -pub(crate) fn device_id(file: &File) -> io::Result { +pub(crate) fn device_id(_file: &File, metadata: &Metadata) -> io::Result { use std::os::unix::fs::MetadataExt; - Ok(file.metadata()?.dev()) + Ok(metadata.dev()) } -pub(crate) fn file_serial_no(file: &File) -> io::Result { +pub(crate) fn file_serial_no(_file: &File, metadata: &Metadata) -> io::Result { use std::os::unix::fs::MetadataExt; - Ok(file.metadata()?.ino()) + Ok(metadata.ino()) } -pub(crate) fn filetype(file: &File) -> io::Result { +pub(crate) fn filetype(metadata: &Metadata) -> io::Result { use std::os::unix::fs::FileTypeExt; - let ftype = file.metadata()?.file_type(); + let ftype = metadata.file_type(); let ret = if ftype.is_file() { host::__WASI_FILETYPE_REGULAR_FILE } else if ftype.is_dir() { @@ -396,10 +396,9 @@ pub(crate) fn filetype(file: &File) -> io::Result { Ok(ret) } -pub(crate) fn change_time(file: &File) -> io::Result { - use std::convert::TryInto; +pub(crate) fn change_time(_file: &File, metadata: &Metadata) -> io::Result { use std::os::unix::fs::MetadataExt; - Ok(file.metadata()?.ctime()) + Ok(metadata.ctime()) } pub(crate) fn fd_filestat_set_times( diff --git a/src/sys/windows/hostcalls_impl/fs.rs b/src/sys/windows/hostcalls_impl/fs.rs index 5f161f4..abbebaa 100644 --- a/src/sys/windows/hostcalls_impl/fs.rs +++ b/src/sys/windows/hostcalls_impl/fs.rs @@ -7,7 +7,7 @@ use crate::sys::errno_from_host; use crate::sys::fdentry_impl::determine_type_rights; use crate::sys::host_impl; use crate::{host, Result}; -use std::fs::File; +use std::fs::{File, Metadata}; use std::io::{self, Seek, SeekFrom}; use std::os::windows::fs::FileExt; use std::os::windows::prelude::{AsRawHandle, FromRawHandle}; @@ -173,15 +173,15 @@ pub(crate) fn path_rename( unimplemented!("path_rename") } -pub(crate) fn num_hardlinks(file: &File) -> io::Result { +pub(crate) fn num_hardlinks(file: &File, _metadata: &Metadata) -> io::Result { Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into()) } -pub(crate) fn device_id(file: &File) -> io::Result { +pub(crate) fn device_id(file: &File, _metadata: &Metadata) -> io::Result { Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into()) } -pub(crate) fn file_serial_no(file: &File) -> io::Result { +pub(crate) fn file_serial_no(file: &File, _metadata: &Metadata) -> io::Result { let info = winx::file::get_fileinfo(file)?; let high = info.nFileIndexHigh; let low = info.nFileIndexLow; @@ -189,12 +189,12 @@ pub(crate) fn file_serial_no(file: &File) -> io::Result { Ok(no) } -pub(crate) fn change_time(file: &File) -> io::Result { +pub(crate) fn change_time(file: &File, _metadata: &Metadata) -> io::Result { winx::file::change_time(file) } -pub(crate) fn filetype(file: &File) -> io::Result { - let ftype = file.metadata()?.file_type(); +pub(crate) fn filetype(metadata: &Metadata) -> io::Result { + let ftype = metadata.file_type(); let ret = if ftype.is_file() { host::__WASI_FILETYPE_REGULAR_FILE } else if ftype.is_dir() { From 7cef07bc3ecaf184f0ab1b8f6a16c78e94f397b9 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Fri, 26 Jul 2019 09:53:32 +0200 Subject: [PATCH 6/7] Refactor the change for two separate fd_filestat_get_impl --- src/helpers.rs | 10 ++++++ src/hostcalls_impl/fs.rs | 46 ++---------------------- src/lib.rs | 1 + src/sys/unix/hostcalls_impl/fs.rs | 53 +++++++++++++++++----------- src/sys/windows/hostcalls_impl/fs.rs | 35 +++++++++++++++++- 5 files changed, 80 insertions(+), 65 deletions(-) create mode 100644 src/helpers.rs diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..08755b5 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,10 @@ +use crate::{host, Result}; +use std::convert::TryInto; +use std::time::{SystemTime, UNIX_EPOCH}; +pub(crate) fn systemtime_to_timestamp(st: SystemTime) -> Result { + st.duration_since(UNIX_EPOCH) + .map_err(|_| host::__WASI_EINVAL)? // date earlier than UNIX_EPOCH + .as_nanos() + .try_into() + .map_err(|_| host::__WASI_EOVERFLOW) // u128 doesn't fit into u64 +} diff --git a/src/hostcalls_impl/fs.rs b/src/hostcalls_impl/fs.rs index c4c96a5..e05ceec 100644 --- a/src/hostcalls_impl/fs.rs +++ b/src/hostcalls_impl/fs.rs @@ -4,7 +4,7 @@ use crate::fdentry::{Descriptor, FdEntry}; use crate::memory::*; use crate::sys::{errno_from_host, host_impl, hostcalls_impl}; use crate::{host, wasm32, Result}; -use log::{debug, trace}; +use log::trace; use std::io::{self, Read, Seek, SeekFrom, Write}; pub(crate) fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> Result<()> { @@ -730,55 +730,13 @@ pub(crate) fn fd_filestat_get( .get_fd_entry(fd, 0, 0) .and_then(|fe| fe.fd_object.descriptor.as_file())?; - let host_filestat = fd_filestat_get_impl(fd)?; + let host_filestat = hostcalls_impl::fd_filestat_get_impl(fd)?; trace!(" | *filestat_ptr={:?}", host_filestat); enc_filestat_byref(memory, filestat_ptr, host_filestat) } -fn fd_filestat_get_impl(file: &std::fs::File) -> Result { - use std::convert::TryInto; - use std::time::{SystemTime, UNIX_EPOCH}; - - fn convert_err(e: io::Error) -> host::__wasi_errno_t { - debug!("fd_filestat_get: os error: {}", e); - e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) - } - - fn timestamp(st: SystemTime) -> Result { - st.duration_since(UNIX_EPOCH) - .map_err(|_| host::__WASI_EINVAL)? // date earlier than UNIX_EPOCH - .as_nanos() - .try_into() - .map_err(|_| host::__WASI_EOVERFLOW) // u128 doesn't fit into u64 - } - - let metadata = file.metadata().map_err(convert_err)?; - Ok(host::__wasi_filestat_t { - st_dev: hostcalls_impl::device_id(file, &metadata).map_err(convert_err)?, - st_ino: hostcalls_impl::file_serial_no(file, &metadata).map_err(convert_err)?, - st_nlink: hostcalls_impl::num_hardlinks(file, &metadata) - .map_err(convert_err)? - .try_into() - .map_err(|_| host::__WASI_EOVERFLOW)?, // u64 doesn't fit into u32 - st_size: metadata.len(), - st_atim: metadata - .accessed() - .map_err(convert_err) - .and_then(timestamp)?, - st_ctim: hostcalls_impl::change_time(file, &metadata) - .map_err(convert_err)? - .try_into() - .map_err(|_| host::__WASI_EOVERFLOW)?, // i64 doesn't fit into u64 - st_mtim: metadata - .modified() - .map_err(convert_err) - .and_then(timestamp)?, - st_filetype: hostcalls_impl::filetype(&metadata).map_err(convert_err)?, - }) -} - pub(crate) fn fd_filestat_set_times( wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t, diff --git a/src/lib.rs b/src/lib.rs index 690358c..23f3760 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ mod ctx; mod fdentry; +mod helpers; mod hostcalls_impl; mod sys; #[macro_use] diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs index a0b0a9f..32c3446 100644 --- a/src/sys/unix/hostcalls_impl/fs.rs +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -3,11 +3,13 @@ use super::fs_helpers::*; use crate::ctx::WasiCtx; use crate::fdentry::FdEntry; +use crate::helpers::systemtime_to_timestamp; use crate::sys::errno_from_host; use crate::sys::fdentry_impl::determine_type_rights; use crate::sys::host_impl; use crate::{host, wasm32, Result}; use nix::libc::{self, c_long, c_void, off_t}; +use std::convert::TryInto; use std::ffi::CString; use std::fs::{File, Metadata}; use std::io; @@ -357,25 +359,43 @@ pub(crate) fn path_rename( } } -pub(crate) fn num_hardlinks(_file: &File, metadata: &Metadata) -> io::Result { +pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { use std::os::unix::fs::MetadataExt; - Ok(metadata.nlink()) -} -pub(crate) fn device_id(_file: &File, metadata: &Metadata) -> io::Result { - use std::os::unix::fs::MetadataExt; - Ok(metadata.dev()) -} + fn convert_err(e: io::Error) -> host::__wasi_errno_t { + log::debug!("fd_filestat_get: os error: {}", e); + e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) + } -pub(crate) fn file_serial_no(_file: &File, metadata: &Metadata) -> io::Result { - use std::os::unix::fs::MetadataExt; - Ok(metadata.ino()) + let metadata = file.metadata().map_err(convert_err)?; + Ok(host::__wasi_filestat_t { + st_dev: metadata.dev(), + st_ino: metadata.ino(), + st_nlink: metadata + .nlink() + .try_into() + .map_err(|_| host::__WASI_EOVERFLOW)?, // u64 doesn't fit into u32 + st_size: metadata.len(), + st_atim: metadata + .accessed() + .map_err(convert_err) + .and_then(systemtime_to_timestamp)?, + st_ctim: metadata + .ctime() + .try_into() + .map_err(|_| host::__WASI_EOVERFLOW)?, // i64 doesn't fit into u64 + st_mtim: metadata + .modified() + .map_err(convert_err) + .and_then(systemtime_to_timestamp)?, + st_filetype: filetype(&metadata), + }) } -pub(crate) fn filetype(metadata: &Metadata) -> io::Result { +fn filetype(metadata: &Metadata) -> host::__wasi_filetype_t { use std::os::unix::fs::FileTypeExt; let ftype = metadata.file_type(); - let ret = if ftype.is_file() { + if ftype.is_file() { host::__WASI_FILETYPE_REGULAR_FILE } else if ftype.is_dir() { host::__WASI_FILETYPE_DIRECTORY @@ -391,14 +411,7 @@ pub(crate) fn filetype(metadata: &Metadata) -> io::Result io::Result { - use std::os::unix::fs::MetadataExt; - Ok(metadata.ctime()) + } } pub(crate) fn fd_filestat_set_times( diff --git a/src/sys/windows/hostcalls_impl/fs.rs b/src/sys/windows/hostcalls_impl/fs.rs index abbebaa..90b9e5a 100644 --- a/src/sys/windows/hostcalls_impl/fs.rs +++ b/src/sys/windows/hostcalls_impl/fs.rs @@ -3,10 +3,12 @@ use super::fs_helpers::*; use crate::ctx::WasiCtx; use crate::fdentry::FdEntry; +use crate::helpers::systemtime_to_timestamp; use crate::sys::errno_from_host; use crate::sys::fdentry_impl::determine_type_rights; use crate::sys::host_impl; use crate::{host, Result}; +use std::convert::TryInto; use std::fs::{File, Metadata}; use std::io::{self, Seek, SeekFrom}; use std::os::windows::fs::FileExt; @@ -193,7 +195,38 @@ pub(crate) fn change_time(file: &File, _metadata: &Metadata) -> io::Result winx::file::change_time(file) } -pub(crate) fn filetype(metadata: &Metadata) -> io::Result { +pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { + fn convert_err(e: io::Error) -> host::__wasi_errno_t { + log::debug!("fd_filestat_get: os error: {}", e); + e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) + } + + let metadata = file.metadata().map_err(convert_err)?; + Ok(host::__wasi_filestat_t { + st_dev: device_id(file, &metadata).map_err(convert_err)?, + st_ino: file_serial_no(file, &metadata).map_err(convert_err)?, + st_nlink: num_hardlinks(file, &metadata) + .map_err(convert_err)? + .try_into() + .map_err(|_| host::__WASI_EOVERFLOW)?, // u64 doesn't fit into u32 + st_size: metadata.len(), + st_atim: metadata + .accessed() + .map_err(convert_err) + .and_then(systemtime_to_timestamp)?, + st_ctim: change_time(file, &metadata) + .map_err(convert_err)? + .try_into() + .map_err(|_| host::__WASI_EOVERFLOW)?, // i64 doesn't fit into u64 + st_mtim: metadata + .modified() + .map_err(convert_err) + .and_then(systemtime_to_timestamp)?, + st_filetype: filetype(&metadata).map_err(convert_err)?, + }) +} + +fn filetype(metadata: &Metadata) -> io::Result { let ftype = metadata.file_type(); let ret = if ftype.is_file() { host::__WASI_FILETYPE_REGULAR_FILE From d5915857766947298c21d6604a4664b1e5392407 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Fri, 26 Jul 2019 14:05:25 +0200 Subject: [PATCH 7/7] Refactor error handling --- src/sys/mod.rs | 14 ++++++++++++-- src/sys/unix/hostcalls_impl/fs.rs | 14 ++++---------- src/sys/windows/hostcalls_impl/fs.rs | 23 +++++++++-------------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/sys/mod.rs b/src/sys/mod.rs index dcad0ea..8a60ea6 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -6,17 +6,27 @@ cfg_if! { mod unix; pub use self::unix::*; - pub fn errno_from_host(err: i32) -> host::__wasi_errno_t { + pub(crate) fn errno_from_host(err: i32) -> host::__wasi_errno_t { host_impl::errno_from_nix(nix::errno::from_i32(err)) } } else if #[cfg(windows)] { mod windows; pub use self::windows::*; - pub fn errno_from_host(err: i32) -> host::__wasi_errno_t { + pub(crate) fn errno_from_host(err: i32) -> host::__wasi_errno_t { host_impl::errno_from_win(winx::winerror::WinError::from_u32(err as u32)) } } else { compile_error!("wasi-common doesn't compile for this platform yet"); } } + +pub(crate) fn errno_from_ioerror(e: std::io::Error) -> host::__wasi_errno_t { + match e.raw_os_error() { + Some(code) => errno_from_host(code), + None => { + log::debug!("Inconvertible OS error: {}", e); + host::__WASI_EIO + } + } +} diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs index 32c3446..13cdbed 100644 --- a/src/sys/unix/hostcalls_impl/fs.rs +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -4,15 +4,14 @@ use super::fs_helpers::*; use crate::ctx::WasiCtx; use crate::fdentry::FdEntry; use crate::helpers::systemtime_to_timestamp; -use crate::sys::errno_from_host; use crate::sys::fdentry_impl::determine_type_rights; use crate::sys::host_impl; +use crate::sys::{errno_from_host, errno_from_ioerror}; use crate::{host, wasm32, Result}; use nix::libc::{self, c_long, c_void, off_t}; use std::convert::TryInto; use std::ffi::CString; use std::fs::{File, Metadata}; -use std::io; use std::os::unix::fs::FileExt; use std::os::unix::prelude::{AsRawFd, FromRawFd}; @@ -362,12 +361,7 @@ pub(crate) fn path_rename( pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { use std::os::unix::fs::MetadataExt; - fn convert_err(e: io::Error) -> host::__wasi_errno_t { - log::debug!("fd_filestat_get: os error: {}", e); - e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) - } - - let metadata = file.metadata().map_err(convert_err)?; + let metadata = file.metadata().map_err(errno_from_ioerror)?; Ok(host::__wasi_filestat_t { st_dev: metadata.dev(), st_ino: metadata.ino(), @@ -378,7 +372,7 @@ pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result Result io::Result } pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { - fn convert_err(e: io::Error) -> host::__wasi_errno_t { - log::debug!("fd_filestat_get: os error: {}", e); - e.raw_os_error().map_or(host::__WASI_EIO, errno_from_host) - } - - let metadata = file.metadata().map_err(convert_err)?; + let metadata = file.metadata().map_err(errno_from_ioerror)?; Ok(host::__wasi_filestat_t { - st_dev: device_id(file, &metadata).map_err(convert_err)?, - st_ino: file_serial_no(file, &metadata).map_err(convert_err)?, + st_dev: device_id(file, &metadata).map_err(errno_from_ioerror)?, + st_ino: file_serial_no(file, &metadata).map_err(errno_from_ioerror)?, st_nlink: num_hardlinks(file, &metadata) - .map_err(convert_err)? + .map_err(errno_from_ioerror)? .try_into() .map_err(|_| host::__WASI_EOVERFLOW)?, // u64 doesn't fit into u32 st_size: metadata.len(), st_atim: metadata .accessed() - .map_err(convert_err) + .map_err(errno_from_ioerror) .and_then(systemtime_to_timestamp)?, st_ctim: change_time(file, &metadata) - .map_err(convert_err)? + .map_err(errno_from_ioerror)? .try_into() .map_err(|_| host::__WASI_EOVERFLOW)?, // i64 doesn't fit into u64 st_mtim: metadata .modified() - .map_err(convert_err) + .map_err(errno_from_ioerror) .and_then(systemtime_to_timestamp)?, - st_filetype: filetype(&metadata).map_err(convert_err)?, + st_filetype: filetype(&metadata).map_err(errno_from_ioerror)?, }) }