Skip to content

Commit 1f0ad37

Browse files
committed
uefi: fs: Add file times plumbing
- Add FileTimes implementation. Signed-off-by: Ayush Singh <ayush@beagleboard.org>
1 parent ab67c37 commit 1f0ad37

File tree

3 files changed

+174
-39
lines changed

3 files changed

+174
-39
lines changed

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

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ pub struct File(!);
1818
pub struct FileAttr {
1919
attr: u64,
2020
size: u64,
21+
accessed: SystemTime,
22+
modified: SystemTime,
23+
created: SystemTime,
2124
}
2225

2326
pub struct ReadDir(!);
@@ -33,7 +36,10 @@ pub struct OpenOptions {
3336
}
3437

3538
#[derive(Copy, Clone, Debug, Default)]
36-
pub struct FileTimes {}
39+
pub struct FileTimes {
40+
accessed: Option<SystemTime>,
41+
modified: Option<SystemTime>,
42+
}
3743

3844
#[derive(Clone, PartialEq, Eq, Debug)]
3945
// Bool indicates if file is readonly
@@ -60,15 +66,15 @@ impl FileAttr {
6066
}
6167

6268
pub fn modified(&self) -> io::Result<SystemTime> {
63-
unsupported()
69+
Ok(self.modified)
6470
}
6571

6672
pub fn accessed(&self) -> io::Result<SystemTime> {
67-
unsupported()
73+
Ok(self.accessed)
6874
}
6975

7076
pub fn created(&self) -> io::Result<SystemTime> {
71-
unsupported()
77+
Ok(self.created)
7278
}
7379
}
7480

@@ -92,8 +98,13 @@ impl FilePermissions {
9298
}
9399

94100
impl FileTimes {
95-
pub fn set_accessed(&mut self, _t: SystemTime) {}
96-
pub fn set_modified(&mut self, _t: SystemTime) {}
101+
pub fn set_accessed(&mut self, t: SystemTime) {
102+
self.accessed = Some(t);
103+
}
104+
105+
pub fn set_modified(&mut self, t: SystemTime) {
106+
self.modified = Some(t);
107+
}
97108
}
98109

