feat: first-draft preview-capable zosstorage

- CLI: add topology selection (-t/--topology), preview flags (--show/--report), and removable policy override (--allow-removable) (src/cli/args.rs)
- Config: built-in sensible defaults; deterministic overlays for logging, fstab, removable, topology (src/config/loader.rs)
- Device: discovery via /proc + /sys with include/exclude regex and removable policy (src/device/discovery.rs)
- Idempotency: detection via blkid; safe emptiness checks (src/idempotency/mod.rs)
- Partition: topology-driven planning (Single, DualIndependent, BtrfsRaid1, SsdHddBcachefs) (src/partition/plan.rs)
- FS: planning + creation (mkfs.vfat, mkfs.btrfs, bcachefs format) and UUID capture via blkid (src/fs/plan.rs)
- Orchestrator: pre-flight with preview JSON (disks, partition_plan, filesystems_planned, mount scheme). Skips emptiness in preview; supports stdout+file (src/orchestrator/run.rs)
- Util/Logging/Types/Errors: process execution, tracing, shared types (src/util/mod.rs, src/logging/mod.rs, src/types.rs, src/errors.rs)
- Docs: add README with exhaustive usage and preview JSON shape (README.md)

Builds and unit tests pass: discovery, util, idempotency helpers, and fs parser tests.
This commit is contained in:
2025-09-29 11:37:07 +02:00
commit 507bc172c2
38 changed files with 6558 additions and 0 deletions

12
src/mount/mod.rs Normal file
View File

@@ -0,0 +1,12 @@
//! Mount module barrel.
//!
//! Re-exports the concrete ops implementation from ops.rs to avoid a large mod.rs.
//! See [src/mount/ops.rs](ops.rs) for details.
//
// REGION: API
// api: mount::ops::*
// REGION: API-END
pub mod ops;
pub use ops::*;

84
src/mount/ops.rs Normal file
View File

@@ -0,0 +1,84 @@
// REGION: API
// api: mount::MountPlan { entries: Vec<(String /* source */, String /* target */, String /* fstype */, String /* options */)> }
// api: mount::MountResult { source: String, target: String, fstype: String, options: String }
// api: mount::plan_mounts(fs_results: &[crate::fs::FsResult], cfg: &crate::config::types::Config) -> crate::Result<MountPlan>
// api: mount::apply_mounts(plan: &MountPlan) -> crate::Result<Vec<MountResult>>
// api: mount::maybe_write_fstab(mounts: &[MountResult], cfg: &crate::config::types::Config) -> crate::Result<()>
// REGION: API-END
//
// REGION: RESPONSIBILITIES
// - Translate filesystem identities to mount targets, defaulting to /var/cache/<UUID>.
// - Perform mounts using syscalls (nix) and create target directories as needed.
// - Optionally generate /etc/fstab entries in deterministic order.
// Non-goals: filesystem creation, device discovery, partitioning.
// REGION: RESPONSIBILITIES-END
//
// REGION: EXTENSION_POINTS
// ext: support custom mount scheme mapping beyond per-UUID.
// ext: add configurable mount options per filesystem kind via Config.
// REGION: EXTENSION_POINTS-END
//
// REGION: SAFETY
// safety: must ensure target directories exist and avoid overwriting unintended paths.
// safety: ensure options include sensible defaults (e.g., btrfs compress, ssd) when applicable.
// REGION: SAFETY-END
//
// REGION: ERROR_MAPPING
// errmap: syscall failures -> crate::Error::Mount with context.
// errmap: fstab write IO errors -> crate::Error::Mount with path details.
// REGION: ERROR_MAPPING-END
//
// REGION: TODO
// todo: implement option synthesis (e.g., compress=zstd:3 for btrfs) based on Config and device rotational hints.
// todo: implement deterministic fstab ordering and idempotent writes.
// REGION: TODO-END
//! Mount planning and application.
//!
//! Translates filesystem results into mount targets (default under /var/cache/<UUID>)
//! and applies mounts using syscalls (via nix) in later implementation.
//!
//! See [fn plan_mounts](ops.rs:1), [fn apply_mounts](ops.rs:1),
//! and [fn maybe_write_fstab](ops.rs:1).
#![allow(dead_code)]
use crate::{Result, types::Config, fs::FsResult};
/// Mount plan entries: (source, target, fstype, options)
#[derive(Debug, Clone)]
pub struct MountPlan {
/// Source device path, target directory, filesystem type, and mount options.
pub entries: Vec<(String, String, String, String)>,
}
/// Result of applying a single mount entry.
#[derive(Debug, Clone)]
pub struct MountResult {
/// Source device path (e.g., /dev/nvme0n1p3).
pub source: String,
/// Target directory (e.g., /var/cache/<UUID>).
pub target: String,
/// Filesystem type (e.g., "btrfs", "vfat").
pub fstype: String,
/// Options string (comma-separated).
pub options: String,
}
/// Build mount plan under /var/cache/<UUID> by default.
pub fn plan_mounts(fs_results: &[FsResult], _cfg: &Config) -> Result<MountPlan> {
let _ = fs_results;
// Placeholder: map filesystem UUIDs to per-UUID directories and assemble options.
todo!("create per-UUID directories and mount mapping based on config")
}
/// Apply mounts using syscalls (nix), ensuring directories exist.
pub fn apply_mounts(_plan: &MountPlan) -> Result<Vec<MountResult>> {
// Placeholder: perform mount syscalls and return results.
todo!("perform mount syscalls and return results")
}
/// Optionally generate /etc/fstab entries in deterministic order.
pub fn maybe_write_fstab(_mounts: &[MountResult], _cfg: &Config) -> Result<()> {
// Placeholder: write fstab when enabled in configuration.
todo!("when enabled, write fstab entries deterministically")
}