Skip to content

Commit fc63980

Browse files
committed
uefi: fs: Add file times plumbing
- Add FileTimes implementation. Signed-off-by: Ayush Singh <[email protected]>
1 parent 5ab6924 commit fc63980

File tree

3 files changed

+146
-24
lines changed

3 files changed

+146
-24
lines changed

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

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub struct File(!);
1818
pub struct FileAttr {
1919
attr: u64,
2020
size: u64,
21+
times: FileTimes,
2122
}
2223

2324
pub struct ReadDir(!);
@@ -32,8 +33,12 @@ pub struct OpenOptions {
3233
create_new: bool,
3334
}
3435

35-
#[derive(Copy, Clone, Debug, Default)]
36-
pub struct FileTimes {}
36+
#[derive(Copy, Clone, Debug)]
37+
pub struct FileTimes {
38+
accessed: SystemTime,
39+
modified: SystemTime,
40+
created: SystemTime,
41+
}
3742

3843
#[derive(Clone, PartialEq, Eq, Debug)]
3944
// Bool indicates if file is readonly
@@ -60,15 +65,15 @@ impl FileAttr {
6065
}
6166

6267
pub fn modified(&self) -> io::Result<SystemTime> {
63-
unsupported()
68+
Ok(self.times.modified)
6469
}
6570

6671
pub fn accessed(&self) -> io::Result<SystemTime> {
67-
unsupported()
72+
Ok(self.times.accessed)
6873
}
6974

7075
pub fn created(&self) -> io::Result<SystemTime> {
71-
unsupported()
76+
Ok(self.times.created)
7277
}
7378
}
7479

@@ -92,8 +97,19 @@ impl FilePermissions {
9297
}
9398

9499
impl FileTimes {
95-
pub fn set_accessed(&mut self, _t: SystemTime) {}
96-
pub fn set_modified(&mut self, _t: SystemTime) {}
100+
pub fn set_accessed(&mut self, t: SystemTime) {
101+
self.accessed = t;
102+
}
103+
104+
pub fn set_modified(&mut self, t: SystemTime) {
105+
self.modified = t;
106+
}
107+
}
108+
109+
impl Default for FileTimes {
110+
fn default() -> Self {
111+
Self { accessed: SystemTime::ZERO, modified: SystemTime::ZERO, created: SystemTime::ZERO }
112+
}
97113
}
98114

99115
impl FileType {
@@ -386,6 +402,7 @@ mod uefi_fs {
386402
use crate::path::Path;
387403
use crate::ptr::NonNull;
388404
use crate::sys::helpers;
405+
use crate::sys::time::{self, SystemTime};
389406

390407
pub(crate) struct File(NonNull<file::Protocol>);
391408

@@ -533,4 +550,27 @@ mod uefi_fs {
533550

534551
Ok(())
535552
}
553+
554+
/// Get the current timezone.
555+
fn current_timezone() -> i16 {
556+
time::system_time_internal::now().unwrap().timezone
557+
}
558+
559+
/// EDK2 FAT driver uses EFI_UNSPECIFIED_TIMEZONE to represent localtime. So for proper
560+
/// conversion to SystemTime, we use the current time to get the timezone in such cases.
561+
#[allow(dead_code)]
562+
fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> SystemTime {
563+
time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
564+
current_timezone()
565+
} else {
566+
time.timezone
567+
};
568+
SystemTime::from_uefi(time)
569+
}
570+
571+
/// Convert to UEFI Time with the current timezone.
572+
#[allow(dead_code)]
573+
fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time {
574+
time.to_uefi_loose(current_timezone(), 0)
575+
}
536576
}

library/std/src/sys/pal/uefi/tests.rs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ use crate::time::Duration;
88

99
const SECS_IN_MINUTE: u64 = 60;
1010

