#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum DetectedFs {
Ext,
Ntfs,
Fat,
Apfs,
LinuxSwap,
LinuxLvm,
Luks,
Xfs,
Btrfs,
ExFat,
AllZeros,
Unknown,
}
const FAT_OEM_IDS: &[&[u8]] = &[
b"MSDOS5.0",
b"MSWIN4.0",
b"MSWIN4.1",
b"mkdosfs ",
b"FreeDOS ",
];
#[must_use]
pub fn detect(sector: &[u8]) -> DetectedFs {
if sector.is_empty() {
return DetectedFs::Unknown;
}
if sector.iter().all(|&b| b == 0) {
return DetectedFs::AllZeros;
}
if let Some(fs) = forensicnomicon::filesystems::detect_name(sector).and_then(map_fs_name) {
return fs;
}
if sector.len() >= 11 && FAT_OEM_IDS.iter().any(|id| *id == §or[3..11]) {
return DetectedFs::Fat;
}
if sector.len() >= 4096 && §or[4086..4096] == b"PAGESPACE1" {
return DetectedFs::LinuxSwap;
}
DetectedFs::Unknown
}
fn map_fs_name(name: &str) -> Option<DetectedFs> {
Some(match name {
"ext2/3/4" => DetectedFs::Ext,
"NTFS" => DetectedFs::Ntfs,
"exFAT" => DetectedFs::ExFat,
"XFS" => DetectedFs::Xfs,
"LUKS" => DetectedFs::Luks,
"APFS" => DetectedFs::Apfs,
"Btrfs" => DetectedFs::Btrfs,
"LVM2" => DetectedFs::LinuxLvm,
"Linux swap" => DetectedFs::LinuxSwap,
"FAT32" | "FAT16" | "FAT12" => DetectedFs::Fat,
_ => return None,
})
}
#[must_use]
pub fn type_conflicts(declared: crate::partition::PartitionFamily, detected: DetectedFs) -> bool {
use crate::partition::PartitionFamily as Pf;
use DetectedFs as Df;
if matches!(detected, Df::Unknown | Df::AllZeros) {
return false;
}
matches!(
(declared, detected),
(
Pf::Ntfs,
Df::Ext
| Df::Fat
| Df::Luks
| Df::LinuxSwap
| Df::LinuxLvm
| Df::Xfs
| Df::Btrfs
| Df::Apfs
) | (
Pf::Fat16 | Pf::Fat32 | Pf::Fat12,
Df::Ntfs
| Df::Ext
| Df::Luks
| Df::LinuxSwap
| Df::LinuxLvm
| Df::Xfs
| Df::Btrfs
| Df::Apfs
) | (Pf::Linux, Df::Ntfs | Df::Fat | Df::Luks | Df::Apfs)
| (
Pf::LinuxSwap,
Df::Ntfs | Df::Fat | Df::Ext | Df::Btrfs | Df::Apfs
)
| (
Pf::LinuxLvm,
Df::Ntfs | Df::Fat | Df::Ext | Df::Btrfs | Df::Apfs
)
)
}