use crate::filetime::FileTime;
use crate::{cstr, from_success_code};
use std::ffi::CStr;
use std::fs::File;
use std::io::Result;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
pub fn utimensat(
dirfd: &File,
path: &str,
atime: FileTime,
mtime: FileTime,
symlink_nofollow: bool,
) -> Result<()> {
use crate::filetime::to_timespec;
use std::os::unix::prelude::*;
if let Some(func) = fetch_utimensat() {
let flags = if symlink_nofollow {
libc::AT_SYMLINK_NOFOLLOW
} else {
0
};
let path = cstr(path)?;
let times = [to_timespec(&atime)?, to_timespec(&mtime)?];
return from_success_code(unsafe {
func(dirfd.as_raw_fd(), path.as_ptr(), times.as_ptr(), flags)
});
}
super::utimesat::utimesat(dirfd, path, atime, mtime, symlink_nofollow)
}
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;
}