11+
const MAX_UEFI_TIME: Duration = from_uefi(r_efi::efi::Time {
12+
year: 9999,
13+
month: 12,
14+
day: 31,
15+
hour: 23,
16+
minute: 59,
17+
second: 59,
18+
nanosecond: 999_999_999,
19+
timezone: 1440,
20+
daylight: 0,
21+
pad1: 0,
22+
pad2: 0,
23+
});
24+
1125
#[test]
1226
fn align() {
1327
// UEFI ABI specifies that allocation alignment minimum is always 8. So this can be
@@ -44,7 +58,7 @@ fn systemtime_start() {
4458
};
4559
assert_eq!(from_uefi(&t), Duration::new(0, 0));
4660
assert_eq!(t, to_uefi(&from_uefi(&t), -1440, 0).unwrap());
47-
assert!(to_uefi(&from_uefi(&t), 0, 0).is_none());
61+
assert!(to_uefi(&from_uefi(&t), 0, 0).is_err());
4862
}
4963

5064
#[test]
@@ -64,7 +78,7 @@ fn systemtime_utc_start() {
6478
};
6579
assert_eq!(from_uefi(&t), Duration::new(1440 * SECS_IN_MINUTE, 0));
6680
assert_eq!(t, to_uefi(&from_uefi(&t), 0, 0).unwrap());
67-
assert!(to_uefi(&from_uefi(&t), -1440, 0).is_some());
81+
assert!(to_uefi(&from_uefi(&t), -1440, 0).is_err());
6882
}
6983

7084
#[test]
@@ -82,8 +96,44 @@ fn systemtime_end() {
8296
daylight: 0,
8397
pad2: 0,
8498
};
85-
assert!(to_uefi(&from_uefi(&t), 1440, 0).is_some());
86-
assert!(to_uefi(&from_uefi(&t), 1439, 0).is_none());
99+
assert!(to_uefi(&from_uefi(&t), 1440, 0).is_ok());
100+
assert!(to_uefi(&from_uefi(&t), 1439, 0).is_err());
101+
}
102+
103+
#[test]
104+
fn min_time() {
105+
let inp = Duration::from_secs(1440 * SECS_IN_MINUTE);
106+
let new_tz = to_uefi(&inp, 1440, 0).err().unwrap();
107+
assert_eq!(new_tz, 0);
108+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
109+
110+
let inp = Duration::from_secs(1450 * SECS_IN_MINUTE);
111+
let new_tz = to_uefi(&inp, 1440, 0).err().unwrap();
112+
assert_eq!(new_tz, 10);
113+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
114+
115+
let inp = Duration::from_secs(1450 * SECS_IN_MINUTE + 10);
116+
let new_tz = to_uefi(&inp, 1440, 0).err().unwrap();
117+
assert_eq!(new_tz, 9);
118+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
119+
}
120+
121+
#[test]
122+
fn max_time() {
123+
let inp = MAX_UEFI_TIME;
124+
let new_tz = to_uefi(&inp, -1440, 0).err().unwrap();
125+
assert_eq!(new_tz, 1440);
126+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
127+
128+
let inp = MAX_UEFI_TIME - Duration::from_secs(1440 * SECS_IN_MINUTE);
129+
let new_tz = to_uefi(&inp, -1440, 0).err().unwrap();
130+
assert_eq!(new_tz, 0);
131+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
132+
133+
let inp = MAX_UEFI_TIME - Duration::from_secs(1440 * SECS_IN_MINUTE + 10);
134+
let new_tz = to_uefi(&inp, -1440, 0).err().unwrap();
135+
assert_eq!(new_tz, 0);
136+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
87137
}
88138

89139
// UEFI IoSlice and IoSliceMut Tests

library/std/src/sys/pal/uefi/time.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::time::Duration;
22

3+
const SECS_IN_MINUTE: u64 = 60;
4+
35
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
46
pub struct Instant(Duration);
57

@@ -66,17 +68,32 @@ impl Instant {
6668
}
6769

