use super::cmd;
use super::error::StorageError;
use std::fs;
use std::path::Path;
pub const SUBVOLUMES: &[&str] = &["system", "etc", "modules", "vm-meta"];
const TEMP_MOUNT: &str = "/tmp/mos-storage-init";
pub fn create_subvolumes(data_device: &Path) -> Result<(), StorageError> {
let dev_path = data_device.to_string_lossy();
log::info!("creating btrfs subvolumes on {}", dev_path);
fs::create_dir_all(TEMP_MOUNT).map_err(|e| StorageError::SubvolumeFailed {
path: TEMP_MOUNT.to_string(),
error: format!("failed to create temp mount: {}", e),
})?;
cmd::run("mount", &[&dev_path, TEMP_MOUNT]).map_err(|e| StorageError::MountFailed {
from: data_device.to_path_buf(),
target: TEMP_MOUNT.into(),
error: e.to_string(),
})?;
let result = create_subvolumes_inner();
let _ = cmd::run("umount", &[TEMP_MOUNT]);
let _ = fs::remove_dir(TEMP_MOUNT);
result
}
fn create_subvolumes_inner() -> Result<(), StorageError> {
for name in SUBVOLUMES {
let subvol_path = format!("{}/{}", TEMP_MOUNT, name);
log::info!("creating subvolume: {}", name);
cmd::run("btrfs", &["subvolume", "create", &subvol_path]).map_err(|e| {
StorageError::SubvolumeFailed {
path: name.to_string(),
error: e.to_string(),
}
})?;
}
Ok(())
}
pub fn list_subvolumes(mount_point: &Path) -> Result<Vec<String>, StorageError> {
let mount_path = mount_point.to_string_lossy();
let output = cmd::run_output("btrfs", &["subvolume", "list", &mount_path]).map_err(|e| {
StorageError::SubvolumeFailed {
path: mount_path.to_string(),
error: format!("failed to list subvolumes: {}", e),
}
})?;
let subvols: Vec<String> = output
.lines()
.filter_map(|line| {
line.split_whitespace().last().map(String::from)
})
.collect();
Ok(subvols)
}
pub fn all_subvolumes_exist(mount_point: &Path) -> Result<bool, StorageError> {
let existing = list_subvolumes(mount_point)?;
for required in SUBVOLUMES {
if !existing.iter().any(|s| s == *required) {
return Ok(false);
}
}
Ok(true)
}