Skip to content
This repository has been archived by the owner on Nov 9, 2019. It is now read-only.

Commit

Permalink
Implement fd_filestat_get for all platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
marmistrz committed Jul 24, 2019
1 parent 86ae6e3 commit 6610d03
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 12 deletions.
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ cfg_if::cfg_if! {
"file_pread_pwrite" => false,
"renumber" => false,
"file_seek_tell" => false,
"file_allocate" => false,
_ => true,
}
} else {
Expand Down
37 changes: 35 additions & 2 deletions src/hostcalls_impl/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()> {
Expand Down Expand Up @@ -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<host::__wasi_filestat_t> {
use std::convert::TryInto;
use std::time::{SystemTime, UNIX_EPOCH};
fn timestamp(st: SystemTime) -> io::Result<u64> {
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,
Expand Down
49 changes: 44 additions & 5 deletions src/sys/unix/hostcalls_impl/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -356,11 +357,49 @@ pub(crate) fn path_rename(
}
}

pub(crate) fn fd_filestat_get(fd: &File) -> Result<host::__wasi_filestat_t> {
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<u64> {
use std::os::unix::fs::MetadataExt;
Ok(file.metadata()?.nlink())
}

pub(crate) fn device_id(file: &File) -> io::Result<u64> {
use std::os::unix::fs::MetadataExt;
Ok(file.metadata()?.dev())
}

pub(crate) fn file_serial_no(file: &File) -> io::Result<u64> {
use std::os::unix::fs::MetadataExt;
Ok(file.metadata()?.ino())
}

pub(crate) fn filetype(file: &File) -> io::Result<host::__wasi_filetype_t> {
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<u64> {
use std::os::unix::fs::MetadataExt;
use std::convert::TryInto;
Ok(file.metadata()?.ctime().try_into().unwrap())
}

pub(crate) fn fd_filestat_set_times(
Expand Down
35 changes: 33 additions & 2 deletions src/sys/windows/hostcalls_impl/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,39 @@ pub(crate) fn path_rename(
unimplemented!("path_rename")
}

pub(crate) fn fd_filestat_get(fd: &File) -> Result<host::__wasi_filestat_t> {
unimplemented!("fd_filestat_get")
pub(crate) fn num_hardlinks(file: &File) -> io::Result<u64> {
Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into())
}

pub(crate) fn device_id(file: &File) -> io::Result<u64> {
Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into())
}

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 = ((high as u64) << 32) | (low as u64);
Ok(no)
}

pub(crate) fn change_time(file: &File) -> io::Result<u64> {
winx::file::change_time(file)
}

pub(crate) fn filetype(file: &File) -> io::Result<host::__wasi_filetype_t> {
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(
Expand Down
53 changes: 50 additions & 3 deletions winx/src/file.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -457,3 +459,48 @@ pub fn openat<S: AsRef<OsStr>>(
Ok(handle)
}
}

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<fileapi::BY_HANDLE_FILE_INFORMATION> {
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<u64> {
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().unwrap())
}

0 comments on commit 6610d03

Please sign in to comment.