6870
impl SystemTime {
71+
pub(crate) const ZERO: Self = Self(Duration::ZERO);
72+
6973
pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self {
7074
Self(system_time_internal::from_uefi(&t))
7175
}
7276

73-
#[expect(dead_code)]
74-
pub(crate) const fn to_uefi(self, timezone: i16, daylight: u8) -> Option<r_efi::efi::Time> {
77+
pub(crate) const fn to_uefi(
78+
self,
79+
timezone: i16,
80+
daylight: u8,
81+
) -> Result<r_efi::efi::Time, i16> {
7582
system_time_internal::to_uefi(&self.0, timezone, daylight)
7683
}
7784

85+
/// Create UEFI Time with the closest timezone (minute offset) that still allows the time to be
86+
/// represented.
87+
pub(crate) fn to_uefi_loose(self, timezone: i16, daylight: u8) -> r_efi::efi::Time {
88+
match self.to_uefi(timezone, daylight) {
89+
Ok(x) => x,
90+
Err(tz) => self.to_uefi(tz, daylight).unwrap(),
91+
}
92+
}
93+
7894
pub fn now() -> SystemTime {
7995
system_time_internal::now()
96+
.map(Self::from_uefi)
8097
.unwrap_or_else(|| panic!("time not implemented on this platform"))
8198
}
8299

@@ -117,12 +134,11 @@ pub(crate) mod system_time_internal {
117134
use crate::mem::MaybeUninit;
118135
use crate::ptr::NonNull;
119136

120-
const SECS_IN_MINUTE: u64 = 60;
121137
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
122138
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
123139
const TIMEZONE_DELTA: u64 = 1440 * SECS_IN_MINUTE;
124140

125-
pub fn now() -> Option<SystemTime> {
141+
pub(crate) fn now() -> Option<Time> {
126142
let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?;
127143
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
128144
let r = unsafe {
@@ -132,9 +148,7 @@ pub(crate) mod system_time_internal {
132148
return None;
133149
}
134150

135-
let t = unsafe { t.assume_init() };
136-
137-
Some(SystemTime::from_uefi(t))
151+
Some(unsafe { t.assume_init() })
138152
}
139153

140154
/// This algorithm is a modified form of the one described in the post
@@ -193,7 +207,10 @@ pub(crate) mod system_time_internal {
193207
///
194208
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
195209
/// epoch used in the original algorithm.
196-
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Option<Time> {
210+
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Result<Time, i16> {
211+
const MIN_IN_HOUR: u64 = 60;
212+
const MIN_IN_DAY: u64 = MIN_IN_HOUR * 24;
213+
197214
// Check timzone validity
198215
assert!(timezone <= 1440 && timezone >= -1440);
199216

@@ -202,7 +219,11 @@ pub(crate) mod system_time_internal {
202219
dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap();
203220

204221
// Convert to seconds since 1900-01-01-00:00:00 in timezone.
205-
let Some(secs) = secs.checked_sub(TIMEZONE_DELTA) else { return None };
222+
let Some(secs) = secs.checked_sub(TIMEZONE_DELTA) else {
223+
let new_tz =
224+
(secs / SECS_IN_MINUTE - if secs % SECS_IN_MINUTE == 0 { 0 } else { 1 }) as i16;
225+
return Err(new_tz);
226+
};
206227

207228
let days = secs / SECS_IN_DAY;
208229
let remaining_secs = secs % SECS_IN_DAY;
@@ -225,9 +246,10 @@ pub(crate) mod system_time_internal {
225246
let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
226247
let second = (remaining_secs % SECS_IN_MINUTE) as u8;
227248

228-
// Check Bounds
229-
if y >= 1900 && y <= 9999 {
230-
Some(Time {
249+
// At this point, invalid time will be greater than MAX representable time. It cannot be less
250+
// than minimum time since we already take care of that case above.
251+
if y <= 9999 {
252+
Ok(Time {
231253
year: y as u16,
232254
month: m as u8,
233255
day: d as u8,
@@ -241,7 +263,17 @@ pub(crate) mod system_time_internal {
241263
pad2: 0,
242264
})
243265
} else {
244-
None
266+
assert!(y == 10000);
267+
assert!(m == 1);
268+
269+
let delta = ((d - 1) as u64 * MIN_IN_DAY
270+
+ hour as u64 * MIN_IN_HOUR
271+
+ minute as u64
272+
+ if second == 0 { 0 } else { 1 }) as i16;
273+
let new_tz = timezone + delta;
274+
275+
assert!(new_tz <= 1440 && new_tz >= -1440);
276+
Err(new_tz)
245277
}
246278
}
247279
}

0 commit comments

Comments
 (0)