Skip to content

Commit 7cd2b94

Browse files
authored
Fix panics when query file metadata, if timestamps predate the epoch (#332)
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
1 parent e2e9a08 commit 7cd2b94

File tree

2 files changed

+34
-5
lines changed

2 files changed

+34
-5
lines changed

cap-primitives/src/rustix/fs/metadata_ext.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,14 @@ impl MetadataExt {
285285

286286
#[allow(clippy::similar_names)]
287287
fn system_time_from_rustix(sec: i64, nsec: u64) -> Option<SystemTime> {
288-
SystemClock::UNIX_EPOCH.checked_add(Duration::new(u64::try_from(sec).unwrap(), nsec as _))
288+
if sec >= 0 {
289+
SystemClock::UNIX_EPOCH.checked_add(Duration::new(u64::try_from(sec).unwrap(), nsec as _))
290+
} else {
291+
SystemClock::UNIX_EPOCH
292+
.checked_sub(Duration::new(u64::try_from(-sec).unwrap(), 0))
293+
.map(|t| t.checked_add(Duration::new(0, nsec as u32)))
294+
.flatten()
295+
}
289296
}
290297

291298
impl rustix::fs::MetadataExt for MetadataExt {
@@ -396,3 +403,15 @@ impl rustix::fs::MetadataExt for MetadataExt {
396403
self.ctim
397404
}
398405
}
406+
407+
/// It should be possible to represent times before the Epoch.
408+
/// https://github.com/bytecodealliance/cap-std/issues/328
409+
#[test]
410+
fn negative_time() {
411+
let system_time = system_time_from_rustix(-1, 1).unwrap();
412+
let d = SystemClock::UNIX_EPOCH.duration_since(system_time).unwrap();
413+
assert_eq!(d.as_secs(), 0);
414+
if !cfg!(emulate_second_only_system) {
415+
assert_eq!(d.subsec_nanos(), 999999999);
416+
}
417+
}

cap-primitives/src/time/system_time.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,20 @@ impl SystemTime {
3737
#[inline]
3838
pub fn from_std(std: time::SystemTime) -> Self {
3939
if cfg!(emulate_second_only_system) {
40-
let duration = std.duration_since(time::SystemTime::UNIX_EPOCH).unwrap();
41-
let secs = time::Duration::from_secs(duration.as_secs());
42-
Self {
43-
std: time::SystemTime::UNIX_EPOCH.checked_add(secs).unwrap(),
40+
match std.duration_since(time::SystemTime::UNIX_EPOCH) {
41+
Ok(duration) => {
42+
let secs = time::Duration::from_secs(duration.as_secs());
43+
Self {
44+
std: time::SystemTime::UNIX_EPOCH.checked_add(secs).unwrap(),
45+
}
46+
}
47+
Err(_) => {
48+
let duration = time::SystemTime::UNIX_EPOCH.duration_since(std).unwrap();
49+
let secs = time::Duration::from_secs(duration.as_secs());
50+
Self {
51+
std: time::SystemTime::UNIX_EPOCH.checked_sub(secs).unwrap(),
52+
}
53+
}
4454
}
4555
} else {
4656
Self { std }

0 commit comments

Comments
 (0)