#![forbid(unsafe_code)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StatusJson {
pub schema_version: u32,
pub state: String,
pub store: String,
pub uuid: String,
pub generation: u64,
pub format: crate::engine::FormatInfo,
pub physical_capacity_bytes: u64,
pub physical_used_bytes: u64,
pub physical_free_bytes: u64,
pub logical_bytes: u64,
pub inode_count: usize,
pub snapshot_count: usize,
pub fsck: StatusFsck,
pub physical: Option<crate::engine::PhysicalMetrics>,
pub dsfb: StatusDsfb,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StatusFsck {
pub status: String,
pub errors: usize,
pub warnings: usize,
pub leaked_bytes: u64,
pub leaked_objects: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StatusDsfb {
pub tracked_chunks: usize,
pub steps: u64,
pub drift_events: u64,
pub slew_events: u64,
pub narrowed_searches: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FsckJson {
pub schema_version: u32,
pub status: String,
pub superblock_slots_valid: u8,
pub segments_scanned: u64,
pub records_scanned: u64,
pub live_objects: u64,
pub leaked_objects: u64,
pub leaked_bytes: u64,
pub inodes_verified: u64,
pub extents_verified: u64,
pub chunk_descriptors_verified: u64,
pub repairs: Vec<String>,
pub findings: Vec<FsckFindingJson>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FsckFindingJson {
pub code: String,
pub severity: String,
pub category: String,
pub message: String,
}
pub fn finding_code(category: crate::fsck::Category) -> &'static str {
match category {
crate::fsck::Category::Superblock => "SUPERBLOCK",
crate::fsck::Category::Segment => "SEGMENT",
crate::fsck::Category::Record => "RECORD",
crate::fsck::Category::Root => "ROOT",
crate::fsck::Category::Inode => "INODE",
crate::fsck::Category::Directory => "DIRECTORY",
crate::fsck::Category::Extent => "EXTENT",
crate::fsck::Category::ChunkIndex => "CHUNK_INDEX",
crate::fsck::Category::Snapshot => "SNAPSHOT",
crate::fsck::Category::Reference => "REFERENCE",
crate::fsck::Category::Reachability => "REACHABILITY",
crate::fsck::Category::Graph => "GRAPH",
crate::fsck::Category::Repair => "REPAIR",
}
}
pub fn severity_str(s: crate::fsck::Severity) -> &'static str {
match s {
crate::fsck::Severity::Info => "info",
crate::fsck::Severity::Warning => "warning",
crate::fsck::Severity::Error => "error",
}
}
impl FsckJson {
pub fn from_report(report: &crate::fsck::FsckReport) -> Self {
let findings = report
.issues
.iter()
.map(|i| FsckFindingJson {
code: finding_code(i.category).to_string(),
severity: severity_str(i.severity).to_string(),
category: i.category.name().to_string(),
message: i.message.clone(),
})
.collect();
FsckJson {
schema_version: 1,
status: if report.is_clean() {
"clean"
} else {
"corrupt"
}
.into(),
superblock_slots_valid: report.superblock_slots_valid,
segments_scanned: report.segments_scanned,
records_scanned: report.records_scanned,
live_objects: report.live_objects,
leaked_objects: report.leaked_objects,
leaked_bytes: report.leaked_bytes,
inodes_verified: report.inodes_verified,
extents_verified: report.extents_verified,
chunk_descriptors_verified: report.chunk_descriptors_verified,
repairs: report.repaired.clone(),
findings,
}
}
pub fn to_json(&self) -> String {
serde_json::to_string_pretty(self).unwrap_or_else(|_| "{}".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fsck_json_roundtrip() {
let report = crate::fsck::FsckReport {
issues: vec![crate::fsck::FsckIssue::new(
crate::fsck::Severity::Warning,
crate::fsck::Category::Extent,
"example finding",
)],
..Default::default()
};
let j = FsckJson::from_report(&report);
assert_eq!(j.status, "clean"); assert_eq!(j.findings[0].code, "EXTENT");
assert_eq!(j.findings[0].severity, "warning");
let back: FsckJson = serde_json::from_str(&j.to_json()).unwrap();
assert_eq!(back, j);
}
#[test]
fn finding_codes_are_stable_uppercase() {
use crate::fsck::Category::*;
for c in [
Superblock,
Segment,
Record,
Root,
Inode,
Directory,
Extent,
ChunkIndex,
Snapshot,
Reference,
Reachability,
Graph,
Repair,
] {
let code = finding_code(c);
assert_eq!(code, code.to_uppercase());
assert!(!code.contains(' '));
}
}
}