#![forbid(unsafe_code)]
use std::path::PathBuf;
use crate::fsck::FsckOptions;
use crate::store::{Store, StoreConfig};
#[derive(Debug, Clone, clap::Args)]
pub struct StatusArgs {
#[arg(value_name = "STORE")]
pub store: PathBuf,
#[arg(long)]
pub json: bool,
}
pub fn run(args: &StatusArgs) -> Result<(), String> {
if let Err(m) = crate::fsck::ensure_unmounted(&args.store) {
if m.starts_with("no entropyfs store") {
return Err(m);
}
if args.json {
let j = crate::cli::json::StatusJson {
schema_version: 1,
state: "mounted".into(),
store: args.store.display().to_string(),
uuid: String::new(),
generation: 0,
format: crate::engine::FormatInfo {
format_major: 0,
format_minor: 0,
compat: 0,
ro_compat: 0,
incompat: 0,
io_backend: String::new(),
},
physical_capacity_bytes: 0,
physical_used_bytes: 0,
physical_free_bytes: 0,
logical_bytes: 0,
inode_count: 0,
snapshot_count: 0,
fsck: crate::cli::json::StatusFsck {
status: "unknown".into(),
errors: 0,
warnings: 0,
leaked_bytes: 0,
leaked_objects: 0,
},
physical: None,
dsfb: crate::cli::json::StatusDsfb {
tracked_chunks: 0,
steps: 0,
drift_events: 0,
slew_events: 0,
narrowed_searches: 0,
},
};
println!(
"{}",
serde_json::to_string_pretty(&j).unwrap_or_else(|_| "{}".into())
);
return Ok(());
}
println!("state: mounted (lock held)");
return Ok(());
}
let config = StoreConfig::default();
let store = Store::open(&args.store, &config).map_err(|e| e.to_string())?;
let capacity = store.physical_capacity();
let used = store.physical_used();
let logical = store.logical_bytes().unwrap_or(0);
let snapshots = store.list_snapshots().unwrap_or_default();
let inodes = store.all_inodes().unwrap_or_default();
let report =
crate::fsck::fsck(&args.store, &FsckOptions::default()).map_err(|e| e.to_string())?;
let phys = crate::store::physical::physical_report(&store).ok();
let dsfb = store.dsfb_stats();
let uuid_hex = crate::cli::mkfs::hex_encode(&store.current_root().uuid);
let bits = store.feature_bits();
if args.json {
let j = crate::cli::json::StatusJson {
schema_version: 1,
state: "ok".into(),
store: args.store.display().to_string(),
uuid: uuid_hex,
generation: store.generation(),
format: crate::engine::FormatInfo {
format_major: store.current_root().format_major,
format_minor: store.current_root().format_minor,
compat: bits.compat,
ro_compat: bits.ro_compat,
incompat: bits.incompat,
io_backend: store.config().io_backend.name().to_string(),
},
physical_capacity_bytes: capacity,
physical_used_bytes: used,
physical_free_bytes: capacity.saturating_sub(used),
logical_bytes: logical,
inode_count: inodes.len(),
snapshot_count: snapshots.len(),
fsck: crate::cli::json::StatusFsck {
status: if report.is_clean() { "clean" } else { "issues" }.into(),
errors: report.error_count(),
warnings: report.warning_count(),
leaked_bytes: report.leaked_bytes,
leaked_objects: report.leaked_objects,
},
physical: phys.map(|p| crate::engine::PhysicalMetrics {
live_bytes: p.live_bytes,
dead_indexed_bytes: p.dead_indexed_bytes,
index_hidden_bytes: p.index_hidden_bytes,
unindexed_bytes: p.unindexed_bytes,
torn_bytes: p.torn_bytes,
zero_padding_bytes: p.zero_padding_bytes,
format_overhead_bytes: p.format_overhead_bytes,
unexplained_bytes: p.unexplained(),
}),
dsfb: crate::cli::json::StatusDsfb {
tracked_chunks: dsfb.tracked_chunks,
steps: dsfb.steps,
drift_events: dsfb.drift_events,
slew_events: dsfb.slew_events,
narrowed_searches: dsfb.narrowed_searches,
},
};
println!(
"{}",
serde_json::to_string_pretty(&j).unwrap_or_else(|_| "{}".into())
);
return Ok(());
}
println!("store: {}", args.store.display());
println!("uuid: {uuid_hex}");
println!("generation: {}", store.generation());
println!(
"physical: {} bytes capacity, {} used ({} free)",
capacity,
used,
capacity.saturating_sub(used)
);
println!(
"logical: {} bytes across {} inodes",
logical,
inodes.len()
);
println!("snapshots: {}", snapshots.len());
println!(
"fsck: {} ({} errors, {} warnings)",
if report.is_clean() { "clean" } else { "ISSUES" },
report.error_count(),
report.warning_count()
);
println!(
"gc reclaimable: {} bytes ({} objects)",
report.leaked_bytes, report.leaked_objects
);
if let Some(phys) = phys {
println!(
"physical: {} B files = {} B live + {} B dead-indexed + {} B index-hidden + {} B unindexed + {} B overhead ({} unexplained)",
phys.file_bytes,
phys.live_bytes,
phys.dead_indexed_bytes,
phys.index_hidden_bytes,
phys.unindexed_bytes,
phys.format_overhead_bytes
.saturating_add(phys.torn_bytes)
.saturating_add(phys.zero_padding_bytes),
phys.unexplained()
);
}
println!(
"dsfb: {} chunks tracked, {} steps, {} drift, {} slew, {} narrowed",
dsfb.tracked_chunks,
dsfb.steps,
dsfb.drift_events,
dsfb.slew_events,
dsfb.narrowed_searches
);
Ok(())
}