use super::boot::BootMode;
use super::cmd;
use super::error::StorageError;
use super::format::{MOSBOOT_LABEL, MOSDATA_LABEL};
use super::subvolume::SUBVOLUMES;
use std::fs;
use std::path::{Path, PathBuf};
pub struct MountPoints {
pub mos_base: PathBuf,
pub boot: Option<PathBuf>,
pub subvolumes: Vec<SubvolumeMountPoint>,
}
pub struct SubvolumeMountPoint {
pub name: String,
pub path: PathBuf,
}
impl Default for MountPoints {
fn default() -> Self {
Self::new()
}
}
impl MountPoints {
pub fn new() -> Self {
Self {
mos_base: PathBuf::from("/var/mos"),
boot: Some(PathBuf::from("/boot")),
subvolumes: SUBVOLUMES
.iter()
.map(|name| SubvolumeMountPoint {
name: name.to_string(),
path: PathBuf::from(format!("/var/cache/{}", name)),
})
.collect(),
}
}
}
pub fn mount_all(boot_mode: BootMode) -> Result<MountPoints, StorageError> {
let mount_points = MountPoints::new();
if boot_mode.is_efi()
&& let Some(ref boot_path) = mount_points.boot
{
mount_boot(boot_path)?;
}
mount_subvolumes(&mount_points)?;
Ok(mount_points)
}
fn mount_boot(mount_point: &Path) -> Result<(), StorageError> {
log::info!("mounting boot partition at {}", mount_point.display());
fs::create_dir_all(mount_point).map_err(|e| StorageError::MountFailed {
from: PathBuf::from(format!("LABEL={}", MOSBOOT_LABEL)),
target: mount_point.to_path_buf(),
error: format!("failed to create mount point: {}", e),
})?;
let label_arg = format!("LABEL={}", MOSBOOT_LABEL);
let mount_path = mount_point.to_string_lossy();
cmd::run("mount", &[&label_arg, &mount_path]).map_err(|e| StorageError::MountFailed {
from: PathBuf::from(&label_arg),
target: mount_point.to_path_buf(),
error: e.to_string(),
})?;
Ok(())
}
fn mount_subvolumes(mount_points: &MountPoints) -> Result<(), StorageError> {
for subvol in &mount_points.subvolumes {
mount_subvolume(&subvol.name, &subvol.path)?;
}
Ok(())
}
fn mount_subvolume(name: &str, mount_point: &Path) -> Result<(), StorageError> {
log::info!("mounting subvolume {} at {}", name, mount_point.display());
fs::create_dir_all(mount_point).map_err(|e| StorageError::MountFailed {
from: PathBuf::from(name),
target: mount_point.to_path_buf(),
error: format!("failed to create mount point: {}", e),
})?;
let options = format!("noatime,space_cache=v2,subvol=/{}", name);
let label_arg = format!("LABEL={}", MOSDATA_LABEL);
let mount_path = mount_point.to_string_lossy();
cmd::run("mount", &["-o", &options, &label_arg, &mount_path]).map_err(|e| {
StorageError::MountFailed {
from: PathBuf::from(&label_arg),
target: mount_point.to_path_buf(),
error: e.to_string(),
}
})?;
Ok(())
}
pub fn is_mounted(path: &Path) -> bool {
if let Ok(mounts) = fs::read_to_string("/proc/mounts") {
let path_str = path.to_string_lossy();
for line in mounts.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 && parts[1] == path_str {
return true;
}
}
}
false
}
pub fn all_mounted() -> bool {
let mount_points = MountPoints::new();
for subvol in &mount_points.subvolumes {
if !is_mounted(&subvol.path) {
return false;
}
}
true
}
pub fn unmount_all() -> Result<(), StorageError> {
let mount_points = MountPoints::new();
for subvol in mount_points.subvolumes.iter().rev() {
if is_mounted(&subvol.path) {
let path_str = subvol.path.to_string_lossy();
if let Err(e) = cmd::run("umount", &[&path_str]) {
log::warn!("failed to unmount {}: {}", path_str, e);
}
}
}
if let Some(ref boot) = mount_points.boot
&& is_mounted(boot)
{
let path_str = boot.to_string_lossy();
if let Err(e) = cmd::run("umount", &[&path_str]) {
log::warn!("failed to unmount {}: {}", path_str, e);
}
}
Ok(())
}