1use crate::utils::bytes_to_gib;
2use crate::utils::exec;
3use easy_error::format_err;
4use lfs_core::{self, Stats};
5use log::debug;
6use proc_mounts::MountList;
7use std::error::Error;
8use std::fs::create_dir;
9use std::fs::File;
10use std::io::Read;
11use std::path::Path;
12use std::path::PathBuf;
13
14pub fn device_seems_formated(device_path: &String) -> Result<bool, Box<dyn Error>> {
15 debug!("does device {} seems formated ?", device_path);
16 let mut buffer = [0; 1_000_000];
18 let mut file = File::open(device_path)?;
19 let n = file.read(&mut buffer[..])?;
20 for byte in &buffer[..n] {
21 if *byte != 0 {
22 debug!("does device {} seems formated ? -> true", device_path);
23 return Ok(true);
24 }
25 }
26 debug!("does device {} seems formated ? -> false", device_path);
27 Ok(false)
28}
29
30pub fn format(device_path: &String) -> Result<(), Box<dyn Error>> {
31 exec("mkfs.btrfs", &[device_path])?;
32 Ok(())
33}
34
35pub fn is_folder(path: &String) -> bool {
36 PathBuf::from(path).is_dir()
37}
38
39pub fn create_folder(path: &String) -> Result<(), Box<dyn Error>> {
40 Ok(create_dir(path)?)
41}
42
43pub fn is_mounted(device_path: &String, mount_target: &String) -> Result<bool, Box<dyn Error>> {
44 let mount_list = MountList::new()?;
45 let source = Path::new(device_path.as_str());
46 let Some(mount_info) = mount_list.get_mount_by_source(source) else {
47 debug!("{} is not mounted", device_path);
48 return Ok(false);
49 };
50 let dest = PathBuf::from(mount_target.clone());
51 if mount_info.dest != dest {
52 return Err(Box::new(format_err!(
53 "{:?} seems to be mounted on {:?}, not in {}",
54 source,
55 mount_info.dest,
56 mount_target
57 )));
58 }
59 debug!(
60 "{:?} is mounted on {:?}, all good",
61 mount_info.source, mount_info.dest
62 );
63 Ok(true)
64}
65
66pub fn mount(device_path: &String, mount_target: &String) -> Result<(), Box<dyn Error>> {
67 exec("mount", &[device_path, mount_target])?;
68 Ok(())
69}
70
71pub fn umount(device_path: &String) -> Result<(), Box<dyn Error>> {
72 exec("umount", &[device_path])?;
73 Ok(())
74}
75
76fn get_stats(device_path: &String) -> Result<Option<Stats>, Box<dyn Error>> {
77 let mut read_options = lfs_core::ReadOptions::default();
78 read_options.remote_stats(false);
79 for mount in lfs_core::read_mounts(&read_options)? {
80 if mount.info.fs == *device_path {
81 let stats = mount.stats?;
82 return Ok(Some(stats));
83 }
84 }
85 Ok(None)
86}
87
88pub fn used_bytes(device_path: &String) -> Result<usize, Box<dyn Error>> {
89 debug!("used_bytes");
90 let Some(stats) = get_stats(device_path)? else {
91 return Err(Box::new(format_err!(
92 "used_bytes cannot get fs stats from {}",
93 device_path
94 )));
95 };
96 let used_bytes = stats.used() as usize;
97 debug!(
98 "used_bytes on {}: {}B ({}GiB)",
99 device_path,
100 used_bytes,
101 bytes_to_gib(used_bytes)
102 );
103 Ok(used_bytes)
104}
105
106pub fn size_bytes(device_path: &String) -> Result<usize, Box<dyn Error>> {
107 debug!("size_bytes");
108 let Some(stats) = get_stats(device_path)? else {
109 return Err(Box::new(format_err!(
110 "size_bytes cannot get fs stats from {}",
111 device_path
112 )));
113 };
114 let size_bytes = stats.size() as usize;
115 debug!(
116 "size_bytes on {}: {}B ({}GiB)",
117 device_path,
118 size_bytes,
119 bytes_to_gib(size_bytes)
120 );
121 Ok(size_bytes)
122}
123
124pub fn available_bytes(device_path: &String) -> Result<usize, Box<dyn Error>> {
125 debug!("available_bytes");
126 let Some(stats) = get_stats(device_path)? else {
127 return Err(Box::new(format_err!(
128 "available_bytes cannot get fs stats from {}",
129 device_path
130 )));
131 };
132 let available_bytes = stats.available() as usize;
133 debug!(
134 "available_bytes on {}: {}B ({}GiB)",
135 device_path,
136 available_bytes,
137 bytes_to_gib(available_bytes)
138 );
139 Ok(available_bytes)
140}
141
142pub fn used_perc(device_path: &String) -> Result<f32, Box<dyn Error>> {
143 debug!("available_perc");
144 let Some(stats) = get_stats(device_path)? else {
145 return Err(Box::new(format_err!(
146 "available_perc cannot get fs stats from {}",
147 device_path
148 )));
149 };
150 let available_perc = stats.used() as f32 / stats.size() as f32;
151 debug!("available_perc on {}: {}", device_path, available_perc);
152 Ok(available_perc)
153}
154
155pub fn extend_fs_max(mount_target: &String) -> Result<(), Box<dyn Error>> {
156 exec("btrfs", &["filesystem", "resize", "max", mount_target])?;
157 Ok(())
158}
159
160pub fn resize(mount_path: &str, new_size_bytes: usize) -> Result<(), Box<dyn Error>> {
161 let new_size = format!("{}", new_size_bytes);
162 exec(
163 "btrfs",
164 &["filesystem", "resize", new_size.as_str(), mount_path],
165 )?;
166 Ok(())
167}