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