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`.
4 use libc
::{c_char, c_int, timespec}
;
5 use std
::ffi
::{CStr, CString}
;
7 use std
::os
::unix
::prelude
::*;
9 use std
::sync
::atomic
::AtomicUsize
;
10 use std
::sync
::atomic
::Ordering
::SeqCst
;
13 pub fn set_file_times(p
: &Path
, atime
: FileTime
, mtime
: FileTime
) -> io
::Result
<()> {
14 set_times(p
, Some(atime
), Some(mtime
), false)
17 pub fn set_file_mtime(p
: &Path
, mtime
: FileTime
) -> io
::Result
<()> {
18 set_times(p
, None
, Some(mtime
), false)
21 pub fn set_file_atime(p
: &Path
, atime
: FileTime
) -> io
::Result
<()> {
22 set_times(p
, Some(atime
), None
, false)
25 pub fn set_file_handle_times(
27 atime
: Option
<FileTime
>,
28 mtime
: Option
<FileTime
>,
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()) }
;
38 return Err(io
::Error
::last_os_error());
42 super::utimes
::set_file_handle_times(f
, atime
, mtime
)
45 pub fn set_symlink_file_times(p
: &Path
, atime
: FileTime
, mtime
: FileTime
) -> io
::Result
<()> {
46 set_times(p
, Some(atime
), Some(mtime
), true)
51 atime
: Option
<FileTime
>,
52 mtime
: Option
<FileTime
>,
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
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) }
;
70 return Err(io
::Error
::last_os_error());
74 super::utimes
::set_times(p
, atime
, mtime
, symlink
)
77 fn utimensat() -> Option
<unsafe extern "C" fn(c_int
, *const c_char
, *const timespec
, c_int
) -> c_int
>
79 static ADDR
: AtomicUsize
= AtomicUsize
::new(0);
81 fetch(&ADDR
, CStr
::from_bytes_with_nul_unchecked(b
"utimensat\0"))
82 .map(|sym
| mem
::transmute(sym
))
86 fn futimens() -> Option
<unsafe extern "C" fn(c_int
, *const timespec
) -> c_int
> {
87 static ADDR
: AtomicUsize
= AtomicUsize
::new(0);
89 fetch(&ADDR
, CStr
::from_bytes_with_nul_unchecked(b
"futimens\0"))
90 .map(|sym
| mem
::transmute(sym
))
94 fn fetch(cache
: &AtomicUsize
, name
: &CStr
) -> Option
<usize> {
95 match cache
.load(SeqCst
) {
100 let sym
= unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) }
;
101 let (val
, ret
) = if sym
.is_null() {
104 (sym
as usize, Some(sym
as usize))
106 cache
.store(val
, SeqCst
);