xref: /DADK/dadk/src/actions/rootfs/loopdev.rs (revision cfb7b78ff5dff2a09cba93336ba222be89cbd3e1)
1 use std::{path::PathBuf, process::Command};
2 
3 use anyhow::{anyhow, Result};
4 
5 pub struct LoopDevice {
6     img_path: PathBuf,
7     loop_device_path: Option<String>,
8 }
9 impl LoopDevice {
10     pub fn attach(&mut self) -> Result<()> {
11         if self.loop_device_path.is_some() {
12             return Ok(());
13         }
14         let output = Command::new("losetup")
15             .arg("-f")
16             .arg("--show")
17             .arg("-P")
18             .arg(&self.img_path)
19             .output()?;
20 
21         if output.status.success() {
22             let loop_device = String::from_utf8(output.stdout)?.trim().to_string();
23             self.loop_device_path = Some(loop_device);
24             log::trace!(
25                 "Loop device attached: {}",
26                 self.loop_device_path.as_ref().unwrap()
27             );
28             Ok(())
29         } else {
30             Err(anyhow::anyhow!(
31                 "Failed to mount disk image: losetup command exited with status {}",
32                 output.status
33             ))
34         }
35     }
36     /// 获取指定分区的路径
37     ///
38     /// # 参数
39     ///
40     /// * `nth` - 分区的编号
41     ///
42     /// # 返回值
43     ///
44     /// 返回一个 `Result<String>`,包含分区路径的字符串。如果循环设备未附加,则返回错误。
45     ///
46     /// # 错误
47     ///
48     /// 如果循环设备未附加,则返回 `anyhow!("Loop device not attached")` 错误。
49     pub fn partition_path(&self, nth: u8) -> Result<PathBuf> {
50         if self.loop_device_path.is_none() {
51             return Err(anyhow!("Loop device not attached"));
52         }
53         let s = format!("{}p{}", self.loop_device_path.as_ref().unwrap(), nth);
54         let s = PathBuf::from(s);
55         // 判断路径是否存在
56         if !s.exists() {
57             return Err(anyhow!("Partition not exist"));
58         }
59         Ok(s)
60     }
61 
62     pub fn detach(&mut self) -> Result<()> {
63         if self.loop_device_path.is_none() {
64             return Ok(());
65         }
66         let loop_device = self.loop_device_path.take().unwrap();
67         let output = Command::new("losetup")
68             .arg("-d")
69             .arg(loop_device)
70             .output()?;
71 
72         if output.status.success() {
73             self.loop_device_path = None;
74             Ok(())
75         } else {
76             Err(anyhow::anyhow!("Failed to detach loop device"))
77         }
78     }
79 }
80 
81 impl Drop for LoopDevice {
82     fn drop(&mut self) {
83         self.detach().expect("Failed to detach loop device");
84     }
85 }
86 
87 pub struct LoopDeviceBuilder {
88     img_path: Option<PathBuf>,
89 }
90 
91 impl LoopDeviceBuilder {
92     pub fn new() -> Self {
93         LoopDeviceBuilder { img_path: None }
94     }
95 
96     pub fn img_path(mut self, img_path: PathBuf) -> Self {
97         self.img_path = Some(img_path);
98         self
99     }
100 
101     pub fn build(self) -> Result<LoopDevice> {
102         let mut loop_dev = LoopDevice {
103             img_path: self.img_path.unwrap(),
104             loop_device_path: None,
105         };
106         loop_dev.attach()?;
107         Ok(loop_dev)
108     }
109 }
110