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