Skip to content

Commit 8428291

Browse files
committed
fsext: Fix metadata_get_time for change time (ctime) before 1970
Useful for archaeologists and time travellers, do not crash if ctime is before 1970. Creating a file with such a change time is a bit challenging (touch can't change that), so we can't easily add end-to-end tests. Steps to create a filesystem with a file `x` with birth/change time in 1960. ``` dd if=/dev/zero bs=100M count=0 seek=1 of=ext4 mkfs.ext4 ext4 sudo mount ext4 mnt touch mnt/x umount mnt echo "set_inode_field x ctime 196001010101" | debugfs -w ext4 echo "set_inode_field x ctime_extra 1234" | debugfs -w ext4 echo "set_inode_field x crtime 196001010101" | debugfs -w ext4 echo "set_inode_field x crtime_extra 0x123400" | debugfs -w ext4 sudo mount ext4 mnt ``` $ cargo run -p uu_stat mnt/x File: mnt/x size: 0 Blocks: 0 IO Block: 1024 regular empty file Device: 700h/1792d Inode: 13 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2025-07-26 19:52:00.849821694 +0800 Modify: 2025-07-26 19:52:00.849821694 +0800 Change: 1960-01-02 09:01:00.000298240 +0800 Birth: 1960-01-02 09:01:00.000298240 +0800
1 parent 6c73ef6 commit 8428291

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

src/uucore/src/lib/features/fsext.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,20 @@ impl From<&str> for MetadataTimeField {
142142

143143
#[cfg(unix)]
144144
fn metadata_get_change_time(md: &Metadata) -> Option<SystemTime> {
145-
// TODO: This is incorrect for negative timestamps.
146-
Some(UNIX_EPOCH + Duration::new(md.ctime() as u64, md.ctime_nsec() as u32))
145+
let mut st = UNIX_EPOCH;
146+
let (secs, nsecs) = (md.ctime(), md.ctime_nsec());
147+
if secs >= 0 {
148+
st += Duration::from_secs(secs as u64);
149+
} else {
150+
st -= Duration::from_secs(-secs as u64);
151+
}
152+
if nsecs >= 0 {
153+
st += Duration::from_nanos(nsecs as u64);
154+
} else {
155+
// Probably never the case, but cover just in case.
156+
st -= Duration::from_nanos(-nsecs as u64);
157+
}
158+
Some(st)
147159
}
148160

149161
#[cfg(not(unix))]

0 commit comments

Comments
 (0)