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