1use crate::{
2 device::BlockDeviceExt,
3 fs::FileSystem::{self, *},
4 usage::sectors_used,
5};
6use libparted::PartitionFlag;
7use os_detect::{detect_os_from_device, OS};
8use std::{io, path::Path};
9use sys_mount::*;
10use tempdir::TempDir;
11
12pub trait PartitionExt: BlockDeviceExt {
14 fn get_file_system(&self) -> Option<FileSystem>;
16
17 fn get_partition_flags(&self) -> &[PartitionFlag];
19
20 fn get_partition_label(&self) -> Option<&str>;
22
23 fn get_partition_type(&self) -> PartitionType;
27
28 fn get_sector_end(&self) -> u64;
30
31 fn get_sector_start(&self) -> u64;
33
34 fn get_sectors(&self) -> u64 { self.get_sector_end() - self.get_sector_start() }
36
37 fn is_esp_partition(&self) -> bool {
39 self.get_file_system().map_or(false, |fs| {
40 (fs == Fat16 || fs == Fat32)
41 && self.get_partition_flags().contains(&PartitionFlag::PED_PARTITION_ESP)
42 })
43 }
44
45 fn is_linux_compatible(&self) -> bool {
47 self.get_file_system().map_or(false, |fs| match fs {
48 Exfat | Ntfs | Fat16 | Fat32 | Lvm | Luks | Swap => false,
49 Btrfs | Xfs | Ext2 | Ext3 | Ext4 | F2fs => true,
50 })
51 }
52
53 fn is_luks(&self) -> bool { self.get_file_system().map_or(false, |fs| fs == FileSystem::Luks) }
55
56 fn is_swap(&self) -> bool { self.get_file_system().map_or(false, |fs| fs == FileSystem::Swap) }
58
59 fn probe<T, F>(&self, mut func: F) -> T
61 where
62 F: FnMut(Option<(&Path, UnmountDrop<Mount>)>) -> T,
63 {
64 let mount =
65 self.get_file_system().and_then(|fs| TempDir::new("distinst").ok().map(|t| (fs, t)));
66
67 if let Some((fs, tempdir)) = mount {
68 let fs = match fs {
69 FileSystem::Fat16 | FileSystem::Fat32 => "vfat",
70 fs => fs.into(),
71 };
72
73 let base = tempdir.path();
75 if let Ok(m) = Mount::new(self.get_device_path(), base, fs, MountFlags::empty(), None) {
76 return func(Some((base, m.into_unmount_drop(UnmountFlags::DETACH))));
77 }
78 }
79
80 func(None)
81 }
82
83 fn probe_os(&self) -> Option<OS> {
86 self.get_file_system().and_then(|fs| detect_os_from_device(self.get_device_path(), fs))
87 }
88
89 fn sectors_differ_from<P: PartitionExt>(&self, other: &P) -> bool {
91 self.get_sector_start() != other.get_sector_start()
92 || self.get_sector_end() != other.get_sector_end()
93 }
94
95 fn sector_lies_within(&self, sector: u64) -> bool {
97 sector >= self.get_sector_start() && sector <= self.get_sector_end()
98 }
99
100 fn sectors_overlap(&self, start: u64, end: u64) -> bool {
102 let pstart = self.get_sector_start();
103 let pend = self.get_sector_end();
104 !((start < pstart && end < pstart) || (start > pend && end > pend))
105 }
106
107 fn sectors_used(&self) -> io::Result<u64> {
114 let sector_size = self.get_logical_block_size();
115 self.get_file_system()
116 .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "no file system"))
117 .and_then(|fs| sectors_used(self.get_device_path(), fs))
119 .map(move |sectors| sectors / (sector_size / 512))
121 }
122}
123
124#[derive(Debug, PartialEq, Clone, Copy, Hash)]
130pub enum PartitionType {
131 Primary,
132 Logical,
133 Extended,
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 struct Fake {
141 start_sector: u64,
142 end_sector: u64,
143 filesystem: Option<FileSystem>,
144 name: Option<String>,
145 part_type: PartitionType,
146 flags: Vec<PartitionFlag>,
147 }
148
149 impl Default for Fake {
150 fn default() -> Fake {
151 Self {
152 start_sector: 0,
153 end_sector: 1,
154 filesystem: None,
155 name: None,
156 part_type: PartitionType::Primary,
157 flags: Vec::new(),
158 }
159 }
160 }
161
162 impl BlockDeviceExt for Fake {
163 fn get_device_name(&self) -> &str { "fictional" }
164
165 fn get_device_path(&self) -> &Path { Path::new("/dev/fictional") }
166 }
167
168 impl PartitionExt for Fake {
169 fn get_file_system(&self) -> Option<FileSystem> { self.filesystem }
170
171 fn get_partition_flags(&self) -> &[PartitionFlag] { &self.flags }
172
173 fn get_partition_label(&self) -> Option<&str> { self.name.as_ref().map(|s| s.as_str()) }
174
175 fn get_partition_type(&self) -> PartitionType { self.part_type }
176
177 fn get_sector_end(&self) -> u64 { self.end_sector }
178
179 fn get_sector_start(&self) -> u64 { self.start_sector }
180 }
181
182 #[test]
183 fn sector_lies_within() {
184 let mut part = Fake::default();
185 part.start_sector = 100_000;
186 part.end_sector = 10_000_000;
187
188 assert!(part.sector_lies_within(100_000));
189 assert!(part.sector_lies_within(10_000_000));
190 assert!(part.sector_lies_within(5_000_000));
191 assert!(!part.sector_lies_within(99_999));
192 assert!(!part.sector_lies_within(10_000_001));
193 }
194
195 #[test]
196 fn sectors_overlap() {
197 let mut part = Fake::default();
198 part.start_sector = 100_000;
199 part.end_sector = 10_000_000;
200
201 assert!(!part.sectors_overlap(0, 99999));
202 assert!(part.sectors_overlap(0, 100_000));
203 assert!(part.sectors_overlap(0, 100_001));
204 assert!(part.sectors_overlap(200_000, 1_000_000));
205 assert!(part.sectors_overlap(9_999_999, 11_000_000));
206 assert!(part.sectors_overlap(10_000_000, 11_000_000));
207 assert!(!part.sectors_overlap(10_000_001, 11_000_000));
208 assert!(part.sectors_overlap(0, 20_000_000))
209 }
210}