Skip to content

Commit

Permalink
Fix panics when query file metadata, if timestamps predate the epoch
Browse files Browse the repository at this point in the history
It's possible for a file to have a timestamp that predates the Epoch.
But Rust's std::time::Duration object cannot be negative.  So
cap-primitives must carefully handle any negative timestamps it gets.

Fixes #328
  • Loading branch information
asomers committed Sep 18, 2023
1 parent 5e9248c commit f7e7d6e
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 5 deletions.
21 changes: 20 additions & 1 deletion cap-primitives/src/rustix/fs/metadata_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,14 @@ impl MetadataExt {

#[allow(clippy::similar_names)]
fn system_time_from_rustix(sec: i64, nsec: u64) -> Option<SystemTime> {
SystemClock::UNIX_EPOCH.checked_add(Duration::new(u64::try_from(sec).unwrap(), nsec as _))
if sec >= 0 {
SystemClock::UNIX_EPOCH.checked_add(Duration::new(u64::try_from(sec).unwrap(), nsec as _))
} else {
SystemClock::UNIX_EPOCH
.checked_sub(Duration::new(u64::try_from(-sec).unwrap(), 0))
.map(|t| t.checked_add(Duration::new(0, nsec as u32)))
.flatten()
}
}

impl rustix::fs::MetadataExt for MetadataExt {
Expand Down Expand Up @@ -396,3 +403,15 @@ impl rustix::fs::MetadataExt for MetadataExt {
self.ctim
}
}

/// It should be possible to represent times before the Epoch.
/// https://github.com/bytecodealliance/cap-std/issues/328
#[test]
fn negative_time() {
let system_time = system_time_from_rustix(-1, 1).unwrap();
let d = SystemClock::UNIX_EPOCH.duration_since(system_time).unwrap();
assert_eq!(d.as_secs(), 0);
if !cfg!(emulate_second_only_system) {
assert_eq!(d.subsec_nanos(), 999999999);
}
}
18 changes: 14 additions & 4 deletions cap-primitives/src/time/system_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,20 @@ impl SystemTime {
#[inline]
pub fn from_std(std: time::SystemTime) -> Self {
if cfg!(emulate_second_only_system) {
let duration = std.duration_since(time::SystemTime::UNIX_EPOCH).unwrap();
let secs = time::Duration::from_secs(duration.as_secs());
Self {
std: time::SystemTime::UNIX_EPOCH.checked_add(secs).unwrap(),
match std.duration_since(time::SystemTime::UNIX_EPOCH) {
Ok(duration) => {
let secs = time::Duration::from_secs(duration.as_secs());
Self {
std: time::SystemTime::UNIX_EPOCH.checked_add(secs).unwrap(),
}
}
Err(_) => {
let duration = time::SystemTime::UNIX_EPOCH.duration_since(std).unwrap();
let secs = time::Duration::from_secs(duration.as_secs());
Self {
std: time::SystemTime::UNIX_EPOCH.checked_sub(secs).unwrap(),
}
}
}
} else {
Self { std }
Expand Down

0 comments on commit f7e7d6e

Please sign in to comment.