]> git.proxmox.com Git - cargo.git/blob - vendor/filetime/src/unix/macos.rs
New upstream version 0.63.1
[cargo.git] / vendor / filetime / src / unix / macos.rs
1 //! Beginning with macOS 10.13, `utimensat` is supported by the OS, so here, we check if the symbol exists
2 //! and if not, we fallback to `utimes`.
3 use crate::FileTime;
4 use libc::{c_char, c_int, timespec};
5 use std::ffi::{CStr, CString};
6 use std::fs::File;
7 use std::os::unix::prelude::*;
8 use std::path::Path;
9 use std::sync::atomic::AtomicUsize;
10 use std::sync::atomic::Ordering::SeqCst;
11 use std::{io, mem};
12
13 pub fn set_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
14 set_times(p, Some(atime), Some(mtime), false)
15 }
16
17 pub fn set_file_mtime(p: &Path, mtime: FileTime) -> io::Result<()> {
18 set_times(p, None, Some(mtime), false)
19 }
20
21 pub fn set_file_atime(p: &Path, atime: FileTime) -> io::Result<()> {
22 set_times(p, Some(atime), None, false)
23 }
24
25 pub fn set_file_handle_times(
26 f: &File,
27 atime: Option<FileTime>,
28 mtime: Option<FileTime>,
29 ) -> io::Result<()> {
30 // Attempt to use the `futimens` syscall, but if it's not supported by the
31 // current kernel then fall back to an older syscall.
32 if let Some(func) = futimens() {
33 let times = [super::to_timespec(&atime), super::to_timespec(&mtime)];
34 let rc = unsafe { func(f.as_raw_fd(), times.as_ptr()) };
35 if rc == 0 {
36 return Ok(());
37 } else {
38 return Err(io::Error::last_os_error());
39 }
40 }
41
42 super::utimes::set_file_handle_times(f, atime, mtime)
43 }
44
45 pub fn set_symlink_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
46 set_times(p, Some(atime), Some(mtime), true)
47 }
48
49 fn set_times(
50 p: &Path,
51 atime: Option<FileTime>,
52 mtime: Option<FileTime>,
53 symlink: bool,
54 ) -> io::Result<()> {
55 // Attempt to use the `utimensat` syscall, but if it's not supported by the
56 // current kernel then fall back to an older syscall.
57 if let Some(func) = utimensat() {
58 let flags = if symlink {
59 libc::AT_SYMLINK_NOFOLLOW
60 } else {
61 0
62 };
63
64 let p = CString::new(p.as_os_str().as_bytes())?;
65 let times = [super::to_timespec(&atime), super::to_timespec(&mtime)];
66 let rc = unsafe { func(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) };
67 if rc == 0 {
68 return Ok(());
69 } else {
70 return Err(io::Error::last_os_error());
71 }
72 }
73
74 super::utimes::set_times(p, atime, mtime, symlink)
75 }
76
77 fn utimensat() -> Option<unsafe extern "C" fn(c_int, *const c_char, *const timespec, c_int) -> c_int>
78 {
79 static ADDR: AtomicUsize = AtomicUsize::new(0);
80 unsafe {
81 fetch(&ADDR, CStr::from_bytes_with_nul_unchecked(b"utimensat\0"))
82 .map(|sym| mem::transmute(sym))
83 }
84 }
85
86 fn futimens() -> Option<unsafe extern "C" fn(c_int, *const timespec) -> c_int> {
87 static ADDR: AtomicUsize = AtomicUsize::new(0);
88 unsafe {
89 fetch(&ADDR, CStr::from_bytes_with_nul_unchecked(b"futimens\0"))
90 .map(|sym| mem::transmute(sym))
91 }
92 }
93
94 fn fetch(cache: &AtomicUsize, name: &CStr) -> Option<usize> {
95 match cache.load(SeqCst) {
96 0 => {}
97 1 => return None,
98 n => return Some(n),
99 }
100 let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) };
101 let (val, ret) = if sym.is_null() {
102 (1, None)
103 } else {
104 (sym as usize, Some(sym as usize))
105 };
106 cache.store(val, SeqCst);
107 return ret;
108 }