fxfs/fsck/
errors.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::log::*;
6use crate::lsm_tree::types::ItemRef;
7use crate::object_store::ObjectDescriptor;
8use crate::object_store::allocator::{AllocatorKey, AllocatorValue};
9use fxfs_crypto::WrappingKeyId;
10use std::ops::Range;
11
12#[derive(Clone, Debug, PartialEq)]
13pub enum FsckIssue {
14    /// Warnings don't prevent the filesystem from mounting and don't fail fsck, but they indicate a
15    /// consistency issue.
16    Warning(FsckWarning),
17    /// Errors prevent the filesystem from mounting, and will result in fsck failing, but will let
18    /// fsck continue to run to find more issues.
19    Error(FsckError),
20    /// Fatal errors are like Errors, but they're serious enough that fsck should be halted, as any
21    /// further results will probably be false positives.
22    Fatal(FsckFatal),
23}
24
25impl FsckIssue {
26    /// Translates an error to a human-readable string, intended for reporting errors to the user.
27    /// For debugging, std::fmt::Debug is preferred.
28    // TODO(https://fxbug.dev/42177349): Localization
29    pub fn to_string(&self) -> String {
30        match self {
31            FsckIssue::Warning(w) => format!("WARNING: {}", w.to_string()),
32            FsckIssue::Error(e) => format!("ERROR: {}", e.to_string()),
33            FsckIssue::Fatal(f) => format!("FATAL: {}", f.to_string()),
34        }
35    }
36    pub fn is_error(&self) -> bool {
37        match self {
38            FsckIssue::Error(_) | FsckIssue::Fatal(_) => true,
39            FsckIssue::Warning(_) => false,
40        }
41    }
42    pub fn log(&self) {
43        match self {
44            FsckIssue::Warning(w) => w.log(),
45            FsckIssue::Error(e) => e.log(),
46            FsckIssue::Fatal(f) => f.log(),
47        }
48    }
49}
50
51#[derive(Clone, Debug, PartialEq)]
52#[allow(dead_code)]
53pub struct Allocation {
54    range: Range<u64>,
55    value: AllocatorValue,
56}
57
58impl From<ItemRef<'_, AllocatorKey, AllocatorValue>> for Allocation {
59    fn from(item: ItemRef<'_, AllocatorKey, AllocatorValue>) -> Self {
60        Self { range: item.key.device_range.clone(), value: item.value.clone() }
61    }
62}
63
64#[derive(Clone, Debug, PartialEq)]
65#[allow(dead_code)]
66pub struct Key(String);
67
68impl<K: std::fmt::Debug, V> From<ItemRef<'_, K, V>> for Key {
69    fn from(item: ItemRef<'_, K, V>) -> Self {
70        Self(format!("{:?}", item.key))
71    }
72}
73
74impl<K: std::fmt::Debug> From<&K> for Key {
75    fn from(k: &K) -> Self {
76        Self(format!("{:?}", k))
77    }
78}
79
80#[derive(Clone, Debug, PartialEq)]
81#[allow(dead_code)]
82pub struct Value(String);
83
84impl<K, V: std::fmt::Debug> From<ItemRef<'_, K, V>> for Value {
85    fn from(item: ItemRef<'_, K, V>) -> Self {
86        Self(format!("{:?}", item.value))
87    }
88}
89
90// `From<V: std::fmt::Debug> for Value` creates a recursive definition since Value is Debug, so we
91// have to go concrete here.
92impl From<ObjectDescriptor> for Value {
93    fn from(d: ObjectDescriptor) -> Self {
94        Self(format!("{:?}", d))
95    }
96}
97
98impl<V: std::fmt::Debug> From<&V> for Value {
99    fn from(v: &V) -> Self {
100        Self(format!("{:?}", v))
101    }
102}
103
104#[derive(Clone, Debug, PartialEq)]
105pub enum FsckWarning {
106    ExtentForMissingAttribute(u64, u64, u64),
107    ExtentForNonexistentObject(u64, u64),
108    GraveyardRecordForAbsentObject(u64, u64),
109    InvalidObjectIdInStore(u64, Key, Value),
110    LimitForNonExistentStore(u64, u64),
111    OrphanedAttribute(u64, u64, u64),
112    OrphanedObject(u64, u64),
113    OrphanedKeys(u64, u64),
114    OrphanedExtendedAttribute(u64, u64, u64),
115    OrphanedExtendedAttributeRecord(u64, u64),
116    ProjectUsageInconsistent(u64, u64, (i64, i64), (i64, i64)),
117}
118
119impl FsckWarning {
120    fn to_string(&self) -> String {
121        match self {
122            FsckWarning::ExtentForMissingAttribute(store_id, object_id, attr_id) => {
123                format!(
124                    "Found an extent in store {} for missing attribute {} on object {}",
125                    store_id, attr_id, object_id
126                )
127            }
128            FsckWarning::ExtentForNonexistentObject(store_id, object_id) => {
129                format!(
130                    "Found an extent in store {} for a non-existent object {}",
131                    store_id, object_id
132                )
133            }
134            FsckWarning::GraveyardRecordForAbsentObject(store_id, object_id) => {
135                format!(
136                    "Graveyard contains an entry for object {} in store {}, but that object is \
137                    absent",
138                    store_id, object_id
139                )
140            }
141            FsckWarning::InvalidObjectIdInStore(store_id, key, value) => {
142                format!("Store {} has an invalid object ID ({:?}, {:?})", store_id, key, value)
143            }
144            FsckWarning::LimitForNonExistentStore(store_id, limit) => {
145                format!("Bytes limit of {} found for nonexistent store id {}", limit, store_id)
146            }
147            FsckWarning::OrphanedAttribute(store_id, object_id, attribute_id) => {
148                format!(
149                    "Attribute {} found for object {} which doesn't exist in store {}",
150                    attribute_id, object_id, store_id
151                )
152            }
153            FsckWarning::OrphanedObject(store_id, object_id) => {
154                format!("Orphaned object {} was found in store {}", object_id, store_id)
155            }
156            FsckWarning::OrphanedKeys(store_id, object_id) => {
157                format!("Orphaned keys for object {} were found in store {}", object_id, store_id)
158            }
159            FsckWarning::OrphanedExtendedAttribute(store_id, object_id, attribute_id) => {
160                format!(
161                    "Orphaned extended attribute for object {} was found in store {} with \
162                    attribute id {}",
163                    object_id, store_id, attribute_id,
164                )
165            }
166            FsckWarning::OrphanedExtendedAttributeRecord(store_id, object_id) => {
167                format!(
168                    "Orphaned extended attribute record for object {} was found in store {}",
169                    object_id, store_id
170                )
171            }
172            FsckWarning::ProjectUsageInconsistent(store_id, project_id, stored, used) => {
173                format!(
174                    "Project id {} in store {} expected usage ({}, {}) found ({}, {})",
175                    project_id, store_id, stored.0, stored.1, used.0, used.1
176                )
177            }
178        }
179    }
180
181    fn log(&self) {
182        match self {
183            FsckWarning::ExtentForMissingAttribute(store_id, oid, attr_id) => {
184                warn!(store_id, oid, attr_id; "Found an extent for a missing attribute");
185            }
186            FsckWarning::ExtentForNonexistentObject(store_id, oid) => {
187                warn!(store_id, oid; "Extent for missing object");
188            }
189            FsckWarning::GraveyardRecordForAbsentObject(store_id, oid) => {
190                warn!(store_id, oid; "Graveyard entry for missing object");
191            }
192            FsckWarning::InvalidObjectIdInStore(store_id, key, value) => {
193                warn!(store_id, key:?, value:?; "Invalid object ID");
194            }
195            FsckWarning::LimitForNonExistentStore(store_id, limit) => {
196                warn!(store_id, limit; "Found limit for non-existent owner store.");
197            }
198            FsckWarning::OrphanedAttribute(store_id, oid, attribute_id) => {
199                warn!(store_id, oid, attribute_id; "Attribute for missing object");
200            }
201            FsckWarning::OrphanedObject(store_id, oid) => {
202                warn!(oid, store_id; "Orphaned object");
203            }
204            FsckWarning::OrphanedKeys(store_id, oid) => {
205                warn!(oid, store_id; "Orphaned keys");
206            }
207            FsckWarning::OrphanedExtendedAttribute(store_id, oid, attribute_id) => {
208                warn!(oid, store_id, attribute_id; "Orphaned extended attribute");
209            }
210            FsckWarning::OrphanedExtendedAttributeRecord(store_id, oid) => {
211                warn!(oid, store_id; "Orphaned extended attribute record");
212            }
213            FsckWarning::ProjectUsageInconsistent(store_id, project_id, stored, used) => {
214                warn!(project_id, store_id, stored:?, used:?; "Project Inconsistent");
215            }
216        }
217    }
218}
219
220#[derive(Clone, Debug, PartialEq)]
221pub enum FsckError {
222    AllocatedBytesMismatch(Vec<(u64, u64)>, Vec<(u64, u64)>),
223    AllocatedSizeMismatch(u64, u64, u64, u64),
224    AllocationForNonexistentOwner(Allocation),
225    AllocationMismatch(Allocation, Allocation),
226    BadCasefoldHash(u64, u64, u64),
227    BadGraveyardValue(u64, u64),
228    BadLastObjectId(u64, u64),
229    CasefoldInconsistency(u64, u64, u64),
230    ChildEncryptedWithDifferentWrappingKeyThanParent(u64, u64, u64, WrappingKeyId, WrappingKeyId),
231    ConflictingTypeForLink(u64, u64, Value, Value),
232    DuplicateKey(u64, u64, u64),
233    EncryptedChildDirectoryNoWrappingKey(u64, u64),
234    EncryptedDirectoryHasUnencryptedChild(u64, u64, u64),
235    ExtentExceedsLength(u64, u64, u64, u64, Value),
236    ExtraAllocations(Vec<Allocation>),
237    IncorrectMerkleTreeSize(u64, u64, u64, u64),
238    LinkCycle(u64, u64),
239    MalformedAllocation(Allocation),
240    MalformedExtent(u64, u64, Range<u64>, u64),
241    MalformedObjectRecord(u64, Key, Value),
242    MisalignedAllocation(Allocation),
243    MisalignedExtent(u64, u64, Range<u64>, u64),
244    MissingAllocation(Allocation),
245    MissingAttributeForExtendedAttribute(u64, u64, u64),
246    MissingDataAttribute(u64, u64),
247    MissingEncryptionKeys(u64, u64),
248    MissingKey(u64, u64, u64),
249    MissingObjectInfo(u64, u64),
250    MissingOverwriteExtents(u64, u64, u64),
251    MultipleLinksToDirectory(u64, u64),
252    NextObjectIdInUse(u64, u64),
253    NonFileMarkedAsVerified(u64, u64),
254    NonRootProjectIdMetadata(u64, u64, u64),
255    ObjectCountMismatch(u64, u64, u64),
256    ObjectHasChildren(u64, u64),
257    OverwriteExtentFlagUnset(u64, u64, u64),
258    ProjectOnGraveyard(u64, u64, u64),
259    ProjectUsedWithNoUsageTracking(u64, u64, u64),
260    RefCountMismatch(u64, u64, u64),
261    RootObjectHasParent(u64, u64, u64),
262    SubDirCountMismatch(u64, u64, u64, u64),
263    TombstonedAttributeDoesNotExist(u64, u64, u64),
264    TombstonedObjectHasRecords(u64, u64),
265    TrimValueForGraveyardAttributeEntry(u64, u64, u64),
266    UnencryptedDirectoryHasEncryptedChild(u64, u64, u64),
267    UnexpectedJournalFileOffset(u64),
268    UnexpectedObjectInGraveyard(u64),
269    UnexpectedRecordInObjectStore(u64, Key, Value),
270    VerifiedFileDoesNotHaveAMerkleAttribute(u64, u64),
271    VolumeInChildStore(u64, u64),
272    ZombieDir(u64, u64, u64),
273    ZombieFile(u64, u64, Vec<u64>),
274    ZombieSymlink(u64, u64, Vec<u64>),
275}
276
277impl FsckError {
278    fn to_string(&self) -> String {
279        match self {
280            FsckError::AllocatedBytesMismatch(observed, stored) => {
281                format!(
282                    "Per-owner allocated bytes was {:?}, but sum of allocations gave {:?}",
283                    stored, observed
284                )
285            }
286            FsckError::AllocatedSizeMismatch(store_id, oid, observed, stored) => {
287                format!(
288                    "Expected {} bytes allocated for object {} in store {}, but found {} bytes",
289                    stored, oid, store_id, observed
290                )
291            }
292            FsckError::AllocationForNonexistentOwner(alloc) => {
293                format!("Allocation {:?} for non-existent owner", alloc)
294            }
295            FsckError::AllocationMismatch(observed, stored) => {
296                format!("Observed allocation {:?} but allocator has {:?}", observed, stored)
297            }
298            FsckError::BadCasefoldHash(store_id, parent_id, child_id) => {
299                format!(
300                    "Bad casefold hash code for store {store_id}, directory {parent_id}, child \
301                     {child_id}",
302                )
303            }
304            FsckError::BadLastObjectId(highest, last_object_id) => {
305                format!("Last object ID {last_object_id} is less than highest found {highest}")
306            }
307            FsckError::CasefoldInconsistency(store_id, parent_id, child_id) => {
308                format!(
309                    "CasefoldChild inconsistent for store {}, directory {}, child {}",
310                    store_id, parent_id, child_id
311                )
312            }
313            FsckError::ConflictingTypeForLink(store_id, object_id, expected, actual) => {
314                format!(
315                    "Object {} in store {} is of type {:?} but has a link of type {:?}",
316                    store_id, object_id, expected, actual
317                )
318            }
319            FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
320                format!(
321                    "Extent {:?} exceeds length {} of attr {} on object {} in store {}",
322                    extent, size, attr_id, oid, store_id
323                )
324            }
325            FsckError::ExtraAllocations(allocations) => {
326                format!("Unexpected allocations {:?}", allocations)
327            }
328            FsckError::ObjectHasChildren(store_id, object_id) => {
329                format!("Object {} in store {} has unexpected children", object_id, store_id)
330            }
331            FsckError::UnexpectedJournalFileOffset(object_id) => {
332                format!(
333                    "SuperBlock journal_file_offsets contains unexpected object_id ({:?}).",
334                    object_id
335                )
336            }
337            FsckError::LinkCycle(store_id, object_id) => {
338                format!("Detected cycle involving object {} in store {}", store_id, object_id)
339            }
340            FsckError::MalformedAllocation(allocations) => {
341                format!("Malformed allocation {:?}", allocations)
342            }
343            FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
344                format!(
345                    "Extent {:?} (offset {}) for object {} in store {} is malformed",
346                    extent, device_offset, oid, store_id
347                )
348            }
349            FsckError::MalformedObjectRecord(store_id, key, value) => {
350                format!(
351                    "Object record in store {} has mismatched key {:?} and value {:?}",
352                    store_id, key, value
353                )
354            }
355            FsckError::MisalignedAllocation(allocations) => {
356                format!("Misaligned allocation {:?}", allocations)
357            }
358            FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
359                format!(
360                    "Extent {:?} (offset {}) for object {} in store {} is misaligned",
361                    extent, device_offset, oid, store_id
362                )
363            }
364            FsckError::MissingAllocation(allocation) => {
365                format!("Observed {:?} but didn't find record in allocator", allocation)
366            }
367            FsckError::MissingAttributeForExtendedAttribute(store_id, oid, attribute_id) => {
368                format!(
369                    "Object {} in store {} has an extended attribute stored in a nonexistent \
370                    attribute {}",
371                    store_id, oid, attribute_id
372                )
373            }
374            FsckError::MissingDataAttribute(store_id, oid) => {
375                format!("File {} in store {} didn't have the default data attribute", store_id, oid)
376            }
377            FsckError::MissingObjectInfo(store_id, object_id) => {
378                format!("Object {} in store {} had no object record", store_id, object_id)
379            }
380            FsckError::MultipleLinksToDirectory(store_id, object_id) => {
381                format!("Directory {} in store {} has multiple links", store_id, object_id)
382            }
383            FsckError::NonRootProjectIdMetadata(store_id, object_id, project_id) => {
384                format!(
385                    "Project Id {} metadata in store {} attached to object {}",
386                    project_id, store_id, object_id
387                )
388            }
389            FsckError::ObjectCountMismatch(store_id, observed, stored) => {
390                format!("Store {} had {} objects, expected {}", store_id, observed, stored)
391            }
392            FsckError::ProjectOnGraveyard(store_id, project_id, object_id) => {
393                format!(
394                    "Store {} had graveyard object {} with project id {}",
395                    store_id, object_id, project_id
396                )
397            }
398            FsckError::ProjectUsedWithNoUsageTracking(store_id, project_id, node_id) => {
399                format!(
400                    "Store {} had node {} with project ids {} but no usage tracking metadata",
401                    store_id, node_id, project_id
402                )
403            }
404            FsckError::RefCountMismatch(oid, observed, stored) => {
405                format!("Object {} had {} references, expected {}", oid, observed, stored)
406            }
407            FsckError::RootObjectHasParent(store_id, object_id, apparent_parent_id) => {
408                format!(
409                    "Object {} is child of {} but is a root object of store {}",
410                    object_id, apparent_parent_id, store_id
411                )
412            }
413            FsckError::SubDirCountMismatch(store_id, object_id, observed, stored) => {
414                format!(
415                    "Directory {} in store {} should have {} sub dirs but had {}",
416                    object_id, store_id, stored, observed
417                )
418            }
419            FsckError::TombstonedObjectHasRecords(store_id, object_id) => {
420                format!(
421                    "Tombstoned object {} in store {} was referenced by other records",
422                    store_id, object_id
423                )
424            }
425            FsckError::UnexpectedObjectInGraveyard(object_id) => {
426                format!("Found a non-file object {} in graveyard", object_id)
427            }
428            FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
429                format!("Unexpected record ({:?}, {:?}) in object store {}", key, value, store_id)
430            }
431            FsckError::VolumeInChildStore(store_id, object_id) => {
432                format!(
433                    "Volume {} found in child store {} instead of root store",
434                    object_id, store_id
435                )
436            }
437            FsckError::BadGraveyardValue(store_id, object_id) => {
438                format!("Bad graveyard value with key <{}, {}>", store_id, object_id)
439            }
440            FsckError::MissingEncryptionKeys(store_id, object_id) => {
441                format!("Missing encryption keys for <{}, {}>", store_id, object_id)
442            }
443            FsckError::MissingKey(store_id, object_id, key_id) => {
444                format!("Missing encryption key for <{}, {}, {}>", store_id, object_id, key_id)
445            }
446            FsckError::EncryptedChildDirectoryNoWrappingKey(store_id, object_id) => {
447                format!(
448                    "Encrypted directory {} in store {} does not have a wrapping key id set",
449                    object_id, store_id
450                )
451            }
452            FsckError::EncryptedDirectoryHasUnencryptedChild(store_id, parent_oid, child_oid) => {
453                format!(
454                    "Encrypted parent directory {} in store {} has unencrypted child {}",
455                    parent_oid, store_id, child_oid
456                )
457            }
458            FsckError::UnencryptedDirectoryHasEncryptedChild(store_id, parent_oid, child_oid) => {
459                format!(
460                    "Unencrypted parent directory {} in store {} has encrypted child {}",
461                    parent_oid, store_id, child_oid
462                )
463            }
464            FsckError::ChildEncryptedWithDifferentWrappingKeyThanParent(
465                store_id,
466                parent_id,
467                child_id,
468                parent_wrapping_key_id,
469                child_wrapping_key_id,
470            ) => {
471                format!(
472                    "Parent directory {} in store {} encrypted with {:?}, child {} encrypted with \
473                    {:?}",
474                    parent_id, store_id, parent_wrapping_key_id, child_id, child_wrapping_key_id,
475                )
476            }
477            FsckError::DuplicateKey(store_id, object_id, key_id) => {
478                format!("Duplicate key for <{}, {}, {}>", store_id, object_id, key_id)
479            }
480            FsckError::ZombieFile(store_id, object_id, parent_object_ids) => {
481                format!(
482                    "File {object_id} in store {store_id} is in graveyard but still has links \
483                     from {parent_object_ids:?}",
484                )
485            }
486            FsckError::ZombieDir(store_id, object_id, parent_object_id) => {
487                format!(
488                    "Directory {object_id} in store {store_id} is in graveyard but still has \
489                     a link from {parent_object_id}",
490                )
491            }
492            FsckError::ZombieSymlink(store_id, object_id, parent_object_ids) => {
493                format!(
494                    "Symlink {object_id} in store {store_id} is in graveyard but still has \
495                     links from {parent_object_ids:?}",
496                )
497            }
498            FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(store_id, object_id) => {
499                format!(
500                    "Object {} in store {} is marked as fsverity-enabled but is missing a \
501                        merkle attribute",
502                    store_id, object_id
503                )
504            }
505            FsckError::NonFileMarkedAsVerified(store_id, object_id) => {
506                format!(
507                    "Object {} in store {} is marked as verified but is not a file",
508                    store_id, object_id
509                )
510            }
511            FsckError::IncorrectMerkleTreeSize(store_id, object_id, expected_size, actual_size) => {
512                format!(
513                    "Object {} in store {} has merkle tree of size {} expected {}",
514                    object_id, store_id, actual_size, expected_size
515                )
516            }
517            FsckError::TombstonedAttributeDoesNotExist(store_id, object_id, attribute_id) => {
518                format!(
519                    "Object {} in store {} has an attribute {} that is tombstoned but does not \
520                     exist.",
521                    object_id, store_id, attribute_id
522                )
523            }
524            FsckError::TrimValueForGraveyardAttributeEntry(store_id, object_id, attribute_id) => {
525                format!(
526                    "Object {} in store {} has a GraveyardAttributeEntry for attribute {} that has \
527                     ObjectValue::Trim",
528                    object_id, store_id, attribute_id,
529                )
530            }
531            FsckError::MissingOverwriteExtents(store_id, object_id, attribute_id) => {
532                format!(
533                    "Object {} in store {} has an attribute {} that indicated it had overwrite \
534                     extents but none were found",
535                    object_id, store_id, attribute_id,
536                )
537            }
538            FsckError::OverwriteExtentFlagUnset(store_id, object_id, attribute_id) => {
539                format!(
540                    "Object {} in store {} has an attribute {} with overwrite extents but the \
541                     metadata indicated it would not",
542                    object_id, store_id, attribute_id,
543                )
544            }
545            FsckError::NextObjectIdInUse(store_id, next_object_id) => {
546                format!("Next object ID {store_id} will use ({next_object_id}) is already in use",)
547            }
548        }
549    }
550
551    fn log(&self) {
552        match self {
553            FsckError::AllocatedBytesMismatch(observed, stored) => {
554                error!(observed:?, stored:?; "Unexpected allocated bytes");
555            }
556            FsckError::AllocatedSizeMismatch(store_id, oid, observed, stored) => {
557                error!(observed, oid, store_id, stored; "Unexpected allocated size");
558            }
559            FsckError::AllocationForNonexistentOwner(alloc) => {
560                error!(alloc:?; "Allocation for non-existent owner")
561            }
562            FsckError::AllocationMismatch(observed, stored) => {
563                error!(observed:?, stored:?; "Unexpected allocation");
564            }
565            FsckError::BadCasefoldHash(store_id, parent_id, child_id) => {
566                error!(store_id, parent_id, child_id; "Bad casefold hash code");
567            }
568            FsckError::BadLastObjectId(highest, last_object_id) => {
569                error!(highest, last_object_id; "Last object ID is less than highest found");
570            }
571            FsckError::CasefoldInconsistency(store_id, parent_id, child_id) => {
572                error!(store_id:?, parent_id:?, child_id:?; "CasefoldChild inconsistent");
573            }
574            FsckError::ConflictingTypeForLink(store_id, oid, expected, actual) => {
575                error!(store_id, oid, expected:?, actual:?; "Bad link");
576            }
577            FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
578                error!(store_id, oid, attr_id, size, extent:?; "Extent exceeds length");
579            }
580            FsckError::ExtraAllocations(allocations) => {
581                error!(allocations:?; "Unexpected allocations");
582            }
583            FsckError::ObjectHasChildren(store_id, oid) => {
584                error!(store_id, oid; "Object has unexpected children");
585            }
586            FsckError::UnexpectedJournalFileOffset(object_id) => {
587                error!(
588                    oid = object_id;
589                    "SuperBlock journal_file_offsets contains unexpected object-id"
590                );
591            }
592            FsckError::LinkCycle(store_id, oid) => {
593                error!(store_id, oid; "Link cycle");
594            }
595            FsckError::MalformedAllocation(allocations) => {
596                error!(allocations:?; "Malformed allocations");
597            }
598            FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
599                error!(store_id, oid, extent:?, device_offset; "Malformed extent");
600            }
601            FsckError::MalformedObjectRecord(store_id, key, value) => {
602                error!(store_id, key:?, value:?; "Mismatched key and value");
603            }
604            FsckError::MisalignedAllocation(allocations) => {
605                error!(allocations:?; "Misaligned allocation");
606            }
607            FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
608                error!(store_id, oid, extent:?, device_offset; "Misaligned extent");
609            }
610            FsckError::MissingAllocation(allocation) => {
611                error!(allocation:?; "Missing allocation");
612            }
613            FsckError::MissingAttributeForExtendedAttribute(store_id, oid, attribute_id) => {
614                error!(store_id, oid, attribute_id; "Missing attribute for extended attribute");
615            }
616            FsckError::MissingDataAttribute(store_id, oid) => {
617                error!(store_id, oid; "Missing default attribute");
618            }
619            FsckError::MissingObjectInfo(store_id, oid) => {
620                error!(store_id, oid; "Missing object record");
621            }
622            FsckError::MultipleLinksToDirectory(store_id, oid) => {
623                error!(store_id, oid; "Directory with multiple links");
624            }
625            FsckError::NonRootProjectIdMetadata(store_id, object_id, project_id) => {
626                error!(
627                    store_id,
628                    object_id, project_id; "Non root object in volume with project id metadata"
629                );
630            }
631            FsckError::ObjectCountMismatch(store_id, observed, stored) => {
632                error!(store_id, observed, stored; "Object count mismatch");
633            }
634            FsckError::ProjectOnGraveyard(store_id, project_id, object_id) => {
635                error!(store_id, project_id, object_id; "Project was set on graveyard object");
636            }
637            FsckError::ProjectUsedWithNoUsageTracking(store_id, project_id, node_id) => {
638                error!(store_id, project_id, node_id; "Project used without tracking metadata");
639            }
640            FsckError::RefCountMismatch(oid, observed, stored) => {
641                error!(oid, observed, stored; "Reference count mismatch");
642            }
643            FsckError::RootObjectHasParent(store_id, oid, apparent_parent_id) => {
644                error!(store_id, oid, apparent_parent_id; "Root object is a child");
645            }
646            FsckError::SubDirCountMismatch(store_id, oid, observed, stored) => {
647                error!(store_id, oid, observed, stored; "Sub-dir count mismatch");
648            }
649            FsckError::TombstonedObjectHasRecords(store_id, oid) => {
650                error!(store_id, oid; "Tombstoned object with references");
651            }
652            FsckError::UnexpectedObjectInGraveyard(oid) => {
653                error!(oid; "Unexpected object in graveyard");
654            }
655            FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
656                error!(store_id, key:?, value:?; "Unexpected record");
657            }
658            FsckError::VolumeInChildStore(store_id, oid) => {
659                error!(store_id, oid; "Volume in child store");
660            }
661            FsckError::BadGraveyardValue(store_id, oid) => {
662                error!(store_id, oid; "Bad graveyard value");
663            }
664            FsckError::MissingEncryptionKeys(store_id, oid) => {
665                error!(store_id, oid; "Missing encryption keys");
666            }
667            FsckError::MissingKey(store_id, oid, key_id) => {
668                error!(store_id, oid, key_id; "Missing encryption key");
669            }
670            FsckError::EncryptedChildDirectoryNoWrappingKey(store_id, oid) => {
671                error!(store_id, oid; "Encrypted directory does not have a wrapping key id");
672            }
673            FsckError::EncryptedDirectoryHasUnencryptedChild(store_id, parent_oid, child_oid) => {
674                error!(
675                    store_id,
676                    parent_oid, child_oid; "Encrypted directory has unencrypted child"
677                );
678            }
679            FsckError::UnencryptedDirectoryHasEncryptedChild(store_id, parent_oid, child_oid) => {
680                error!(
681                    store_id,
682                    parent_oid, child_oid; "Unencrypted directory has encrypted child"
683                );
684            }
685            FsckError::ChildEncryptedWithDifferentWrappingKeyThanParent(
686                store_id,
687                parent_id,
688                child_id,
689                parent_wrapping_key_id,
690                child_wrapping_key_id,
691            ) => {
692                error!(
693                    store_id,
694                    parent_id,
695                    child_id,
696                    parent_wrapping_key_id:?,
697                    child_wrapping_key_id:?;
698                    "Child directory encrypted with different wrapping key than parent"
699                );
700            }
701            FsckError::DuplicateKey(store_id, oid, key_id) => {
702                error!(store_id, oid, key_id; "Duplicate key")
703            }
704            FsckError::ZombieFile(store_id, oid, parent_oids) => {
705                error!(store_id, oid, parent_oids:?; "Links exist to file in graveyard")
706            }
707            FsckError::ZombieDir(store_id, oid, parent_oid) => {
708                error!(store_id, oid, parent_oid; "A link exists to directory in graveyard")
709            }
710            FsckError::ZombieSymlink(store_id, oid, parent_oids) => {
711                error!(store_id, oid, parent_oids:?; "Links exists to symlink in graveyard")
712            }
713            FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(store_id, oid) => {
714                error!(store_id, oid; "Verified file does not have a merkle attribute")
715            }
716            FsckError::NonFileMarkedAsVerified(store_id, oid) => {
717                error!(store_id, oid; "Non-file marked as verified")
718            }
719            FsckError::IncorrectMerkleTreeSize(store_id, oid, expected_size, actual_size) => {
720                error!(
721                    store_id,
722                    oid, expected_size, actual_size; "Verified file has incorrect merkle tree size"
723                )
724            }
725            FsckError::TombstonedAttributeDoesNotExist(store_id, oid, attribute_id) => {
726                error!(store_id, oid, attribute_id; "Tombstoned attribute does not exist")
727            }
728            FsckError::TrimValueForGraveyardAttributeEntry(store_id, oid, attribute_id) => {
729                error!(
730                    store_id,
731                    oid, attribute_id; "Invalid Trim value for a graveyard attribute entry",
732                )
733            }
734            FsckError::MissingOverwriteExtents(store_id, oid, attribute_id) => {
735                error!(
736                    store_id,
737                    oid,
738                    attribute_id;
739                    "Overwrite extents indicated, but no overwrite extents were found",
740                )
741            }
742            FsckError::OverwriteExtentFlagUnset(store_id, oid, attribute_id) => {
743                error!(
744                    store_id,
745                    oid,
746                    attribute_id;
747                    "Overwrite extents were found, but metadata flag was not set",
748                )
749            }
750            FsckError::NextObjectIdInUse(store_id, next_object_id) => {
751                error!(store_id, next_object_id; "Next object ID is already in use");
752            }
753        }
754    }
755}
756
757#[derive(Clone, Debug, PartialEq)]
758pub enum FsckFatal {
759    MalformedGraveyard,
760    MalformedLayerFile(u64, u64),
761    MalformedStore(u64),
762    MisOrderedLayerFile(u64, u64),
763    MisOrderedObjectStore(u64),
764    OverlappingKeysInLayerFile(u64, u64, Key, Key),
765    InvalidBloomFilter(u64, u64, Key),
766}
767
768impl FsckFatal {
769    fn to_string(&self) -> String {
770        match self {
771            FsckFatal::MalformedGraveyard => {
772                "Graveyard is malformed; root store is inconsistent".to_string()
773            }
774            FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
775                format!("Layer file {} in object store {} is malformed", layer_file_id, store_id)
776            }
777            FsckFatal::MalformedStore(id) => {
778                format!("Object store {} is malformed; root store is inconsistent", id)
779            }
780            FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
781                format!(
782                    "Layer file {} for store/allocator {} contains out-of-order records",
783                    layer_file_id, store_id
784                )
785            }
786            FsckFatal::MisOrderedObjectStore(store_id) => {
787                format!("Store/allocator {} contains out-of-order or duplicate records", store_id)
788            }
789            FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
790                format!(
791                    "Layer file {} for store/allocator {} contains overlapping keys {:?} and {:?}",
792                    layer_file_id, store_id, key1, key2
793                )
794            }
795            FsckFatal::InvalidBloomFilter(store_id, layer_file_id, key) => {
796                format!(
797                    "Filter for layer files is invalid: reported that key {:?} in layer file {} \
798                    for store/allocator {} does not exist",
799                    key, layer_file_id, store_id
800                )
801            }
802        }
803    }
804
805    fn log(&self) {
806        match self {
807            FsckFatal::MalformedGraveyard => {
808                error!("Graveyard is malformed; root store is inconsistent");
809            }
810            FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
811                error!(store_id, layer_file_id; "Layer file malformed");
812            }
813            FsckFatal::MalformedStore(id) => {
814                error!(id; "Malformed store; root store is inconsistent");
815            }
816            FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
817                // This can be for stores or the allocator.
818                error!(oid = store_id, layer_file_id; "Layer file contains out-of-oder records");
819            }
820            FsckFatal::MisOrderedObjectStore(store_id) => {
821                // This can be for stores or the allocator.
822                error!(
823                    oid = store_id;
824                    "Store/allocator contains out-of-order or duplicate records"
825                );
826            }
827            FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
828                // This can be for stores or the allocator.
829                error!(oid = store_id, layer_file_id, key1:?, key2:?; "Overlapping keys");
830            }
831            FsckFatal::InvalidBloomFilter(store_id, layer_file_id, key) => {
832                error!(oid = store_id, layer_file_id, key:?; "Filter for layer files invalid");
833            }
834        }
835    }
836}