blob: 5b50862990967c4b950d8b584fff90c53af14e3e [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{
log::*,
lsm_tree::types::ItemRef,
object_store::{
allocator::{AllocatorKey, AllocatorValue},
ObjectDescriptor,
},
},
std::ops::Range,
};
#[derive(Clone, Debug)]
pub enum FsckIssue {
/// Warnings don't prevent the filesystem from mounting and don't fail fsck, but they indicate a
/// consistency issue.
Warning(FsckWarning),
/// Errors prevent the filesystem from mounting, and will result in fsck failing, but will let
/// fsck continue to run to find more issues.
Error(FsckError),
/// Fatal errors are like Errors, but they're serious enough that fsck should be halted, as any
/// further results will probably be false positives.
Fatal(FsckFatal),
}
impl FsckIssue {
/// Translates an error to a human-readable string, intended for reporting errors to the user.
/// For debugging, std::fmt::Debug is preferred.
// TODO(fxbug.dev/95352): Localization
pub fn to_string(&self) -> String {
match self {
FsckIssue::Warning(w) => format!("WARNING: {}", w.to_string()),
FsckIssue::Error(e) => format!("ERROR: {}", e.to_string()),
FsckIssue::Fatal(f) => format!("FATAL: {}", f.to_string()),
}
}
pub fn is_error(&self) -> bool {
match self {
FsckIssue::Error(_) | FsckIssue::Fatal(_) => true,
FsckIssue::Warning(_) => false,
}
}
pub fn log(&self) {
match self {
FsckIssue::Warning(w) => w.log(),
FsckIssue::Error(e) => e.log(),
FsckIssue::Fatal(f) => f.log(),
}
}
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct Allocation {
range: Range<u64>,
value: AllocatorValue,
}
impl From<ItemRef<'_, AllocatorKey, AllocatorValue>> for Allocation {
fn from(item: ItemRef<'_, AllocatorKey, AllocatorValue>) -> Self {
Self { range: item.key.device_range.clone(), value: item.value.clone() }
}
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct Key(String);
impl<K: std::fmt::Debug, V> From<ItemRef<'_, K, V>> for Key {
fn from(item: ItemRef<'_, K, V>) -> Self {
Self(format!("{:?}", item.key))
}
}
impl<K: std::fmt::Debug> From<&K> for Key {
fn from(k: &K) -> Self {
Self(format!("{:?}", k))
}
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct Value(String);
impl<K, V: std::fmt::Debug> From<ItemRef<'_, K, V>> for Value {
fn from(item: ItemRef<'_, K, V>) -> Self {
Self(format!("{:?}", item.value))
}
}
// `From<V: std::fmt::Debug> for Value` creates a recursive definition since Value is Debug, so we
// have to go concrete here.
impl From<ObjectDescriptor> for Value {
fn from(d: ObjectDescriptor) -> Self {
Self(format!("{:?}", d))
}
}
impl<V: std::fmt::Debug> From<&V> for Value {
fn from(v: &V) -> Self {
Self(format!("{:?}", v))
}
}
#[derive(Clone, Debug)]
pub enum FsckWarning {
ExtentForMissingAttribute(u64, u64, u64),
ExtentForDirectory(u64, u64),
ExtentForNonexistentObject(u64, u64),
GraveyardRecordForAbsentObject(u64, u64),
InvalidObjectIdInStore(u64, Key, Value),
OrphanedAttribute(u64, u64, u64),
OrphanedObject(u64, u64),
}
impl FsckWarning {
fn to_string(&self) -> String {
match self {
FsckWarning::ExtentForMissingAttribute(store_id, object_id, attr_id) => {
format!(
"Found an extent in store {} for missing attribute {} on object {}",
store_id, attr_id, object_id
)
}
FsckWarning::ExtentForDirectory(store_id, object_id) => {
format!(
"Found an extent in store {} for a directory object {}",
store_id, object_id
)
}
FsckWarning::ExtentForNonexistentObject(store_id, object_id) => {
format!(
"Found an extent in store {} for a non-existent object {}",
store_id, object_id
)
}
FsckWarning::GraveyardRecordForAbsentObject(store_id, object_id) => {
format!(
"Graveyard contains an entry for object {} in store {}, but that object is \
absent",
store_id, object_id
)
}
FsckWarning::InvalidObjectIdInStore(store_id, key, value) => {
format!("Store {} has an invalid object ID ({:?}, {:?})", store_id, key, value)
}
FsckWarning::OrphanedAttribute(store_id, object_id, attribute_id) => {
format!(
"Attribute {} found for object {} which doesn't exist in store {}",
attribute_id, object_id, store_id
)
}
FsckWarning::OrphanedObject(store_id, object_id) => {
format!("Orphaned object {} was found in store {}", object_id, store_id)
}
}
}
fn log(&self) {
match self {
FsckWarning::ExtentForMissingAttribute(store_id, oid, attr_id) => {
warn!(store_id, oid, attr_id, "Found an extent for a missing attribute");
}
FsckWarning::ExtentForDirectory(store_id, oid) => {
warn!(store_id, oid, "Extent for a directory object");
}
FsckWarning::ExtentForNonexistentObject(store_id, oid) => {
warn!(store_id, oid, "Extent for missing object");
}
FsckWarning::GraveyardRecordForAbsentObject(store_id, oid) => {
warn!(store_id, oid, "Graveyard entry for missing object");
}
FsckWarning::InvalidObjectIdInStore(store_id, key, value) => {
warn!(store_id, ?key, ?value, "Invalid object ID");
}
FsckWarning::OrphanedAttribute(store_id, oid, attribute_id) => {
warn!(store_id, oid, attribute_id, "Attribute for missing object");
}
FsckWarning::OrphanedObject(store_id, oid) => {
warn!(oid, store_id, "Orphaned object");
}
}
}
}
#[derive(Clone, Debug)]
pub enum FsckError {
AllocatedBytesMismatch(Vec<(u64, i64)>, Vec<(u64, i64)>),
AllocatedSizeMismatch(u64, u64, u64, u64),
AllocationMismatch(Allocation, Allocation),
AttributeOnDirectory(u64, u64),
ConflictingTypeForLink(u64, u64, Value, Value),
ExtentExceedsLength(u64, u64, u64, u64, Value),
ExtraAllocations(Vec<Allocation>),
FileHasChildren(u64, u64),
UnexpectedJournalFileOffset(u64),
LinkCycle(u64, u64),
MalformedAllocation(Allocation),
MalformedExtent(u64, u64, Range<u64>, u64),
MalformedObjectRecord(u64, Key, Value),
MisalignedAllocation(Allocation),
MisalignedExtent(u64, u64, Range<u64>, u64),
MissingAllocation(Allocation),
MissingDataAttribute(u64, u64),
MissingObjectInfo(u64, u64),
MultipleLinksToDirectory(u64, u64),
ObjectCountMismatch(u64, u64, u64),
RefCountMismatch(u64, u64, u64),
RootObjectHasParent(u64, u64, u64),
SubDirCountMismatch(u64, u64, u64, u64),
TombstonedObjectHasRecords(u64, u64),
UnexpectedObjectInGraveyard(u64),
UnexpectedRecordInObjectStore(u64, Key, Value),
VolumeInChildStore(u64, u64),
}
impl FsckError {
fn to_string(&self) -> String {
match self {
FsckError::AllocationMismatch(expected, actual) => {
format!("Expected allocation {:?} but found allocation {:?}", expected, actual)
}
FsckError::AllocatedBytesMismatch(expected, actual) => {
format!(
"Expected allocated bytes for each owner to be {:?}, but found {:?}",
expected, actual
)
}
FsckError::AllocatedSizeMismatch(store_id, oid, expected, actual) => {
format!(
"Expected {} bytes allocated for object {} in store {}, but found {} bytes",
expected, oid, store_id, actual
)
}
FsckError::AttributeOnDirectory(store_id, object_id) => {
format!("Directory {} in store {} had attributes", object_id, store_id)
}
FsckError::ConflictingTypeForLink(store_id, object_id, expected, actual) => {
format!(
"Object {} in store {} is of type {:?} but has a link of type {:?}",
store_id, object_id, expected, actual
)
}
FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
format!(
"Extent {:?} exceeds length {} of attr {} on object {} in store {}",
extent, size, attr_id, oid, store_id
)
}
FsckError::ExtraAllocations(allocations) => {
format!("Unexpected allocations {:?}", allocations)
}
FsckError::FileHasChildren(store_id, object_id) => {
format!("Object {} in store {} has children", object_id, store_id)
}
FsckError::UnexpectedJournalFileOffset(object_id) => {
format!(
"SuperBlock journal_file_offsets contains unexpected object_id ({:?}).",
object_id
)
}
FsckError::LinkCycle(store_id, object_id) => {
format!("Detected cycle involving object {} in store {}", store_id, object_id)
}
FsckError::MalformedAllocation(allocations) => {
format!("Malformed allocation {:?}", allocations)
}
FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
format!(
"Extent {:?} (offset {}) for object {} in store {} is malformed",
extent, device_offset, oid, store_id
)
}
FsckError::MalformedObjectRecord(store_id, key, value) => {
format!(
"Object record in store {} has mismatched key {:?} and value {:?}",
store_id, key, value
)
}
FsckError::MisalignedAllocation(allocations) => {
format!("Misaligned allocation {:?}", allocations)
}
FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
format!(
"Extent {:?} (offset {}) for object {} in store {} is misaligned",
extent, device_offset, oid, store_id
)
}
FsckError::MissingAllocation(allocation) => {
format!("Expected but didn't find allocation {:?}", allocation)
}
FsckError::MissingDataAttribute(store_id, oid) => {
format!("File {} in store {} didn't have the default data attribute", store_id, oid)
}
FsckError::MissingObjectInfo(store_id, object_id) => {
format!("Object {} in store {} had no object record", store_id, object_id)
}
FsckError::MultipleLinksToDirectory(store_id, object_id) => {
format!("Directory {} in store {} has multiple links", store_id, object_id)
}
FsckError::ObjectCountMismatch(store_id, expected, actual) => {
format!("Store {} had {} objects, expected {}", store_id, actual, expected)
}
FsckError::RefCountMismatch(oid, expected, actual) => {
format!("Object {} had {} references, expected {}", oid, actual, expected)
}
FsckError::RootObjectHasParent(store_id, object_id, apparent_parent_id) => {
format!(
"Object {} is child of {} but is a root object of store {}",
object_id, apparent_parent_id, store_id
)
}
FsckError::SubDirCountMismatch(store_id, object_id, expected, actual) => {
format!(
"Directory {} in store {} should have {} sub dirs but had {}",
object_id, store_id, expected, actual
)
}
FsckError::TombstonedObjectHasRecords(store_id, object_id) => {
format!(
"Tombstoned object {} in store {} was referenced by other records",
store_id, object_id
)
}
FsckError::UnexpectedObjectInGraveyard(object_id) => {
format!("Found a non-file object {} in graveyard", object_id)
}
FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
format!("Unexpected record ({:?}, {:?}) in object store {}", key, value, store_id)
}
FsckError::VolumeInChildStore(store_id, object_id) => {
format!(
"Volume {} found in child store {} instead of root store",
object_id, store_id
)
}
}
}
fn log(&self) {
match self {
FsckError::AllocationMismatch(expected, actual) => {
error!(?expected, ?actual, "Unexpected allocation");
}
FsckError::AllocatedBytesMismatch(expected, actual) => {
error!(?expected, ?actual, "Unexpected allocated bytes");
}
FsckError::AllocatedSizeMismatch(store_id, oid, expected, actual) => {
error!(expected, oid, store_id, actual, "Unexpected allocated size");
}
FsckError::AttributeOnDirectory(store_id, oid) => {
error!(store_id, oid, "Attribute for directory");
}
FsckError::ConflictingTypeForLink(store_id, oid, expected, actual) => {
error!(store_id, oid, ?expected, ?actual, "Bad link");
}
FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
error!(store_id, oid, attr_id, size, ?extent, "Extent exceeds length");
}
FsckError::ExtraAllocations(allocations) => {
error!(?allocations, "Unexpected allocations");
}
FsckError::FileHasChildren(store_id, oid) => {
error!(store_id, oid, "File has children");
}
FsckError::UnexpectedJournalFileOffset(object_id) => {
error!(
oid = object_id,
"SuperBlock journal_file_offsets contains unexpected object-id"
);
}
FsckError::LinkCycle(store_id, oid) => {
error!(store_id, oid, "Link cycle");
}
FsckError::MalformedAllocation(allocations) => {
error!(?allocations, "Malformed allocations");
}
FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
error!(store_id, oid, ?extent, device_offset, "Malformed extent");
}
FsckError::MalformedObjectRecord(store_id, key, value) => {
error!(store_id, ?key, ?value, "Mismatched key and value");
}
FsckError::MisalignedAllocation(allocations) => {
error!(?allocations, "Misaligned allocation");
}
FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
error!(store_id, oid, ?extent, device_offset, "Misaligned extent");
}
FsckError::MissingAllocation(allocation) => {
error!(?allocation, "Missing allocation");
}
FsckError::MissingDataAttribute(store_id, oid) => {
error!(store_id, oid, "Missing default attribute");
}
FsckError::MissingObjectInfo(store_id, oid) => {
error!(store_id, oid, "Missing object record");
}
FsckError::MultipleLinksToDirectory(store_id, oid) => {
error!(store_id, oid, "Directory with multiple links");
}
FsckError::ObjectCountMismatch(store_id, expected, actual) => {
error!(store_id, expected, actual, "Object count mismatch");
}
FsckError::RefCountMismatch(oid, expected, actual) => {
error!(oid, expected, actual, "Reference count mistmatch");
}
FsckError::RootObjectHasParent(store_id, oid, apparent_parent_id) => {
error!(store_id, oid, apparent_parent_id, "Root object is a child");
}
FsckError::SubDirCountMismatch(store_id, oid, expected, actual) => {
error!(store_id, oid, expected, actual, "Sub-dir count mismatch");
}
FsckError::TombstonedObjectHasRecords(store_id, oid) => {
error!(store_id, oid, "Tombstoned object with references");
}
FsckError::UnexpectedObjectInGraveyard(oid) => {
error!(oid, "Unexpected object in graveyard");
}
FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
error!(store_id, ?key, ?value, "Unexpected record");
}
FsckError::VolumeInChildStore(store_id, oid) => {
error!(store_id, oid, "Volume in child store");
}
}
}
}
#[derive(Clone, Debug)]
pub enum FsckFatal {
MalformedGraveyard,
MalformedLayerFile(u64, u64),
MalformedStore(u64),
MisOrderedLayerFile(u64, u64),
MisOrderedObjectStore(u64),
MissingLayerFile(u64, u64),
MissingStoreInfo(u64),
OverlappingKeysInLayerFile(u64, u64, Key, Key),
}
impl FsckFatal {
fn to_string(&self) -> String {
match self {
FsckFatal::MalformedGraveyard => {
"Graveyard is malformed; root store is inconsistent".to_string()
}
FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
format!("Layer file {} in object store {} is malformed", layer_file_id, store_id)
}
FsckFatal::MalformedStore(id) => {
format!("Object store {} is malformed; root store is inconsistent", id)
}
FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
format!(
"Layer file {} for store/allocator {} contains out-of-order records",
layer_file_id, store_id
)
}
FsckFatal::MisOrderedObjectStore(store_id) => {
format!("Store/allocator {} contains out-of-order or duplicate records", store_id)
}
FsckFatal::MissingLayerFile(store_id, layer_file_id) => {
format!(
"Object store {} requires layer file {} which is missing",
store_id, layer_file_id
)
}
FsckFatal::MissingStoreInfo(id) => {
format!("Object store {} has no store info object", id)
}
FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
format!(
"Layer file {} for store/allocator {} contains overlapping keys {:?} and {:?}",
store_id, layer_file_id, key1, key2
)
}
}
}
fn log(&self) {
match self {
FsckFatal::MalformedGraveyard => {
error!("Graveyard is malformed; root store is inconsistent");
}
FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
error!(store_id, layer_file_id, "Layer file malformed");
}
FsckFatal::MalformedStore(id) => {
error!(id, "Malformed store; root store is inconsistent");
}
FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
// This can be for stores or the allocator.
error!(oid = store_id, layer_file_id, "Layer file contains out-of-oder records");
}
FsckFatal::MisOrderedObjectStore(store_id) => {
// This can be for stores or the allocator.
error!(
oid = store_id,
"Store/allocator contains out-of-order or duplicate records"
);
}
FsckFatal::MissingLayerFile(store_id, layer_file_id) => {
// This can be for stores or the allocator.
error!(oid = store_id, layer_file_id, "Missing layer file");
}
FsckFatal::MissingStoreInfo(id) => {
error!(id, "Missing store info");
}
FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
// This can be for stores or the allocator.
error!(oid = store_id, layer_file_id, ?key1, ?key2, "Overlapping keys");
}
}
}
}