use crate::{
device::BlockDeviceExt,
fs::FileSystem::{self, *},
usage::sectors_used,
};
use libparted::PartitionFlag;
use os_detect::{detect_os_from_device, OS};
use std::{io, path::Path};
use sys_mount::*;
use tempdir::TempDir;
pub trait PartitionExt: BlockDeviceExt {
fn get_file_system(&self) -> Option<FileSystem>;
fn get_partition_flags(&self) -> &[PartitionFlag];
fn get_partition_label(&self) -> Option<&str>;
fn get_partition_type(&self) -> PartitionType;
fn get_sector_end(&self) -> u64;
fn get_sector_start(&self) -> u64;
fn get_sectors(&self) -> u64 { self.get_sector_end() - self.get_sector_start() }
fn is_esp_partition(&self) -> bool {
self.get_file_system().map_or(false, |fs| {
(fs == Fat16 || fs == Fat32)
&& self.get_partition_flags().contains(&PartitionFlag::PED_PARTITION_ESP)
})
}
fn is_linux_compatible(&self) -> bool {
self.get_file_system().map_or(false, |fs| match fs {
Exfat | Ntfs | Fat16 | Fat32 | Lvm | Luks | Swap => false,
Btrfs | Xfs | Ext2 | Ext3 | Ext4 | F2fs => true,
})
}
fn is_luks(&self) -> bool { self.get_file_system().map_or(false, |fs| fs == FileSystem::Luks) }
fn is_swap(&self) -> bool { self.get_file_system().map_or(false, |fs| fs == FileSystem::Swap) }
fn probe<T, F>(&self, mut func: F) -> T
where
F: FnMut(Option<(&Path, UnmountDrop<Mount>)>) -> T,
{
let mount =
self.get_file_system().and_then(|fs| TempDir::new("distinst").ok().map(|t| (fs, t)));
if let Some((fs, tempdir)) = mount {
let fs = match fs {
FileSystem::Fat16 | FileSystem::Fat32 => "vfat",
fs => fs.into(),
};
let base = tempdir.path();
if let Ok(m) = Mount::new(self.get_device_path(), base, fs, MountFlags::empty(), None) {
return func(Some((base, m.into_unmount_drop(UnmountFlags::DETACH))));
}
}
func(None)
}
fn probe_os(&self) -> Option<OS> {
self.get_file_system().and_then(|fs| detect_os_from_device(self.get_device_path(), fs))
}
fn sectors_differ_from<P: PartitionExt>(&self, other: &P) -> bool {
self.get_sector_start() != other.get_sector_start()
|| self.get_sector_end() != other.get_sector_end()
}
fn sector_lies_within(&self, sector: u64) -> bool {
sector >= self.get_sector_start() && sector <= self.get_sector_end()
}
fn sectors_overlap(&self, start: u64, end: u64) -> bool {
let pstart = self.get_sector_start();
let pend = self.get_sector_end();
!((start < pstart && end < pstart) || (start > pend && end > pend))
}
fn sectors_used(&self) -> io::Result<u64> {
let sector_size = self.get_logical_block_size();
self.get_file_system()
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "no file system"))
.and_then(|fs| sectors_used(self.get_device_path(), fs))
.map(move |sectors| sectors / (sector_size / 512))
}
}
#[derive(Debug, PartialEq, Clone, Copy, Hash)]
pub enum PartitionType {
Primary,
Logical,
Extended,
}
#[cfg(test)]
mod tests {
use super::*;
struct Fake {
start_sector: u64,
end_sector: u64,
filesystem: Option<FileSystem>,
name: Option<String>,
part_type: PartitionType,
flags: Vec<PartitionFlag>,
}
impl Default for Fake {
fn default() -> Fake {
Self {
start_sector: 0,
end_sector: 1,
filesystem: None,
name: None,
part_type: PartitionType::Primary,
flags: Vec::new(),
}
}
}
impl BlockDeviceExt for Fake {
fn get_device_name(&self) -> &str { "fictional" }
fn get_device_path(&self) -> &Path { Path::new("/dev/fictional") }
}
impl PartitionExt for Fake {
fn get_file_system(&self) -> Option<FileSystem> { self.filesystem }
fn get_partition_flags(&self) -> &[PartitionFlag] { &self.flags }
fn get_partition_label(&self) -> Option<&str> { self.name.as_ref().map(|s| s.as_str()) }
fn get_partition_type(&self) -> PartitionType { self.part_type }
fn get_sector_end(&self) -> u64 { self.end_sector }
fn get_sector_start(&self) -> u64 { self.start_sector }
}
#[test]
fn sector_lies_within() {
let mut part = Fake::default();
part.start_sector = 100_000;
part.end_sector = 10_000_000;
assert!(part.sector_lies_within(100_000));
assert!(part.sector_lies_within(10_000_000));
assert!(part.sector_lies_within(5_000_000));
assert!(!part.sector_lies_within(99_999));
assert!(!part.sector_lies_within(10_000_001));
}
#[test]
fn sectors_overlap() {
let mut part = Fake::default();
part.start_sector = 100_000;
part.end_sector = 10_000_000;
assert!(!part.sectors_overlap(0, 99999));
assert!(part.sectors_overlap(0, 100_000));
assert!(part.sectors_overlap(0, 100_001));
assert!(part.sectors_overlap(200_000, 1_000_000));
assert!(part.sectors_overlap(9_999_999, 11_000_000));
assert!(part.sectors_overlap(10_000_000, 11_000_000));
assert!(!part.sectors_overlap(10_000_001, 11_000_000));
assert!(part.sectors_overlap(0, 20_000_000))
}
}