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
61
crates/wasi-common/src/sys/unix/linux/filetime.rs
Normal file
61
crates/wasi-common/src/sys/unix/linux/filetime.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
//! This internal module consists of helper types and functions for dealing
|
||||
//! with setting the file times specific to Linux.
|
||||
use super::super::filetime::FileTime;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
|
||||
|
||||
pub(crate) const UTIME_NOW: i64 = 1_073_741_823;
|
||||
pub(crate) const UTIME_OMIT: i64 = 1_073_741_822;
|
||||
|
||||
/// Wrapper for `utimensat` syscall, however, with an added twist such that `utimensat` symbol
|
||||
/// is firstly resolved (i.e., we check whether it exists on the host), and only used if that is
|
||||
/// the case. Otherwise, the syscall resorts to a less accurate `utimesat` emulated syscall.
|
||||
/// The original implementation can be found here: [filetime::unix::linux::set_times]
|
||||
///
|
||||
/// [filetime::unix::linux::set_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/linux.rs#L64
|
||||
pub(crate) fn utimensat(
|
||||
dirfd: &File,
|
||||
path: &str,
|
||||
atime: FileTime,
|
||||
mtime: FileTime,
|
||||
symlink_nofollow: bool,
|
||||
) -> io::Result<()> {
|
||||
use super::super::filetime::{to_timespec, utimesat};
|
||||
use std::ffi::CString;
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
let flags = if symlink_nofollow {
|
||||
libc::AT_SYMLINK_NOFOLLOW
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Attempt to use the `utimensat` syscall, but if it's not supported by the
|
||||
// current kernel then fall back to an older syscall.
|
||||
static INVALID: AtomicBool = AtomicBool::new(false);
|
||||
if !INVALID.load(Relaxed) {
|
||||
let p = CString::new(path.as_bytes())?;
|
||||
let times = [to_timespec(&atime), to_timespec(&mtime)];
|
||||
let rc = unsafe {
|
||||
libc::syscall(
|
||||
libc::SYS_utimensat,
|
||||
dirfd.as_raw_fd(),
|
||||
p.as_ptr(),
|
||||
times.as_ptr(),
|
||||
flags,
|
||||
)
|
||||
};
|
||||
if rc == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let err = io::Error::last_os_error();
|
||||
if err.raw_os_error() == Some(libc::ENOSYS) {
|
||||
INVALID.store(true, Relaxed);
|
||||
} else {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
utimesat(dirfd, path, atime, mtime, symlink_nofollow)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub(crate) mod filetime;
|
||||
pub(crate) mod hostcalls_impl;
|
||||
pub(crate) mod osfile;
|
||||
|
||||
@@ -30,13 +31,3 @@ pub(crate) mod fdentry_impl {
|
||||
pub(crate) mod host_impl {
|
||||
pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC;
|
||||
}
|
||||
|
||||
pub(crate) mod fs_helpers {
|
||||
pub(crate) fn utime_now() -> libc::c_long {
|
||||
libc::UTIME_NOW
|
||||
}
|
||||
|
||||
pub(crate) fn utime_omit() -> libc::c_long {
|
||||
libc::UTIME_OMIT
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user