Skip to content

Commit 39c0b00

Browse files
committed
Error instead of panicking when setting file times if the passed SystemTime doesn't fit into the required type
1 parent 8ce3204 commit 39c0b00

File tree

4 files changed

+37
-49
lines changed

4 files changed

+37
-49
lines changed

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@
251251
#![feature(doc_notable_trait)]
252252
#![feature(dropck_eyepatch)]
253253
#![feature(exhaustive_patterns)]
254+
#![feature(if_let_guard)]
254255
#![feature(intra_doc_pointers)]
255256
#![feature(lang_items)]
256257
#![feature(let_chains)]

library/std/src/sys/unix/fs.rs

+21-41
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,11 @@ pub struct FilePermissions {
313313
mode: mode_t,
314314
}
315315

316-
#[derive(Copy, Clone)]
317-
pub struct FileTimes([libc::timespec; 2]);
316+
#[derive(Copy, Clone, Debug, Default)]
317+
pub struct FileTimes {
318+
accessed: Option<SystemTime>,
319+
modified: Option<SystemTime>,
320+
}
318321

319322
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
320323
pub struct FileType {
@@ -512,45 +515,11 @@ impl FilePermissions {
512515

513516
impl FileTimes {
514517
pub fn set_accessed(&mut self, t: SystemTime) {
515-
self.0[0] = t.t.to_timespec().expect("Invalid system time");
518+
self.accessed = Some(t);
516519
}
517520

518521
pub fn set_modified(&mut self, t: SystemTime) {
519-
self.0[1] = t.t.to_timespec().expect("Invalid system time");
520-
}
521-
}
522-
523-
struct TimespecDebugAdapter<'a>(&'a libc::timespec);
524-
525-
impl fmt::Debug for TimespecDebugAdapter<'_> {
526-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527-
f.debug_struct("timespec")
528-
.field("tv_sec", &self.0.tv_sec)
529-
.field("tv_nsec", &self.0.tv_nsec)
530-
.finish()
531-
}
532-
}
533-
534-
impl fmt::Debug for FileTimes {
535-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536-
f.debug_struct("FileTimes")
537-
.field("accessed", &TimespecDebugAdapter(&self.0[0]))
538-
.field("modified", &TimespecDebugAdapter(&self.0[1]))
539-
.finish()
540-
}
541-
}
542-
543-
impl Default for FileTimes {
544-
fn default() -> Self {
545-
// Redox doesn't appear to support `UTIME_OMIT`, so we stub it out here, and always return
546-
// an error in `set_times`.
547-
// ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
548-
// the same as for Redox.
549-
#[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))]
550-
let omit = libc::timespec { tv_sec: 0, tv_nsec: 0 };
551-
#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
552-
let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ };
553-
Self([omit; 2])
522+
self.modified = Some(t);
554523
}
555524
}
556525

@@ -1084,6 +1053,17 @@ impl File {
10841053
}
10851054

10861055
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
1056+
#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
1057+
let to_timespec = |time: Option<SystemTime>| {
1058+
match time {
1059+
Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
1060+
Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")),
1061+
Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too small to set as a file time")),
1062+
None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
1063+
}
1064+
};
1065+
#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
1066+
let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
10871067
cfg_if::cfg_if! {
10881068
if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] {
10891069
// Redox doesn't appear to support `UTIME_OMIT`.
@@ -1099,7 +1079,7 @@ impl File {
10991079
cvt(unsafe {
11001080
weak!(fn futimens(c_int, *const libc::timespec) -> c_int);
11011081
match futimens.get() {
1102-
Some(futimens) => futimens(self.as_raw_fd(), times.0.as_ptr()),
1082+
Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()),
11031083
#[cfg(target_os = "macos")]
11041084
None => {
11051085
fn ts_to_tv(ts: &libc::timespec) -> libc::timeval {
@@ -1108,7 +1088,7 @@ impl File {
11081088
tv_usec: (ts.tv_nsec / 1000) as _
11091089
}
11101090
}
1111-
let timevals = [ts_to_tv(&times.0[0]), ts_to_tv(&times.0[1])];
1091+
let timevals = [ts_to_tv(&times[0]), ts_to_tv(&times[1])];
11121092
libc::futimes(self.as_raw_fd(), timevals.as_ptr())
11131093
}
11141094
// futimes requires even newer Android.
@@ -1121,7 +1101,7 @@ impl File {
11211101
})?;
11221102
Ok(())
11231103
} else {
1124-
cvt(unsafe { libc::futimens(self.as_raw_fd(), times.0.as_ptr()) })?;
1104+
cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?;
11251105
Ok(())
11261106
}
11271107
}

library/std/src/sys/wasi/fs.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ pub struct FilePermissions {
6565

6666
#[derive(Copy, Clone, Debug, Default)]
6767
pub struct FileTimes {
68-
accessed: Option<wasi::Timestamp>,
69-
modified: Option<wasi::Timestamp>,
68+
accessed: Option<SystemTime>,
69+
modified: Option<SystemTime>,
7070
}
7171

7272
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
@@ -120,11 +120,11 @@ impl FilePermissions {
120120

121121
impl FileTimes {
122122
pub fn set_accessed(&mut self, t: SystemTime) {
123-
self.accessed = Some(t.to_wasi_timestamp_or_panic());
123+
self.accessed = Some(t);
124124
}
125125

126126
pub fn set_modified(&mut self, t: SystemTime) {
127-
self.modified = Some(t.to_wasi_timestamp_or_panic());
127+
self.modified = Some(t);
128128
}
129129
}
130130

@@ -476,9 +476,16 @@ impl File {
476476
}
477477

478478
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
479+
let to_timestamp = |time: Option<SystemTime>| {
480+
match time {
481+
Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts),
482+
Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")),
483+
None => Ok(0),
484+
}
485+
};
479486
self.fd.filestat_set_times(
480-
times.accessed.unwrap_or(0),
481-
times.modified.unwrap_or(0),
487+
to_timestamp(times.accessed)?,
488+
to_timestamp(times.modified)?,
482489
times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM)
483490
| times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM),
484491
)

library/std/src/sys/wasi/time.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ impl SystemTime {
4747
SystemTime(Duration::from_nanos(ts))
4848
}
4949

50-
pub fn to_wasi_timestamp_or_panic(&self) -> wasi::Timestamp {
51-
self.0.as_nanos().try_into().expect("time does not fit in WASI timestamp")
50+
pub fn to_wasi_timestamp(&self) -> Option<wasi::Timestamp> {
51+
self.0.as_nanos().try_into().ok()
5252
}
5353

5454
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {

0 commit comments

Comments
 (0)