Skip to content

Commit

Permalink
Update use of libc::timespec to prepare for future libc version (#91)
Browse files Browse the repository at this point in the history
In a future release of the `libc` crate, `libc::timespec` will contain
private padding fields on `*-linux-musl` targets and so the struct will
no longer be able to be created using the literal initialization syntax.

Update struct literal use of `libc::timespec` to initialize to zero
first and then manually update the appropriate fields. Also updates a
raw syscall to use the libc function instead as on musl 1.2, it
correctly handles `libc::timespec` values which, in musl 1.2, are
always 16 bytes in length regardless of platform.
  • Loading branch information
wesleywiser committed Dec 7, 2022
1 parent d57bd40 commit bcce691
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 17 deletions.
33 changes: 24 additions & 9 deletions src/unix/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ pub fn set_file_handle_times(
static INVALID: AtomicBool = AtomicBool::new(false);
if !INVALID.load(SeqCst) {
let times = [super::to_timespec(&atime), super::to_timespec(&mtime)];

// We normally use a syscall because the `utimensat` function is documented
// as not accepting a file descriptor in the first argument (even though, on
// Linux, the syscall itself can accept a file descriptor there).
#[cfg(not(target_env = "musl"))]
let rc = unsafe {
libc::syscall(
libc::SYS_utimensat,
Expand All @@ -43,6 +48,24 @@ pub fn set_file_handle_times(
0,
)
};
// However, on musl, we call the musl libc function instead. This is because
// on newer musl versions starting with musl 1.2, `timespec` is always a 64-bit
// value even on 32-bit targets. As a result, musl internally converts their
// `timespec` values to the correct ABI before invoking the syscall. Since we
// use `timespec` from the libc crate, it matches musl's definition and not
// the Linux kernel's version (for some platforms) so we must use musl's
// `utimensat` function to properly convert the value. musl's `utimensat`
// function allows file descriptors in the path argument so this is fine.
#[cfg(target_env = "musl")]
let rc = unsafe {
libc::utimensat(
f.as_raw_fd(),
ptr::null::<libc::c_char>(),
times.as_ptr(),
0,
)
};

if rc == 0 {
return Ok(());
}
Expand Down Expand Up @@ -78,15 +101,7 @@ fn set_times(
if !INVALID.load(SeqCst) {
let p = CString::new(p.as_os_str().as_bytes())?;
let times = [super::to_timespec(&atime), super::to_timespec(&mtime)];
let rc = unsafe {
libc::syscall(
libc::SYS_utimensat,
libc::AT_FDCWD,
p.as_ptr(),
times.as_ptr(),
flags,
)
};
let rc = unsafe { libc::utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) };
if rc == 0 {
return Ok(());
}
Expand Down
15 changes: 7 additions & 8 deletions src/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,16 @@ fn to_timespec(ft: &Option<FileTime>) -> timespec {
}
}

let mut ts: timespec = unsafe { std::mem::zeroed() };
if let &Some(ft) = ft {
timespec {
tv_sec: ft.seconds() as time_t,
tv_nsec: ft.nanoseconds() as _,
}
ts.tv_sec = ft.seconds() as time_t;
ts.tv_nsec = ft.nanoseconds() as _;
} else {
timespec {
tv_sec: 0,
tv_nsec: UTIME_OMIT as _,
}
ts.tv_sec = 0;
ts.tv_nsec = UTIME_OMIT as _;
}

ts
}

pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
Expand Down

0 comments on commit bcce691

Please sign in to comment.