zinit 0.1.0

Process supervisor with dependency management
Documentation
//! Mount operations for MOS storage

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};

/// Mount points for MOS storage
pub struct MountPoints {
    /// Base mount for btrfs root (optional)
    pub mos_base: PathBuf,
    /// Boot partition mount (EFI only)
    pub boot: Option<PathBuf>,
    /// Subvolume mount points
    pub subvolumes: Vec<SubvolumeMountPoint>,
}

/// A subvolume mount point
pub struct SubvolumeMountPoint {
    /// Subvolume name
    pub name: String,
    /// Mount point path
    pub path: PathBuf,
}

impl Default for MountPoints {
    fn default() -> Self {
        Self::new()
    }
}

impl MountPoints {
    /// Create the default mount point configuration
    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(),
        }
    }
}

/// Mount all MOS storage
///
/// Mounts:
/// - Boot partition (EFI only) at /boot
/// - Data subvolumes at /var/cache/{system,etc,modules,vm-meta}
pub fn mount_all(boot_mode: BootMode) -> Result<MountPoints, StorageError> {
    let mount_points = MountPoints::new();

    // Mount boot partition (EFI only)
    if boot_mode.is_efi()
        && let Some(ref boot_path) = mount_points.boot
    {
        mount_boot(boot_path)?;
    }

    // Mount subvolumes
    mount_subvolumes(&mount_points)?;

    Ok(mount_points)
}

/// Mount the boot partition
fn mount_boot(mount_point: &Path) -> Result<(), StorageError> {
    log::info!("mounting boot partition at {}", mount_point.display());

    // Create mount point
    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),
    })?;

    // Mount by label
    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(())
}

/// Mount all subvolumes
fn mount_subvolumes(mount_points: &MountPoints) -> Result<(), StorageError> {
    for subvol in &mount_points.subvolumes {
        mount_subvolume(&subvol.name, &subvol.path)?;
    }
    Ok(())
}

/// Mount a single subvolume
fn mount_subvolume(name: &str, mount_point: &Path) -> Result<(), StorageError> {
    log::info!("mounting subvolume {} at {}", name, mount_point.display());

    // Create mount point
    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),
    })?;

    // Mount options
    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(())
}

/// Check if a path is mounted
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
}

/// Check if all MOS storage is mounted
pub fn all_mounted() -> bool {
    let mount_points = MountPoints::new();

    for subvol in &mount_points.subvolumes {
        if !is_mounted(&subvol.path) {
            return false;
        }
    }

    true
}

/// Unmount all MOS storage
pub fn unmount_all() -> Result<(), StorageError> {
    let mount_points = MountPoints::new();

    // Unmount subvolumes first
    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);
            }
        }
    }

    // Unmount boot
    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(())
}