1use crate::{mount, DevicePath};
6use im::{ordset, OrdSet};
7use std::path::PathBuf;
8
9type Children = OrdSet<Device>;
10pub type Paths = OrdSet<DevicePath>;
11
12#[derive(
13 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
14)]
15pub struct Root {
16 pub children: Children,
17}
18
19impl Default for Root {
20 fn default() -> Self {
21 Self {
22 children: ordset![],
23 }
24 }
25}
26
27#[derive(
28 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
29)]
30pub struct ScsiDevice {
31 pub serial: Option<String>,
32 pub scsi80: Option<String>,
33 pub major: String,
34 pub minor: String,
35 pub devpath: PathBuf,
36 pub size: u64,
37 pub filesystem_type: Option<String>,
38 pub fs_uuid: Option<String>,
39 pub fs_label: Option<String>,
40 pub paths: Paths,
41 pub mount: Option<mount::Mount>,
42 pub children: Children,
43}
44
45#[derive(
46 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
47)]
48pub struct Partition {
49 pub serial: Option<String>,
50 pub scsi80: Option<String>,
51 pub partition_number: u64,
52 pub size: u64,
53 pub major: String,
54 pub minor: String,
55 pub devpath: PathBuf,
56 pub filesystem_type: Option<String>,
57 pub fs_uuid: Option<String>,
58 pub fs_label: Option<String>,
59 pub paths: Paths,
60 pub mount: Option<mount::Mount>,
61 pub children: Children,
62}
63
64#[derive(
65 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
66)]
67pub struct MdRaid {
68 pub size: u64,
69 pub major: String,
70 pub minor: String,
71 pub filesystem_type: Option<String>,
72 pub fs_uuid: Option<String>,
73 pub fs_label: Option<String>,
74 pub paths: Paths,
75 pub mount: Option<mount::Mount>,
76 pub uuid: String,
77 pub children: Children,
78}
79
80#[derive(
81 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
82)]
83pub struct Mpath {
84 pub devpath: PathBuf,
85 pub serial: Option<String>,
86 pub scsi80: Option<String>,
87 pub dm_name: String,
88 pub size: u64,
89 pub major: String,
90 pub minor: String,
91 pub filesystem_type: Option<String>,
92 pub fs_uuid: Option<String>,
93 pub fs_label: Option<String>,
94 pub paths: Paths,
95 pub children: Children,
96 pub mount: Option<mount::Mount>,
97}
98
99#[derive(
100 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
101)]
102pub struct VolumeGroup {
103 pub name: String,
104 pub uuid: String,
105 pub size: u64,
106 pub children: Children,
107}
108
109#[derive(
110 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
111)]
112pub struct LogicalVolume {
113 pub name: String,
114 pub uuid: String,
115 pub major: String,
116 pub minor: String,
117 pub size: u64,
118 pub children: Children,
119 pub devpath: PathBuf,
120 pub paths: Paths,
121 pub filesystem_type: Option<String>,
122 pub fs_uuid: Option<String>,
123 pub fs_label: Option<String>,
124 pub mount: Option<mount::Mount>,
125}
126
127#[derive(
128 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
129)]
130pub struct Zpool {
131 pub guid: u64,
132 pub name: String,
133 pub health: String,
134 pub state: String,
135 pub size: u64,
136 pub vdev: libzfs_types::VDev,
137 pub props: Vec<libzfs_types::ZProp>,
138 pub children: Children,
139 pub mount: Option<mount::Mount>,
140}
141
142#[derive(
143 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
144)]
145pub struct Dataset {
146 pub guid: u64,
147 pub name: String,
148 pub kind: String,
149 pub props: Vec<libzfs_types::ZProp>,
150 pub mount: Option<mount::Mount>,
151}
152
153#[derive(
154 Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, Clone,
155)]
156pub enum Device {
157 Root(Root),
158 ScsiDevice(ScsiDevice),
159 Partition(Partition),
160 MdRaid(MdRaid),
161 Mpath(Mpath),
162 VolumeGroup(VolumeGroup),
163 LogicalVolume(LogicalVolume),
164 Zpool(Zpool),
165 Dataset(Dataset),
166}
167
168#[derive(Debug, serde::Serialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
169pub struct DeviceId(pub String);
170
171impl Device {
172 pub fn find_device_by_devpath(&self, dev_path: &DevicePath) -> Option<&Device> {
173 match self {
174 Self::Root(x) => x
175 .children
176 .iter()
177 .find_map(|c| c.find_device_by_devpath(dev_path)),
178 Self::ScsiDevice(x) => {
179 if x.paths.contains(dev_path) {
180 return Some(self);
181 }
182
183 x.children
184 .iter()
185 .find_map(|c| c.find_device_by_devpath(dev_path))
186 }
187 Self::Partition(x) => {
188 if x.paths.contains(dev_path) {
189 return Some(self);
190 }
191
192 x.children
193 .iter()
194 .find_map(|c| c.find_device_by_devpath(dev_path))
195 }
196 Self::MdRaid(x) => {
197 if x.paths.contains(dev_path) {
198 return Some(self);
199 }
200
201 x.children
202 .iter()
203 .find_map(|c| c.find_device_by_devpath(dev_path))
204 }
205 Self::Mpath(x) => {
206 if x.paths.contains(dev_path) {
207 return Some(self);
208 }
209
210 x.children
211 .iter()
212 .find_map(|c| c.find_device_by_devpath(dev_path))
213 }
214 Self::VolumeGroup(x) => x
215 .children
216 .iter()
217 .find_map(|c| c.find_device_by_devpath(dev_path)),
218 Self::LogicalVolume(x) => {
219 if x.paths.contains(dev_path) {
220 return Some(self);
221 }
222
223 x.children
224 .iter()
225 .find_map(|c| c.find_device_by_devpath(dev_path))
226 }
227 Self::Zpool(x) => x
228 .children
229 .iter()
230 .find_map(|c| c.find_device_by_devpath(dev_path)),
231 Self::Dataset(x) => {
232 if x.name == dev_path.0.to_string_lossy() {
233 Some(self)
234 } else {
235 None
236 }
237 }
238 }
239 }
240 pub fn get_fs_uuid(&self) -> Option<&str> {
241 match self {
242 Self::Root(_) => None,
243 Self::ScsiDevice(x) => x.fs_uuid.as_deref(),
244 Self::Partition(x) => x.fs_uuid.as_deref(),
245 Self::MdRaid(x) => x.fs_uuid.as_deref(),
246 Self::Mpath(x) => x.fs_uuid.as_deref(),
247 Self::VolumeGroup(_) => None,
248 Self::LogicalVolume(x) => x.fs_uuid.as_deref(),
249 Self::Zpool(_) => None,
250 Self::Dataset(x) => {
251 let guid = x.props.iter().find(|x| x.name == "guid")?;
252
253 Some(&guid.value)
254 }
255 }
256 }
257 pub fn find_device_by_id(&self, id: &DeviceId) -> Option<&Device> {
258 match self {
259 Self::Root(x) => x.children.iter().find_map(|c| c.find_device_by_id(id)),
260 Self::ScsiDevice(x) => {
261 if self.get_id().as_ref()? == id {
262 return Some(self);
263 }
264
265 x.children.iter().find_map(|c| c.find_device_by_id(id))
266 }
267 Self::Partition(x) => {
268 if self.get_id().as_ref()? == id {
269 return Some(self);
270 }
271
272 x.children.iter().find_map(|c| c.find_device_by_id(id))
273 }
274 Self::MdRaid(x) => {
275 if self.get_id().as_ref()? == id {
276 return Some(self);
277 }
278
279 x.children.iter().find_map(|c| c.find_device_by_id(id))
280 }
281 Self::Mpath(x) => {
282 if self.get_id().as_ref()? == id {
283 return Some(self);
284 }
285
286 x.children.iter().find_map(|c| c.find_device_by_id(id))
287 }
288 Self::VolumeGroup(x) => {
289 if self.get_id().as_ref()? == id {
290 return Some(self);
291 }
292
293 x.children.iter().find_map(|c| c.find_device_by_id(id))
294 }
295 Self::LogicalVolume(x) => {
296 if self.get_id().as_ref()? == id {
297 return Some(self);
298 }
299
300 x.children.iter().find_map(|c| c.find_device_by_id(id))
301 }
302 Self::Zpool(x) => {
303 if self.get_id().as_ref()? == id {
304 return Some(self);
305 }
306
307 x.children.iter().find_map(|c| c.find_device_by_id(id))
308 }
309 Self::Dataset(_) => {
310 if self.get_id().as_ref()? == id {
311 Some(self)
312 } else {
313 None
314 }
315 }
316 }
317 }
318 pub fn get_id(&self) -> Option<DeviceId> {
319 match self {
320 Self::Root(_) => Some(DeviceId("root".into())),
321 Self::ScsiDevice(x) => Some(DeviceId(format!("scsi_{}", x.serial.as_ref()?))),
322 Self::Partition(x) => Some(DeviceId(format!(
323 "partition{}_{}",
324 x.partition_number,
325 x.serial.as_ref()?
326 ))),
327 Self::MdRaid(x) => Some(DeviceId(format!("mdraid_{}", x.uuid))),
328 Self::Mpath(x) => Some(DeviceId(format!("mpath_{}", x.serial.as_ref()?))),
329 Self::VolumeGroup(x) => Some(DeviceId(format!("vg_{}", x.uuid))),
330 Self::LogicalVolume(x) => Some(DeviceId(format!("lv_{}", x.uuid))),
331 Self::Zpool(x) => Some(DeviceId(format!("zpool_{}", x.guid))),
332 Self::Dataset(x) => Some(DeviceId(format!("dataset_{}", x.guid))),
333 }
334 }
335 pub fn children(&self) -> Option<&OrdSet<Device>> {
336 match self {
337 Self::Root(x) => Some(&x.children),
338 Self::ScsiDevice(x) => Some(&x.children),
339 Self::Partition(x) => Some(&x.children),
340 Self::MdRaid(x) => Some(&x.children),
341 Self::Mpath(x) => Some(&x.children),
342 Self::VolumeGroup(x) => Some(&x.children),
343 Self::LogicalVolume(x) => Some(&x.children),
344 Self::Zpool(x) => Some(&x.children),
345 Self::Dataset(_) => None,
346 }
347 }
348}