Dynamically load utimensat if exists on the host (#535)
* Dynamically load utimensat if exists on the host This commit introduces a change to file time management for *nix based hosts in that it firstly tries to load `utimensat` symbol, and if it doesn't exist, then falls back to `utimes` instead. This change is borrowing very heavily from [filetime] crate, however, it introduces a couple of helpers and methods specific to WASI use case (or more generally, to a use case which requires modifying times of entities specified by a pair `(DirFD, RelativePath)` rather than the typical file time specification based only absolute path or raw file descriptor as is the case with [filetime] crate. The trick here is, that on kernels which do not have `utimensat` symbol, this implementation emulates this behaviour by a combination of `openat` and `utimes`. This commit also is meant to address #516. [filetime]: https://github.com/alexcrichton/filetime * Fix symlink NOFOLLOW flag setting * Add docs and specify UTIME_NOW/OMIT on Linux Previously, we relied on [libc] crate for `UTIME_NOW` and `UTIME_OMIT` constants on Linux. However, following the convention assumed in [filetime] crate, this is now changed to directly specified by us in our crate. [libc]: https://github.com/rust-lang/libc [filetime]: https://github.com/alexcrichton/filetime * Refactor UTIME_NOW/OMIT for BSD * Address final discussion points
This commit is contained in:
committed by
Alex Crichton
parent
5a1845b4ca
commit
0006a2af95
@@ -1,6 +1,5 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_unsafe)]
|
||||
use super::fs_helpers::*;
|
||||
use crate::helpers::systemtime_to_timestamp;
|
||||
use crate::hostcalls_impl::{FileType, PathGet};
|
||||
use crate::sys::host_impl;
|
||||
@@ -281,25 +280,8 @@ pub(crate) fn path_filestat_set_times(
|
||||
st_mtim: wasi::__wasi_timestamp_t,
|
||||
fst_flags: wasi::__wasi_fstflags_t,
|
||||
) -> Result<()> {
|
||||
use nix::sys::stat::{utimensat, UtimensatFlags};
|
||||
use nix::sys::time::{TimeSpec, TimeValLike};
|
||||
|
||||
// FIXME this should be a part of nix
|
||||
fn timespec_omit() -> TimeSpec {
|
||||
let raw_ts = libc::timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: utime_omit(),
|
||||
};
|
||||
unsafe { std::mem::transmute(raw_ts) }
|
||||
};
|
||||
|
||||
fn timespec_now() -> TimeSpec {
|
||||
let raw_ts = libc::timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: utime_now(),
|
||||
};
|
||||
unsafe { std::mem::transmute(raw_ts) }
|
||||
};
|
||||
use super::super::filetime::*;
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
|
||||
let set_atim = fst_flags & wasi::__WASI_FILESTAT_SET_ATIM != 0;
|
||||
let set_atim_now = fst_flags & wasi::__WASI_FILESTAT_SET_ATIM_NOW != 0;
|
||||
@@ -310,31 +292,32 @@ pub(crate) fn path_filestat_set_times(
|
||||
return Err(Error::EINVAL);
|
||||
}
|
||||
|
||||
let atflags = match dirflags {
|
||||
wasi::__WASI_LOOKUP_SYMLINK_FOLLOW => UtimensatFlags::FollowSymlink,
|
||||
_ => UtimensatFlags::NoFollowSymlink,
|
||||
};
|
||||
|
||||
let symlink_nofollow = wasi::__WASI_LOOKUP_SYMLINK_FOLLOW != dirflags;
|
||||
let atim = if set_atim {
|
||||
let st_atim = st_atim.try_into()?;
|
||||
TimeSpec::nanoseconds(st_atim)
|
||||
let time = UNIX_EPOCH + Duration::from_nanos(st_atim);
|
||||
FileTime::FileTime(filetime::FileTime::from_system_time(time))
|
||||
} else if set_atim_now {
|
||||
timespec_now()
|
||||
FileTime::Now
|
||||
} else {
|
||||
timespec_omit()
|
||||
FileTime::Omit
|
||||
};
|
||||
|
||||
let mtim = if set_mtim {
|
||||
let st_mtim = st_mtim.try_into()?;
|
||||
TimeSpec::nanoseconds(st_mtim)
|
||||
let time = UNIX_EPOCH + Duration::from_nanos(st_mtim);
|
||||
FileTime::FileTime(filetime::FileTime::from_system_time(time))
|
||||
} else if set_mtim_now {
|
||||
timespec_now()
|
||||
FileTime::Now
|
||||
} else {
|
||||
timespec_omit()
|
||||
FileTime::Omit
|
||||
};
|
||||
|
||||
let fd = resolved.dirfd().as_raw_fd().into();
|
||||
utimensat(fd, resolved.path(), &atim, &mtim, atflags).map_err(Into::into)
|
||||
utimensat(
|
||||
resolved.dirfd(),
|
||||
resolved.path(),
|
||||
atim,
|
||||
mtim,
|
||||
symlink_nofollow,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user