Skip to main content

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::allocator::{AllocatorKey, AllocatorValue};
8use crate::object_store::{AttributeId, ObjectDescriptor, ProjectId};
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, AttributeId),
107    ExtentForNonexistentObject(u64, u64),
108    GraveyardRecordForAbsentObject(u64, u64),
109    InvalidObjectIdInStore(u64, Key, Value),
110    LimitForNonExistentStore(u64, u64),
111    OrphanedAttribute(u64, u64, AttributeId),
112    OrphanedObject(u64, u64),
113    OrphanedKeys(u64, u64),
114    OrphanedExtendedAttribute(u64, u64, AttributeId),
115    OrphanedExtendedAttributeRecord(u64, u64),
116    ProjectUsageInconsistent(u64, ProjectId, (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, u32, u32),
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, AttributeId, 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, AttributeId),
246    MissingDataAttribute(u64, u64),
247    MissingEncryptionKeys(u64, u64),
248    MissingKey(u64, u64, u64),
249    MissingObjectInfo(u64, u64),
250    MissingOverwriteExtents(u64, u64, AttributeId),
251    MultipleLinksToDirectory(u64, u64),
252    NextObjectIdInUse(u64, u64),
253    NonFileMarkedAsVerified(u64, u64),
254    NonRootProjectIdMetadata(u64, u64, ProjectId),
255    ObjectCountMismatch(u64, u64, u64),
256    ObjectHasChildren(u64, u64),
257    OverwriteExtentFlagUnset(u64, u64, AttributeId),
258    ProjectOnGraveyard(u64, ProjectId, u64),
259    ProjectUsedWithNoUsageTracking(u64, ProjectId, u64),
260    RefCountMismatch(u64, u64, u64),
261    RootObjectHasParent(u64, u64, u64),
262    SubDirCountMismatch(u64, u64, u64, u64),
263    TombstonedAttributeDoesNotExist(u64, u64, AttributeId),
264    TombstonedObjectHasRecords(u64, u64),
265    TrimValueForGraveyardAttributeEntry(u64, u64, AttributeId),
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    InvalidInoLblk32KeyUsage(u64, u64),
276}
277
278impl FsckError {
279    fn to_string(&self) -> String {
280        match self {
281            FsckError::AllocatedBytesMismatch(observed, stored) => {
282                format!(
283                    "Per-owner allocated bytes was {:?}, but sum of allocations gave {:?}",
284                    stored, observed
285                )
286            }
287            FsckError::AllocatedSizeMismatch(store_id, oid, observed, stored) => {
288                format!(
289                    "Expected {} bytes allocated for object {} in store {}, but found {} bytes",
290                    stored, oid, store_id, observed
291                )
292            }
293            FsckError::AllocationForNonexistentOwner(alloc) => {
294                format!("Allocation {:?} for non-existent owner", alloc)
295            }
296            FsckError::AllocationMismatch(observed, stored) => {
297                format!("Observed allocation {:?} but allocator has {:?}", observed, stored)
298            }
299            FsckError::BadCasefoldHash(store_id, parent_id, child_id, expected, actual) => {
300                format!(
301                    "Bad casefold hash code for store {store_id}, directory {parent_id}, child \
302                     {child_id}. Expected {expected:08x}, actual {actual:08x}",
303                )
304            }
305            FsckError::BadLastObjectId(highest, last_object_id) => {
306                format!("Last object ID {last_object_id} is less than highest found {highest}")
307            }
308            FsckError::CasefoldInconsistency(store_id, parent_id, child_id) => {
309                format!(
310                    "CasefoldChild inconsistent for store {}, directory {}, child {}",
311                    store_id, parent_id, child_id
312                )
313            }
314            FsckError::ConflictingTypeForLink(store_id, object_id, expected, actual) => {
315                format!(
316                    "Object {} in store {} is of type {:?} but has a link of type {:?}",
317                    store_id, object_id, expected, actual
318                )
319            }
320            FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
321                format!(
322                    "Extent {:?} exceeds length {} of attr {} on object {} in store {}",
323                    extent, size, attr_id, oid, store_id
324                )
325            }
326            FsckError::ExtraAllocations(allocations) => {
327                format!("Unexpected allocations {:?}", allocations)
328            }
329            FsckError::ObjectHasChildren(store_id, object_id) => {
330                format!("Object {} in store {} has unexpected children", object_id, store_id)
331            }
332            FsckError::UnexpectedJournalFileOffset(object_id) => {
333                format!(
334                    "SuperBlock journal_file_offsets contains unexpected object_id ({:?}).",
335                    object_id
336                )
337            }
338            FsckError::LinkCycle(store_id, object_id) => {
339                format!("Detected cycle involving object {} in store {}", store_id, object_id)
340            }
341            FsckError::MalformedAllocation(allocations) => {
342                format!("Malformed allocation {:?}", allocations)
343            }
344            FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
345                format!(
346                    "Extent {:?} (offset {}) for object {} in store {} is malformed",
347                    extent, device_offset, oid, store_id
348                )
349            }
350            FsckError::MalformedObjectRecord(store_id, key, value) => {
351                format!(
352                    "Object record in store {} has mismatched key {:?} and value {:?}",
353                    store_id, key, value
354                )
355            }
356            FsckError::MisalignedAllocation(allocations) => {
357                format!("Misaligned allocation {:?}", allocations)
358            }
359            FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
360                format!(
361                    "Extent {:?} (offset {}) for object {} in store {} is misaligned",
362                    extent, device_offset, oid, store_id
363                )
364            }
365            FsckError::MissingAllocation(allocation) => {
366                format!("Observed {:?} but didn't find record in allocator", allocation)
367            }
368            FsckError::MissingAttributeForExtendedAttribute(store_id, oid, attribute_id) => {
369                format!(
370                    "Object {} in store {} has an extended attribute stored in a nonexistent \
371                    attribute {}",
372                    store_id, oid, attribute_id
373                )
374            }
375            FsckError::MissingDataAttribute(store_id, oid) => {
376                format!("File {} in store {} didn't have the default data attribute", store_id, oid)
377            }
378            FsckError::MissingObjectInfo(store_id, object_id) => {
379                format!("Object {} in store {} had no object record", store_id, object_id)
380            }
381            FsckError::MultipleLinksToDirectory(store_id, object_id) => {
382                format!("Directory {} in store {} has multiple links", store_id, object_id)
383            }
384            FsckError::NonRootProjectIdMetadata(store_id, object_id, project_id) => {
385                format!(
386                    "Project Id {} metadata in store {} attached to object {}",
387                    project_id, store_id, object_id
388                )
389            }
390            FsckError::ObjectCountMismatch(store_id, observed, stored) => {
391                format!("Store {} had {} objects, expected {}", store_id, observed, stored)
392            }
393            FsckError::ProjectOnGraveyard(store_id, project_id, object_id) => {
394                format!(
395                    "Store {} had graveyard object {} with project id {}",
396                    store_id, object_id, project_id
397                )
398            }
399            FsckError::ProjectUsedWithNoUsageTracking(store_id, project_id, node_id) => {
400                format!(
401                    "Store {} had node {} with project ids {} but no usage tracking metadata",
402                    store_id, node_id, project_id
403                )
404            }
405            FsckError::RefCountMismatch(oid, observed, stored) => {
406                format!("Object {} had {} references, expected {}", oid, observed, stored)
407            }
408            FsckError::RootObjectHasParent(store_id, object_id, apparent_parent_id) => {
409                format!(
410                    "Object {} is child of {} but is a root object of store {}",
411                    object_id, apparent_parent_id, store_id
412                )
413            }
414            FsckError::SubDirCountMismatch(store_id, object_id, observed, stored) => {
415                format!(
416                    "Directory {} in store {} should have {} sub dirs but had {}",
417                    object_id, store_id, stored, observed
418                )
419            }
420            FsckError::TombstonedObjectHasRecords(store_id, object_id) => {
421                format!(
422                    "Tombstoned object {} in store {} was referenced by other records",
423                    store_id, object_id
424                )
425            }
426            FsckError::UnexpectedObjectInGraveyard(object_id) => {
427                format!("Found a non-file object {} in graveyard", object_id)
428            }
429            FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
430                format!("Unexpected record ({:?}, {:?}) in object store {}", key, value, store_id)
431            }
432            FsckError::VolumeInChildStore(store_id, object_id) => {
433                format!(
434                    "Volume {} found in child store {} instead of root store",
435                    object_id, store_id
436                )
437            }
438            FsckError::BadGraveyardValue(store_id, object_id) => {
439                format!("Bad graveyard value with key <{}, {}>", store_id, object_id)
440            }
441            FsckError::MissingEncryptionKeys(store_id, object_id) => {
442                format!("Missing encryption keys for <{}, {}>", store_id, object_id)
443            }
444            FsckError::MissingKey(store_id, object_id, key_id) => {
445                format!("Missing encryption key for <{}, {}, {}>", store_id, object_id, key_id)
446            }
447            FsckError::EncryptedChildDirectoryNoWrappingKey(store_id, object_id) => {
448                format!(
449                    "Encrypted directory {} in store {} does not have a wrapping key id set",
450                    object_id, store_id
451                )
452            }
453            FsckError::EncryptedDirectoryHasUnencryptedChild(store_id, parent_oid, child_oid) => {
454                format!(
455                    "Encrypted parent directory {} in store {} has unencrypted child {}",
456                    parent_oid, store_id, child_oid
457                )
458            }
459            FsckError::UnencryptedDirectoryHasEncryptedChild(store_id, parent_oid, child_oid) => {
460                format!(
461                    "Unencrypted parent directory {} in store {} has encrypted child {}",
462                    parent_oid, store_id, child_oid
463                )
464            }
465            FsckError::ChildEncryptedWithDifferentWrappingKeyThanParent(
466                store_id,
467                parent_id,
468                child_id,
469                parent_wrapping_key_id,
470                child_wrapping_key_id,
471            ) => {
472                format!(
473                    "Parent directory {} in store {} encrypted with {:?}, child {} encrypted with \
474                    {:?}",
475                    parent_id, store_id, parent_wrapping_key_id, child_id, child_wrapping_key_id,
476                )
477            }
478            FsckError::DuplicateKey(store_id, object_id, key_id) => {
479                format!("Duplicate key for <{}, {}, {}>", store_id, object_id, key_id)
480            }
481            FsckError::ZombieFile(store_id, object_id, parent_object_ids) => {
482                format!(
483                    "File {object_id} in store {store_id} is in graveyard but still has links \
484                     from {parent_object_ids:?}",
485                )
486            }
487            FsckError::ZombieDir(store_id, object_id, parent_object_id) => {
488                format!(
489                    "Directory {object_id} in store {store_id} is in graveyard but still has \
490                     a link from {parent_object_id}",
491                )
492            }
493            FsckError::ZombieSymlink(store_id, object_id, parent_object_ids) => {
494                format!(
495                    "Symlink {object_id} in store {store_id} is in graveyard but still has \
496                     links from {parent_object_ids:?}",
497                )
498            }
499            FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(store_id, object_id) => {
500                format!(
501                    "Object {} in store {} is marked as fsverity-enabled but is missing a \
502                        merkle attribute",
503                    store_id, object_id
504                )
505            }
506            FsckError::NonFileMarkedAsVerified(store_id, object_id) => {
507                format!(
508                    "Object {} in store {} is marked as verified but is not a file",
509                    store_id, object_id
510                )
511            }
512            FsckError::InvalidInoLblk32KeyUsage(store_id, object_id) => {
513                format!("Object {object_id} in store {store_id} uses an InoLblk32 key invalidly")
514            }
515            FsckError::IncorrectMerkleTreeSize(store_id, object_id, expected_size, actual_size) => {
516                format!(
517                    "Object {} in store {} has merkle tree of size {} expected {}",
518                    object_id, store_id, actual_size, expected_size
519                )
520            }
521            FsckError::TombstonedAttributeDoesNotExist(store_id, object_id, attribute_id) => {
522                format!(
523                    "Object {} in store {} has an attribute {} that is tombstoned but does not \
524                     exist.",
525                    object_id, store_id, attribute_id
526                )
527            }
528            FsckError::TrimValueForGraveyardAttributeEntry(store_id, object_id, attribute_id) => {
529                format!(
530                    "Object {} in store {} has a GraveyardAttributeEntry for attribute {} that has \
531                     ObjectValue::Trim",
532                    object_id, store_id, attribute_id,
533                )
534            }
535            FsckError::MissingOverwriteExtents(store_id, object_id, attribute_id) => {
536                format!(
537                    "Object {} in store {} has an attribute {} that indicated it had overwrite \
538                     extents but none were found",
539                    object_id, store_id, attribute_id,
540                )
541            }
542            FsckError::OverwriteExtentFlagUnset(store_id, object_id, attribute_id) => {
543                format!(
544                    "Object {} in store {} has an attribute {} with overwrite extents but the \
545                     metadata indicated it would not",
546                    object_id, store_id, attribute_id,
547                )
548            }
549            FsckError::NextObjectIdInUse(store_id, next_object_id) => {
550                format!("Next object ID {store_id} will use ({next_object_id}) is already in use",)
551            }
552        }
553    }
554
555    fn log(&self) {
556        match self {
557            FsckError::AllocatedBytesMismatch(observed, stored) => {
558                error!(observed:?, stored:?; "Unexpected allocated bytes");
559            }
560            FsckError::AllocatedSizeMismatch(store_id, oid, observed, stored) => {
561                error!(observed, oid, store_id, stored; "Unexpected allocated size");
562            }
563            FsckError::AllocationForNonexistentOwner(alloc) => {
564                error!(alloc:?; "Allocation for non-existent owner")
565            }
566            FsckError::AllocationMismatch(observed, stored) => {
567                error!(observed:?, stored:?; "Unexpected allocation");
568            }
569            FsckError::BadCasefoldHash(store_id, parent_id, child_id, expected, actual) => {
570                warn!(store_id, parent_id, child_id, expected, actual; "Bad casefold hash code");
571            }
572            FsckError::BadLastObjectId(highest, last_object_id) => {
573                error!(highest, last_object_id; "Last object ID is less than highest found");
574            }
575            FsckError::CasefoldInconsistency(store_id, parent_id, child_id) => {
576                error!(store_id:?, parent_id:?, child_id:?; "CasefoldChild inconsistent");
577            }
578            FsckError::ConflictingTypeForLink(store_id, oid, expected, actual) => {
579                error!(store_id, oid, expected:?, actual:?; "Bad link");
580            }
581            FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
582                error!(store_id, oid, attr_id, size, extent:?; "Extent exceeds length");
583            }
584            FsckError::ExtraAllocations(allocations) => {
585                error!(allocations:?; "Unexpected allocations");
586            }
587            FsckError::ObjectHasChildren(store_id, oid) => {
588                error!(store_id, oid; "Object has unexpected children");
589            }
590            FsckError::UnexpectedJournalFileOffset(object_id) => {
591                error!(
592                    oid = object_id;
593                    "SuperBlock journal_file_offsets contains unexpected object-id"
594                );
595            }
596            FsckError::LinkCycle(store_id, oid) => {
597                error!(store_id, oid; "Link cycle");
598            }
599            FsckError::MalformedAllocation(allocations) => {
600                error!(allocations:?; "Malformed allocations");
601            }
602            FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
603                error!(store_id, oid, extent:?, device_offset; "Malformed extent");
604            }
605            FsckError::MalformedObjectRecord(store_id, key, value) => {
606                error!(store_id, key:?, value:?; "Mismatched key and value");
607            }
608            FsckError::MisalignedAllocation(allocations) => {
609                error!(allocations:?; "Misaligned allocation");
610            }
611            FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
612                error!(store_id, oid, extent:?, device_offset; "Misaligned extent");
613            }
614            FsckError::MissingAllocation(allocation) => {
615                error!(allocation:?; "Missing allocation");
616            }
617            FsckError::MissingAttributeForExtendedAttribute(store_id, oid, attribute_id) => {
618                error!(store_id, oid, attribute_id; "Missing attribute for extended attribute");
619            }
620            FsckError::MissingDataAttribute(store_id, oid) => {
621                error!(store_id, oid; "Missing default attribute");
622            }
623            FsckError::MissingObjectInfo(store_id, oid) => {
624                error!(store_id, oid; "Missing object record");
625            }
626            FsckError::MultipleLinksToDirectory(store_id, oid) => {
627                error!(store_id, oid; "Directory with multiple links");
628            }
629            FsckError::NonRootProjectIdMetadata(store_id, object_id, project_id) => {
630                error!(
631                    store_id,
632                    object_id, project_id; "Non root object in volume with project id metadata"
633                );
634            }
635            FsckError::ObjectCountMismatch(store_id, observed, stored) => {
636                error!(store_id, observed, stored; "Object count mismatch");
637            }
638            FsckError::ProjectOnGraveyard(store_id, project_id, object_id) => {
639                error!(store_id, project_id, object_id; "Project was set on graveyard object");
640            }
641            FsckError::ProjectUsedWithNoUsageTracking(store_id, project_id, node_id) => {
642                error!(store_id, project_id, node_id; "Project used without tracking metadata");
643            }
644            FsckError::RefCountMismatch(oid, observed, stored) => {
645                error!(oid, observed, stored; "Reference count mismatch");
646            }
647            FsckError::RootObjectHasParent(store_id, oid, apparent_parent_id) => {
648                error!(store_id, oid, apparent_parent_id; "Root object is a child");
649            }
650            FsckError::SubDirCountMismatch(store_id, oid, observed, stored) => {
651                error!(store_id, oid, observed, stored; "Sub-dir count mismatch");
652            }
653            FsckError::TombstonedObjectHasRecords(store_id, oid) => {
654                error!(store_id, oid; "Tombstoned object with references");
655            }
656            FsckError::UnexpectedObjectInGraveyard(oid) => {
657                error!(oid; "Unexpected object in graveyard");
658            }
659            FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
660                error!(store_id, key:?, value:?; "Unexpected record");
661            }
662            FsckError::VolumeInChildStore(store_id, oid) => {
663                error!(store_id, oid; "Volume in child store");
664            }
665            FsckError::BadGraveyardValue(store_id, oid) => {
666                error!(store_id, oid; "Bad graveyard value");
667            }
668            FsckError::MissingEncryptionKeys(store_id, oid) => {
669                error!(store_id, oid; "Missing encryption keys");
670            }
671            FsckError::MissingKey(store_id, oid, key_id) => {
672                error!(store_id, oid, key_id; "Missing encryption key");
673            }
674            FsckError::EncryptedChildDirectoryNoWrappingKey(store_id, oid) => {
675                error!(store_id, oid; "Encrypted directory does not have a wrapping key id");
676            }
677            FsckError::EncryptedDirectoryHasUnencryptedChild(store_id, parent_oid, child_oid) => {
678                error!(
679                    store_id,
680                    parent_oid, child_oid; "Encrypted directory has unencrypted child"
681                );
682            }
683            FsckError::UnencryptedDirectoryHasEncryptedChild(store_id, parent_oid, child_oid) => {
684                error!(
685                    store_id,
686                    parent_oid, child_oid; "Unencrypted directory has encrypted child"
687                );
688            }
689            FsckError::ChildEncryptedWithDifferentWrappingKeyThanParent(
690                store_id,
691                parent_id,
692                child_id,
693                parent_wrapping_key_id,
694                child_wrapping_key_id,
695            ) => {
696                error!(
697                    store_id,
698                    parent_id,
699                    child_id,
700                    parent_wrapping_key_id:?,
701                    child_wrapping_key_id:?;
702                    "Child directory encrypted with different wrapping key than parent"
703                );
704            }
705            FsckError::DuplicateKey(store_id, oid, key_id) => {
706                error!(store_id, oid, key_id; "Duplicate key")
707            }
708            FsckError::ZombieFile(store_id, oid, parent_oids) => {
709                error!(store_id, oid, parent_oids:?; "Links exist to file in graveyard")
710            }
711            FsckError::ZombieDir(store_id, oid, parent_oid) => {
712                error!(store_id, oid, parent_oid; "A link exists to directory in graveyard")
713            }
714            FsckError::ZombieSymlink(store_id, oid, parent_oids) => {
715                error!(store_id, oid, parent_oids:?; "Links exists to symlink in graveyard")
716            }
717            FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(store_id, oid) => {
718                error!(store_id, oid; "Verified file does not have a merkle attribute")
719            }
720            FsckError::NonFileMarkedAsVerified(store_id, oid) => {
721                error!(store_id, oid; "Non-file marked as verified")
722            }
723            FsckError::InvalidInoLblk32KeyUsage(store_id, oid) => {
724                error!(store_id, oid; "Invalid InoLblk32 key usage")
725            }
726            FsckError::IncorrectMerkleTreeSize(store_id, oid, expected_size, actual_size) => {
727                error!(
728                    store_id,
729                    oid, expected_size, actual_size; "Verified file has incorrect merkle tree size"
730                )
731            }
732            FsckError::TombstonedAttributeDoesNotExist(store_id, oid, attribute_id) => {
733                error!(store_id, oid, attribute_id; "Tombstoned attribute does not exist")
734            }
735            FsckError::TrimValueForGraveyardAttributeEntry(store_id, oid, attribute_id) => {
736                error!(
737                    store_id,
738                    oid, attribute_id; "Invalid Trim value for a graveyard attribute entry",
739                )
740            }
741            FsckError::MissingOverwriteExtents(store_id, oid, attribute_id) => {
742                error!(
743                    store_id,
744                    oid,
745                    attribute_id;
746                    "Overwrite extents indicated, but no overwrite extents were found",
747                )
748            }
749            FsckError::OverwriteExtentFlagUnset(store_id, oid, attribute_id) => {
750                error!(
751                    store_id,
752                    oid,
753                    attribute_id;
754                    "Overwrite extents were found, but metadata flag was not set",
755                )
756            }
757            FsckError::NextObjectIdInUse(store_id, next_object_id) => {
758                error!(store_id, next_object_id; "Next object ID is already in use");
759            }
760        }
761    }
762}
763
764#[derive(Clone, Debug, PartialEq)]
765pub enum FsckFatal {
766    MalformedGraveyard,
767    MalformedLayerFile(u64, u64),
768    MalformedStore(u64),
769    MisOrderedLayerFile(u64, u64),
770    MisOrderedObjectStore(u64),
771    OverlappingKeysInLayerFile(u64, u64, Key, Key),
772    InvalidBloomFilter(u64, u64, Key),
773}
774
775impl FsckFatal {
776    fn to_string(&self) -> String {
777        match self {
778            FsckFatal::MalformedGraveyard => {
779                "Graveyard is malformed; root store is inconsistent".to_string()
780            }
781            FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
782                format!("Layer file {} in object store {} is malformed", layer_file_id, store_id)
783            }
784            FsckFatal::MalformedStore(id) => {
785                format!("Object store {} is malformed; root store is inconsistent", id)
786            }
787            FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
788                format!(
789                    "Layer file {} for store/allocator {} contains out-of-order records",
790                    layer_file_id, store_id
791                )
792            }
793            FsckFatal::MisOrderedObjectStore(store_id) => {
794                format!("Store/allocator {} contains out-of-order or duplicate records", store_id)
795            }
796            FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
797                format!(
798                    "Layer file {} for store/allocator {} contains overlapping keys {:?} and {:?}",
799                    layer_file_id, store_id, key1, key2
800                )
801            }
802            FsckFatal::InvalidBloomFilter(store_id, layer_file_id, key) => {
803                format!(
804                    "Filter for layer files is invalid: reported that key {:?} in layer file {} \
805                    for store/allocator {} does not exist",
806                    key, layer_file_id, store_id
807                )
808            }
809        }
810    }
811
812    fn log(&self) {
813        match self {
814            FsckFatal::MalformedGraveyard => {
815                error!("Graveyard is malformed; root store is inconsistent");
816            }
817            FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
818                error!(store_id, layer_file_id; "Layer file malformed");
819            }
820            FsckFatal::MalformedStore(id) => {
821                error!(id; "Malformed store; root store is inconsistent");
822            }
823            FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
824                // This can be for stores or the allocator.
825                error!(oid = store_id, layer_file_id; "Layer file contains out-of-oder records");
826            }
827            FsckFatal::MisOrderedObjectStore(store_id) => {
828                // This can be for stores or the allocator.
829                error!(
830                    oid = store_id;
831                    "Store/allocator contains out-of-order or duplicate records"
832                );
833            }
834            FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
835                // This can be for stores or the allocator.
836                error!(oid = store_id, layer_file_id, key1:?, key2:?; "Overlapping keys");
837            }
838            FsckFatal::InvalidBloomFilter(store_id, layer_file_id, key) => {
839                error!(oid = store_id, layer_file_id, key:?; "Filter for layer files invalid");
840            }
841        }
842    }
843}