mirror of
https://gitea.tendokyu.moe/beerpsi/fsdecrypt.git
synced 2026-09-28 17:58:10 +03:00
fix: parse exFAT UtcOffset per spec, tolerate bad timestamps
This commit is contained in:
+44
-25
@@ -30,22 +30,36 @@ fn exfat_timestamp_to_system_time(
|
||||
) -> Result<SystemTime> {
|
||||
let exfat_date = timestamp.date();
|
||||
let exfat_time = timestamp.time();
|
||||
// exFAT UTC offset is in 15-minute intervals, so 1 = UTC+00:15, 2 = UTC+00:30, etc.
|
||||
let exfat_utc_offset = timestamp.utc_offset() as i32 * 15 * 60;
|
||||
let chrono_date_time = FixedOffset::east_opt(exfat_utc_offset)
|
||||
.ok_or_else(|| anyhow!("invaid utc offset: {}", timestamp.utc_offset()))?
|
||||
.with_ymd_and_hms(
|
||||
exfat_date.year as i32,
|
||||
exfat_date.month as u32,
|
||||
exfat_date.day as u32,
|
||||
exfat_time.hour as u32,
|
||||
exfat_time.minute as u32,
|
||||
exfat_time.second as u32,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
return Ok(SystemTime::UNIX_EPOCH
|
||||
+ Duration::from_micros(chrono_date_time.timestamp_micros().try_into()?));
|
||||
// The exFAT UtcOffset byte packs an OffsetValid flag (bit 7) with a 7-bit
|
||||
// two's-complement OffsetFromUtc in 15-minute units. When OffsetValid is 0
|
||||
// the timestamp has no timezone info and the offset bits must be ignored.
|
||||
let raw = timestamp.utc_offset() as u8;
|
||||
let offset_seconds = if raw & 0x80 == 0 {
|
||||
0
|
||||
} else {
|
||||
let offset_quarters = (((raw & 0x7F) << 1) as i8) >> 1;
|
||||
offset_quarters as i32 * 15 * 60
|
||||
};
|
||||
let fixed_offset = FixedOffset::east_opt(offset_seconds).unwrap_or_else(|| FixedOffset::east_opt(0).unwrap());
|
||||
|
||||
let chrono_date_time = match fixed_offset.with_ymd_and_hms(
|
||||
exfat_date.year as i32,
|
||||
exfat_date.month as u32,
|
||||
exfat_date.day as u32,
|
||||
exfat_time.hour as u32,
|
||||
exfat_time.minute as u32,
|
||||
exfat_time.second as u32,
|
||||
) {
|
||||
chrono::LocalResult::Single(dt) => dt,
|
||||
_ => return Ok(SystemTime::UNIX_EPOCH),
|
||||
};
|
||||
|
||||
let micros: u64 = chrono_date_time
|
||||
.timestamp_micros()
|
||||
.try_into()
|
||||
.unwrap_or(0);
|
||||
Ok(SystemTime::UNIX_EPOCH + Duration::from_micros(micros))
|
||||
}
|
||||
|
||||
fn extract_exfat_contents(exfat_path: &Path) -> Result<()> {
|
||||
@@ -96,17 +110,22 @@ fn extract_exfat_elements(
|
||||
match element {
|
||||
FsElement::F(ref mut file) => {
|
||||
let dest_path = output_dir.join(file.name());
|
||||
let mut dest = File::create(dest_path)?;
|
||||
let mut dest = File::create(&dest_path)?;
|
||||
|
||||
dest.set_times(
|
||||
FileTimes::new()
|
||||
.set_accessed(exfat_timestamp_to_system_time(
|
||||
file.timestamps().accessed(),
|
||||
)?)
|
||||
.set_modified(exfat_timestamp_to_system_time(
|
||||
file.timestamps().modified(),
|
||||
)?),
|
||||
)?;
|
||||
let accessed = exfat_timestamp_to_system_time(file.timestamps().accessed());
|
||||
let modified = exfat_timestamp_to_system_time(file.timestamps().modified());
|
||||
if let (Ok(accessed), Ok(modified)) = (accessed, modified) {
|
||||
if let Err(e) = dest.set_times(
|
||||
FileTimes::new()
|
||||
.set_accessed(accessed)
|
||||
.set_modified(modified),
|
||||
) {
|
||||
println!(
|
||||
"WARNING: Failed to set times on {}: {e}",
|
||||
dest_path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut writer = BufWriter::with_capacity(256 * 1024, &mut dest);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user