99110
impl FileType {
@@ -394,6 +405,7 @@ mod uefi_fs {
394405
use crate::path::Path;
395406
use crate::ptr::NonNull;
396407
use crate::sys::helpers;
408+
use crate::sys::time::{self, SystemTime};
397409

398410
pub(crate) struct File(NonNull<file::Protocol>);
399411

@@ -541,4 +553,23 @@ mod uefi_fs {
541553

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

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

Lines changed: 75 additions & 6 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
@@ -28,6 +42,19 @@ fn align() {
2842
}
2943
}
3044

45+
// UEFI Time cannot implement Eq due to uninitilaized pad1 and pad2
46+
fn uefi_time_cmp(t1: r_efi::efi::Time, t2: r_efi::efi::Time) -> bool {
47+
t1.year == t2.year
48+
&& t1.month == t2.month
49+
&& t1.day == t2.day
50+
&& t1.hour == t2.hour
51+
&& t1.minute == t2.minute
52+
&& t1.second == t2.second
53+
&& t1.nanosecond == t2.nanosecond
54+
&& t1.timezone == t2.timezone
55+
&& t1.daylight == t2.daylight
56+
}
57+
3158
#[test]
3259
fn systemtime_start() {
3360
let t = r_efi::efi::Time {
@@ -37,14 +64,15 @@ fn systemtime_start() {
3764
hour: 0,
3865
minute: 0,
3966
second: 0,
67+
pad1: 0,
4068
nanosecond: 0,
4169
timezone: -1440,
4270
daylight: 0,
4371
pad2: 0,
4472
};
4573
assert_eq!(from_uefi(&t), Duration::new(0, 0));
46-
assert_eq!(t, to_uefi(&from_uefi(&t), -1440, 0).unwrap());
47-
assert!(to_uefi(&from_uefi(&t), 0, 0).is_none());
74+
assert!(uefi_time_cmp(t, to_uefi(&from_uefi(&t), -1440, 0).unwrap()));
75+
assert!(to_uefi(&from_uefi(&t), 0, 0).is_err());
4876
}
4977

5078
#[test]
@@ -63,8 +91,8 @@ fn systemtime_utc_start() {
6391
pad2: 0,
6492
};
6593
assert_eq!(from_uefi(&t), Duration::new(1440 * SECS_IN_MINUTE, 0));
66-
assert_eq!(t, to_uefi(&from_uefi(&t), 0, 0).unwrap());
67-
assert!(to_uefi(&from_uefi(&t), -1440, 0).is_some());
94+
assert!(uefi_time_cmp(t, to_uefi(&from_uefi(&t), 0, 0).unwrap()));
95+
assert!(to_uefi(&from_uefi(&t), -1440, 0).is_ok());
6896
}
6997

7098
#[test]
@@ -82,8 +110,49 @@ fn systemtime_end() {
82110
daylight: 0,
83111
pad2: 0,
84112
};
85-
assert!(to_uefi(&from_uefi(&t), 1440, 0).is_some());
86-
assert!(to_uefi(&from_uefi(&t), 1439, 0).is_none());
113+
assert!(to_uefi(&from_uefi(&t), 1440, 0).is_ok());
114+
assert!(to_uefi(&from_uefi(&t), 1439, 0).is_err());
115+
}
116+
117+
#[test]
118+
fn min_time() {
119+
let inp = Duration::from_secs(1440 * SECS_IN_MINUTE);
120+
let new_tz = to_uefi(&inp, 1440, 0).err().unwrap();
121+
assert_eq!(new_tz, 0);
122+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
123+
124+
let inp = Duration::from_secs(1450 * SECS_IN_MINUTE);
125+
let new_tz = to_uefi(&inp, 1440, 0).err().unwrap();
126+
assert_eq!(new_tz, 10);
127+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
128+
129+
let inp = Duration::from_secs(1450 * SECS_IN_MINUTE + 10);
130+
let new_tz = to_uefi(&inp, 1440, 0).err().unwrap();
131+
assert_eq!(new_tz, 10);
132+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
133+
134+
let inp = Duration::from_secs(1430 * SECS_IN_MINUTE);
135+
let new_tz = to_uefi(&inp, 1440, 0).err().unwrap();
136+
assert_eq!(new_tz, -10);
137+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
138+
}
139+
140+
#[test]
141+
fn max_time() {
142+
let inp = MAX_UEFI_TIME.0;
143+
let new_tz = to_uefi(&inp, -1440, 0).err().unwrap();
144+
assert_eq!(new_tz, 1440);
145+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
146+
147+
let inp = MAX_UEFI_TIME.0 - Duration::from_secs(1440 * SECS_IN_MINUTE);
148+
let new_tz = to_uefi(&inp, -1440, 0).err().unwrap();
149+
assert_eq!(new_tz, 0);
150+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
151+
152+
let inp = MAX_UEFI_TIME.0 - Duration::from_secs(1440 * SECS_IN_MINUTE + 10);
153+
let new_tz = to_uefi(&inp, -1440, 0).err().unwrap();
154+
assert_eq!(new_tz, 0);
155+
assert!(to_uefi(&inp, new_tz, 0).is_ok());
87156
}
88157

89158
// UEFI IoSlice and IoSliceMut Tests

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

Lines changed: 62 additions & 27 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

@@ -70,13 +72,32 @@ impl SystemTime {
7072
Self(system_time_internal::from_uefi(&t))
7173
}
7274

73-
#[expect(dead_code)]
74-
pub(crate) const fn to_uefi(self, timezone: i16, daylight: u8) -> Option<r_efi::efi::Time> {
75-
system_time_internal::to_uefi(&self.0, timezone, daylight)
75+
pub(crate) const fn to_uefi(
76+
self,
77+
timezone: i16,
78+
daylight: u8,
79+
) -> Result<r_efi::efi::Time, i16> {
80+
// system_time_internal::to_uefi requires a valid timezone. In case of unspecified timezone,
81+
// we just pass 0 since it is assumed that no timezone related adjustments are required.
82+
if timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
83+
system_time_internal::to_uefi(&self.0, 0, daylight)
84+
} else {
85+
system_time_internal::to_uefi(&self.0, timezone, daylight)
86+
}
87+
}
88+
89+
/// Create UEFI Time with the closest timezone (minute offset) that still allows the time to be
90+
/// represented.
91+
pub(crate) fn to_uefi_loose(self, timezone: i16, daylight: u8) -> r_efi::efi::Time {
92+
match self.to_uefi(timezone, daylight) {
93+
Ok(x) => x,
94+
Err(tz) => self.to_uefi(tz, daylight).unwrap(),
95+
}
7696
}
7797

7898
pub fn now() -> SystemTime {
7999
system_time_internal::now()
100+
.map(Self::from_uefi)
80101
.unwrap_or_else(|| panic!("time not implemented on this platform"))
81102
}
82103

@@ -104,12 +125,11 @@ pub(crate) mod system_time_internal {
104125
use crate::mem::MaybeUninit;
105126
use crate::ptr::NonNull;
106127

107-
const SECS_IN_MINUTE: u64 = 60;
108128
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
109129
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
110-
const TIMEZONE_DELTA: u64 = 1440 * SECS_IN_MINUTE;
130+
const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64;
111131

112-
pub fn now() -> Option<SystemTime> {
132+
pub(crate) fn now() -> Option<Time> {
113133
let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?;
114134
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
115135
let r = unsafe {
@@ -119,9 +139,7 @@ pub(crate) mod system_time_internal {
119139
return None;
120140
}
121141

122-
let t = unsafe { t.assume_init() };
123-
124-
Some(SystemTime::from_uefi(t))
142+
Some(unsafe { t.assume_init() })
125143
}
126144

127145
/// This algorithm is a modified form of the one described in the post
@@ -161,17 +179,15 @@ pub(crate) mod system_time_internal {
161179
+ (t.minute as u64) * SECS_IN_MINUTE
162180
+ (t.hour as u64) * SECS_IN_HOUR;
163181

164-
// Calculate the offset from 1/1/1900 at timezone -1440 min
165-
let adjusted_localtime_epoc: u64 = localtime_epoch + TIMEZONE_DELTA;
166-
167-
let epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
168-
adjusted_localtime_epoc
182+
let normalized_timezone = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
183+
-SYSTEMTIME_TIMEZONE
169184
} else {
170-
adjusted_localtime_epoc
171-
.checked_add_signed((t.timezone as i64) * SECS_IN_MINUTE as i64)
172-
.unwrap()
185+
(t.timezone as i64) * SECS_IN_MINUTE as i64 - SYSTEMTIME_TIMEZONE
173186
};
174187

188+
// Calculate the offset from 1/1/1900 at timezone -1440 min
189+
let epoch = localtime_epoch.checked_add_signed(normalized_timezone).unwrap();
190+
175191
Duration::new(epoch, t.nanosecond)
176192
}
177193

@@ -180,16 +196,24 @@ pub(crate) mod system_time_internal {
180196
///
181197
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
182198
/// epoch used in the original algorithm.
183-
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Option<Time> {
199+
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Result<Time, i16> {
200+
const MIN_IN_HOUR: u64 = 60;
201+
const MIN_IN_DAY: u64 = MIN_IN_HOUR * 24;
202+
184203
// Check timezone validity
185204
assert!(timezone <= 1440 && timezone >= -1440);
186205

187-
// FIXME(#126043): use checked_sub_signed once stabilized
188-
let secs =
189-
dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap();
190-
191206
// Convert to seconds since 1900-01-01-00:00:00 in timezone.
192-
let Some(secs) = secs.checked_sub(TIMEZONE_DELTA) else { return None };
207+
let Some(secs) = dur
208+
.as_secs()
209+
.checked_add_signed(SYSTEMTIME_TIMEZONE - (timezone as i64 * SECS_IN_MINUTE as i64))
210+
else {
211+
// If the current timezone cannot be used, find the closest timezone that will allow the
212+
// conversion to succeed.
213+
let new_tz = (dur.as_secs() / SECS_IN_MINUTE) as i16
214+
+ (SYSTEMTIME_TIMEZONE / SECS_IN_MINUTE as i64) as i16;
215+
return Err(new_tz);
216+
};
193217

194218
let days = secs / SECS_IN_DAY;
195219
let remaining_secs = secs % SECS_IN_DAY;
@@ -212,9 +236,10 @@ pub(crate) mod system_time_internal {
212236
let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
213237
let second = (remaining_secs % SECS_IN_MINUTE) as u8;
214238

215-
// Check Bounds
216-
if y >= 1900 && y <= 9999 {
217-
Some(Time {
239+
// At this point, invalid time will be greater than MAX representable time. It cannot be less
240+
// than minimum time since we already take care of that case above.
241+
if y <= 9999 {
242+
Ok(Time {
218243
year: y as u16,
219244
month: m as u8,
220245
day: d as u8,
@@ -228,7 +253,17 @@ pub(crate) mod system_time_internal {
228253
pad2: 0,
229254
})
230255
} else {
231-
None
256+
assert!(y == 10000);
257+
assert!(m == 1);
258+
259+
let delta = ((d - 1) as u64 * MIN_IN_DAY
260+
+ hour as u64 * MIN_IN_HOUR
261+
+ minute as u64
262+
+ if second == 0 { 0 } else { 1 }) as i16;
263+
let new_tz = timezone + delta;
264+
265+
assert!(new_tz <= 1440 && new_tz >= -1440);
266+
Err(new_tz)
232267
}
233268
}
234269
}

0 commit comments

Comments
 (0)