diff options
Diffstat (limited to 'vendor/filetime/src/unix/macos.rs')
-rw-r--r-- | vendor/filetime/src/unix/macos.rs | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/vendor/filetime/src/unix/macos.rs b/vendor/filetime/src/unix/macos.rs new file mode 100644 index 0000000..efe92d4 --- /dev/null +++ b/vendor/filetime/src/unix/macos.rs @@ -0,0 +1,108 @@ +//! Beginning with macOS 10.13, `utimensat` is supported by the OS, so here, we check if the symbol exists +//! and if not, we fallback to `utimes`. +use crate::FileTime; +use libc::{c_char, c_int, timespec}; +use std::ffi::{CStr, CString}; +use std::fs::File; +use std::os::unix::prelude::*; +use std::path::Path; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::{io, mem}; + +pub fn set_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> { + set_times(p, Some(atime), Some(mtime), false) +} + +pub fn set_file_mtime(p: &Path, mtime: FileTime) -> io::Result<()> { + set_times(p, None, Some(mtime), false) +} + +pub fn set_file_atime(p: &Path, atime: FileTime) -> io::Result<()> { + set_times(p, Some(atime), None, false) +} + +pub fn set_file_handle_times( + f: &File, + atime: Option<FileTime>, + mtime: Option<FileTime>, +) -> io::Result<()> { + // Attempt to use the `futimens` syscall, but if it's not supported by the + // current kernel then fall back to an older syscall. + if let Some(func) = futimens() { + let times = [super::to_timespec(&atime), super::to_timespec(&mtime)]; + let rc = unsafe { func(f.as_raw_fd(), times.as_ptr()) }; + if rc == 0 { + return Ok(()); + } else { + return Err(io::Error::last_os_error()); + } + } + + super::utimes::set_file_handle_times(f, atime, mtime) +} + +pub fn set_symlink_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> { + set_times(p, Some(atime), Some(mtime), true) +} + +fn set_times( + p: &Path, + atime: Option<FileTime>, + mtime: Option<FileTime>, + symlink: bool, +) -> io::Result<()> { + // 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) = utimensat() { + let flags = if symlink { + libc::AT_SYMLINK_NOFOLLOW + } else { + 0 + }; + + let p = CString::new(p.as_os_str().as_bytes())?; + let times = [super::to_timespec(&atime), super::to_timespec(&mtime)]; + let rc = unsafe { func(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) }; + if rc == 0 { + return Ok(()); + } else { + return Err(io::Error::last_os_error()); + } + } + + super::utimes::set_times(p, atime, mtime, symlink) +} + +fn utimensat() -> Option<unsafe extern "C" fn(c_int, *const c_char, *const timespec, c_int) -> c_int> +{ + static ADDR: AtomicUsize = AtomicUsize::new(0); + unsafe { + fetch(&ADDR, CStr::from_bytes_with_nul_unchecked(b"utimensat\0")) + .map(|sym| mem::transmute(sym)) + } +} + +fn futimens() -> Option<unsafe extern "C" fn(c_int, *const timespec) -> c_int> { + static ADDR: AtomicUsize = AtomicUsize::new(0); + unsafe { + fetch(&ADDR, CStr::from_bytes_with_nul_unchecked(b"futimens\0")) + .map(|sym| 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; +} |