summaryrefslogtreecommitdiffstats
path: root/vendor/filetime/src/unix/macos.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/filetime/src/unix/macos.rs')
-rw-r--r--vendor/filetime/src/unix/macos.rs108
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;
+}