mirror of
https://gitea.tendokyu.moe/beerpsi/fsdecrypt.git
synced 2026-09-28 09:48:06 +03:00
Merge pull request 'fix: skip NTFS DOS (8.3) short-name entries when extracting' (#7) from jujuforce/fsdecrypt:fix/ntfs-dos-short-name-extraction into trunk
This commit is contained in:
+46
-3
@@ -7,7 +7,10 @@ use std::{
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use ntfs::{structured_values::NtfsStandardInformation, Ntfs, NtfsAttributeType, NtfsTime};
|
||||
use ntfs::{
|
||||
structured_values::{NtfsFileNamespace, NtfsStandardInformation},
|
||||
Ntfs, NtfsAttributeType, NtfsTime,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
@@ -563,6 +566,17 @@ fn is_ntfs_system_entry(name: &str) -> bool {
|
||||
name.starts_with('$') || name == "." || name == ".." || name == "System Volume Information"
|
||||
}
|
||||
|
||||
/// Whether a directory-index entry should be skipped when extracting.
|
||||
///
|
||||
/// Besides NTFS system metadata, this skips DOS (8.3) short-name aliases: a file
|
||||
/// that has a separate short name appears in the index *twice* — once with its
|
||||
/// Win32 long name and once with the `Dos` short name. Without this, every such
|
||||
/// entry is extracted a second time under its mangled `NAME~1.EXT` name (and
|
||||
/// short-named directories get their whole subtree duplicated).
|
||||
fn skip_index_entry(namespace: NtfsFileNamespace, name: &str) -> bool {
|
||||
namespace == NtfsFileNamespace::Dos || is_ntfs_system_entry(name)
|
||||
}
|
||||
|
||||
fn ntfs_time_to_system_time(t: NtfsTime) -> SystemTime {
|
||||
let nanos = (t.nt_timestamp() - WINDOWS_EPOCH_OFFSET) * 100;
|
||||
SystemTime::UNIX_EPOCH + Duration::from_nanos(nanos)
|
||||
@@ -602,7 +616,7 @@ fn extract_ntfs_dir<T: Read + Seek>(
|
||||
let entry = entry?;
|
||||
let key = entry.key().ok_or_else(|| anyhow!("missing key"))??;
|
||||
let name = key.name().to_string_lossy();
|
||||
if is_ntfs_system_entry(&name) {
|
||||
if skip_index_entry(key.namespace(), &name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -649,7 +663,7 @@ fn calculate_ntfs_size<T: Read + Seek>(
|
||||
while let Some(entry) = iter.next(fs) {
|
||||
let entry = entry?;
|
||||
let key = entry.key().ok_or_else(|| anyhow!("missing key"))??;
|
||||
if is_ntfs_system_entry(&key.name().to_string_lossy().as_ref()) {
|
||||
if skip_index_entry(key.namespace(), key.name().to_string_lossy().as_ref()) {
|
||||
continue;
|
||||
}
|
||||
let file = entry.to_file(ntfs, fs)?;
|
||||
@@ -748,3 +762,32 @@ fn read_be_u32(buf: &[u8], offset: usize) -> u32 {
|
||||
fn read_be_u64(buf: &[u8], offset: usize) -> u64 {
|
||||
u64::from_be_bytes(buf[offset..offset + 8].try_into().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::skip_index_entry;
|
||||
use ntfs::structured_values::NtfsFileNamespace;
|
||||
|
||||
#[test]
|
||||
fn skips_dos_short_name_aliases() {
|
||||
// 8.3 aliases duplicate a Win32 entry and must not be extracted again.
|
||||
assert!(skip_index_entry(NtfsFileNamespace::Dos, "OXGETH~1.EXE"));
|
||||
assert!(skip_index_entry(NtfsFileNamespace::Dos, "PROGRA~1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_long_and_native_names() {
|
||||
assert!(!skip_index_entry(NtfsFileNamespace::Win32, "oxGetHwInfo.exe"));
|
||||
// A name that is its own short name (no separate Dos entry) is kept.
|
||||
assert!(!skip_index_entry(NtfsFileNamespace::Win32AndDos, "game.bat"));
|
||||
assert!(!skip_index_entry(NtfsFileNamespace::Posix, "readme"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skips_system_entries_regardless_of_namespace() {
|
||||
assert!(skip_index_entry(NtfsFileNamespace::Win32, "$MFT"));
|
||||
assert!(skip_index_entry(NtfsFileNamespace::Win32, "System Volume Information"));
|
||||
assert!(skip_index_entry(NtfsFileNamespace::Win32, "."));
|
||||
assert!(skip_index_entry(NtfsFileNamespace::Win32, ".."));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user