forked from bytecodealliance/wasmtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 bytecodealliance#516. [filetime]: https://github.com/alexcrichton/filetime
- Loading branch information
Jakub Konka
committed
Nov 10, 2019
1 parent
0f4f9d7
commit 8604caa
Showing
8 changed files
with
282 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use super::super::filetime::FileTime; | ||
use cfg_if::cfg_if; | ||
use std::ffi::CStr; | ||
use std::fs::File; | ||
use std::io; | ||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; | ||
|
||
cfg_if! { | ||
if #[cfg(any( | ||
target_os = "macos", | ||
target_os = "freebsd", | ||
target_os = "ios", | ||
target_os = "dragonfly" | ||
))] { | ||
pub(crate) const UTIME_NOW: i64 = -1; | ||
} else if #[cfg(target_os = "openbsd")] { | ||
// https://github.com/openbsd/src/blob/master/sys/sys/stat.h#L187 | ||
pub(crate) const UTIME_NOW: i64 = -2; | ||
} else if #[cfg(target_os = "netbsd" )] { | ||
// http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/stat.h?rev=1.69&content-type=text/x-cvsweb-markup&only_with_tag=MAIN | ||
pub(crate) const UTIME_NOW: i64 = 1_073_741_823; | ||
} | ||
} | ||
|
||
cfg_if! { | ||
if #[cfg(any( | ||
target_os = "macos", | ||
target_os = "freebsd", | ||
target_os = "ios", | ||
target_os = "dragonfly" | ||
))] { | ||
pub(crate) const UTIME_OMIT: i64 = -2; | ||
} else if #[cfg(target_os = "openbsd")] { | ||
// https://github.com/openbsd/src/blob/master/sys/sys/stat.h#L187 | ||
pub(crate) const UTIME_OMIT: i64 = -1; | ||
} else if #[cfg(target_os = "netbsd")] { | ||
// http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/stat.h?rev=1.69&content-type=text/x-cvsweb-markup&only_with_tag=MAIN | ||
pub(crate) const UTIME_OMIT: i64 = 1_073_741_822; | ||
} | ||
} | ||
|
||
pub(crate) fn utimensat( | ||
dirfd: &File, | ||
path: &str, | ||
atime: FileTime, | ||
mtime: FileTime, | ||
symlink: bool, | ||
) -> io::Result<()> { | ||
use super::super::filetime::{to_timespec, utimesat}; | ||
use std::ffi::CString; | ||
use std::os::unix::prelude::*; | ||
|
||
// Attempt to use the `utimensat` syscall, but if it's not supported by the | ||
// current kernel then fall back to an older syscall. | ||
if let Some(func) = fetch_utimensat() { | ||
let flags = if symlink { | ||
libc::AT_SYMLINK_NOFOLLOW | ||
} else { | ||
0 | ||
}; | ||
|
||
let p = CString::new(path.as_bytes())?; | ||
let times = [to_timespec(&atime), to_timespec(&mtime)]; | ||
let rc = unsafe { func(dirfd.as_raw_fd(), p.as_ptr(), times.as_ptr(), flags) }; | ||
if rc == 0 { | ||
return Ok(()); | ||
} else { | ||
return Err(io::Error::last_os_error()); | ||
} | ||
} | ||
|
||
utimesat(dirfd, path, atime, mtime, symlink) | ||
} | ||
|
||
fn fetch_utimensat() -> Option< | ||
unsafe extern "C" fn( | ||
libc::c_int, | ||
*const libc::c_char, | ||
*const libc::timespec, | ||
libc::c_int, | ||
) -> libc::c_int, | ||
> { | ||
static ADDR: AtomicUsize = AtomicUsize::new(0); | ||
unsafe { | ||
fetch(&ADDR, CStr::from_bytes_with_nul_unchecked(b"utimensat\0")) | ||
.map(|sym| std::mem::transmute(sym)) | ||
} | ||
} | ||
|
||
fn fetch(cache: &AtomicUsize, name: &CStr) -> Option<usize> { | ||
match cache.load(SeqCst) { | ||
0 => {} | ||
1 => return None, | ||
n => return Some(n), | ||
} | ||
let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) }; | ||
let (val, ret) = if sym.is_null() { | ||
(1, None) | ||
} else { | ||
(sym as usize, Some(sym as usize)) | ||
}; | ||
cache.store(val, SeqCst); | ||
return ret; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use std::fs::{self, File}; | ||
use std::io; | ||
|
||
cfg_if::cfg_if! { | ||
if #[cfg(target_os = "linux")] { | ||
pub(crate) use super::linux::filetime::*; | ||
} else if #[cfg(any( | ||
target_os = "macos", | ||
target_os = "netbsd", | ||
target_os = "freebsd", | ||
target_os = "openbsd", | ||
target_os = "ios", | ||
target_os = "dragonfly" | ||
))] { | ||
pub(crate) use super::bsd::filetime::*; | ||
} | ||
} | ||
|
||
#[derive(Debug, Copy, Clone)] | ||
pub(crate) enum FileTime { | ||
Now, | ||
Omit, | ||
FileTime(filetime::FileTime), | ||
} | ||
|
||
fn get_times( | ||
atime: FileTime, | ||
mtime: FileTime, | ||
current: impl Fn() -> io::Result<fs::Metadata>, | ||
) -> io::Result<(filetime::FileTime, filetime::FileTime)> { | ||
use std::time::SystemTime; | ||
|
||
let atime = match atime { | ||
FileTime::Now => { | ||
let time = SystemTime::now(); | ||
filetime::FileTime::from_system_time(time) | ||
} | ||
FileTime::Omit => { | ||
let meta = current()?; | ||
filetime::FileTime::from_last_access_time(&meta) | ||
} | ||
FileTime::FileTime(ft) => ft, | ||
}; | ||
|
||
let mtime = match mtime { | ||
FileTime::Now => { | ||
let time = SystemTime::now(); | ||
filetime::FileTime::from_system_time(time) | ||
} | ||
FileTime::Omit => { | ||
let meta = current()?; | ||
filetime::FileTime::from_last_modification_time(&meta) | ||
} | ||
FileTime::FileTime(ft) => ft, | ||
}; | ||
|
||
Ok((atime, mtime)) | ||
} | ||
|
||
pub(crate) fn utimesat( | ||
dirfd: &File, | ||
path: &str, | ||
atime: FileTime, | ||
mtime: FileTime, | ||
symlink: bool, | ||
) -> io::Result<()> { | ||
use std::ffi::CString; | ||
use std::os::unix::prelude::*; | ||
// emulate *at syscall by reading the path from a combination of | ||
// (fd, path) | ||
let p = CString::new(path.as_bytes())?; | ||
let mut flags = libc::O_RDWR; | ||
if !symlink { | ||
flags |= libc::O_NOFOLLOW; | ||
} | ||
let fd = unsafe { libc::openat(dirfd.as_raw_fd(), p.as_ptr(), flags) }; | ||
let f = unsafe { File::from_raw_fd(fd) }; | ||
let (atime, mtime) = get_times(atime, mtime, || f.metadata())?; | ||
let times = [to_timeval(atime), to_timeval(mtime)]; | ||
let rc = unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) }; | ||
return if rc == 0 { | ||
Ok(()) | ||
} else { | ||
Err(io::Error::last_os_error()) | ||
}; | ||
} | ||
|
||
fn to_timeval(ft: filetime::FileTime) -> libc::timeval { | ||
libc::timeval { | ||
tv_sec: ft.seconds(), | ||
tv_usec: (ft.nanoseconds() / 1000) as libc::suseconds_t, | ||
} | ||
} | ||
|
||
pub(crate) fn to_timespec(ft: &FileTime) -> libc::timespec { | ||
match ft { | ||
FileTime::Now => libc::timespec { | ||
tv_sec: 0, | ||
tv_nsec: UTIME_NOW, | ||
}, | ||
FileTime::Omit => libc::timespec { | ||
tv_sec: 0, | ||
tv_nsec: UTIME_OMIT, | ||
}, | ||
FileTime::FileTime(ft) => libc::timespec { | ||
tv_sec: ft.seconds(), | ||
tv_nsec: ft.nanoseconds() as _, | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.