fractal_btrfs_wrappers/
lib.rs1use anyhow::{anyhow, Error, Result};
2use lazy_static::lazy_static;
3use log::*;
4use regex::Regex;
5use std::path::{Path, PathBuf};
6use std::process::Stdio;
7use tokio::process::{Child, Command};
8
9pub async fn btrfs_subvolume_create(path: &Path) -> Result<()> {
13 let mut command = Command::new("btrfs");
14 command.arg("subvolume").arg("create").arg(path);
15 debug!("Creating BTRFS subvolume: {:?}", command);
16 let output = command.output().await.unwrap();
17 if !output.status.success() {
18 return Err(anyhow!("Error creating BTRFS subvolume: {output:?}"));
19 }
20 Ok(())
21}
22
23pub async fn btrfs_subvolume_snapshot(path: &Path, snapshot: &Path, readonly: bool) -> Result<()> {
26 let mut command = Command::new("btrfs");
27 command.arg("subvolume").arg("snapshot");
28 if readonly {
29 command.arg("-r");
30 }
31 command.arg(path).arg(snapshot);
32 debug!("Snapshotting BTRFS volume: {:?}", command);
33 let output = command.output().await.unwrap();
34 if !output.status.success() {
35 return Err(anyhow!("Error snapshotting subvolume: {:?}", output));
36 }
37 Ok(())
38}
39
40pub async fn btrfs_subvolume_delete(path: &Path) -> Result<()> {
42 let mut command = Command::new("btrfs");
43 command.arg("subvolume").arg("delete").arg(path);
44 debug!("Deleting BTRFS volume: {:?}", command);
45 let output = command.output().await.unwrap();
46 if !output.status.success() {
47 return Err(anyhow!("Error deleting subvolume: {:?}", output));
48 }
49 Ok(())
50}
51
52pub struct BtrfsSubvolumeShow {
54 pub generation: u64,
56}
57
58pub async fn btrfs_subvolume_show(path: &Path) -> Result<BtrfsSubvolumeShow, Error> {
60 lazy_static! {
61 static ref GENERATION: Regex = Regex::new(r"Generation:\s+(\d+)").unwrap();
62 }
63
64 let mut command = Command::new("btrfs");
65 command.arg("subvolume").arg("show").arg(path);
66 debug!("Getting BTRFS subvolume info: {command:?}");
67
68 let output = command.output().await?;
69
70 if !output.status.success() {
71 return Err(anyhow!("Error getting subvolume info: {:?}", output));
72 }
73
74 let stdout = String::from_utf8(output.stdout)?;
75 let generation: u64 = GENERATION
76 .captures(&stdout)
77 .unwrap()
78 .get(1)
79 .unwrap()
80 .as_str()
81 .parse()?;
82
83 Ok(BtrfsSubvolumeShow { generation })
84}
85
86pub async fn btrfs_send(path: &Path, parent: Option<&Path>) -> std::io::Result<Child> {
88 let mut command = Command::new("btrfs");
89 command.arg("send");
90
91 if let Some(parent) = parent {
92 command.arg("-p").arg(parent);
93 }
94
95 command.arg(path);
96 debug!("Sending BTRFS snapshot: {:?}", command);
97
98 command.stdout(Stdio::piped());
99 command.stderr(Stdio::piped());
100 command.spawn()
101}
102
103pub async fn btrfs_receive(path: &Path) -> std::io::Result<Child> {
105 let mut command = Command::new("btrfs");
106 command.arg("receive");
107 command.arg(path);
108 debug!("Receiving BTRFS snapshot: {:?}", command);
109
110 command.stdin(Stdio::piped());
111 command.spawn()
112}
113
114pub async fn mkfs_btrfs(path: &Path) -> Result<()> {
118 let mut command = Command::new("mkfs.btrfs");
119 command.arg(path);
120 debug!("Creating BTRFS filesystem: {:?}", command);
121 let output = command.output().await?;
122 if !output.status.success() {
123 Err(anyhow!("Error creating BTRFS filesystem: {:?}", output))
124 } else {
125 Ok(())
126 }
127}
128
129pub async fn umount(path: &Path) -> Result<()> {
131 let mut command = Command::new("umount");
132 command.arg(path);
133 debug!("Unmounting filesystem: {:?}", command);
134 let output = command.output().await?;
135 if !output.status.success() {
136 Err(anyhow!("Error unmounting filesystem: {:?}", output))
137 } else {
138 Ok(())
139 }
140}
141
142pub struct MountOptions {
144 pub block_device: PathBuf,
146 pub mount_target: PathBuf,
148}
149
150pub async fn mount_btrfs(options: MountOptions) -> Result<()> {
152 let mut command = Command::new("mount");
153 command
154 .arg("-t")
155 .arg("btrfs")
156 .arg(options.block_device)
157 .arg(options.mount_target);
158 debug!("Mounting BTRFS filesystem: {:?}", command);
159 let output = command.output().await?;
160 if !output.status.success() {
161 Err(anyhow!("Error mounting BTRFS filesystem: {:?}", output))
162 } else {
163 Ok(())
164 }
165}