1use crate::log::*;
6use crate::lsm_tree::types::ItemRef;
7use crate::object_store::allocator::{AllocatorKey, AllocatorValue};
8use crate::object_store::{AttributeId, ObjectDescriptor, ProjectId};
9use fxfs_crypto::WrappingKeyId;
10use std::ops::Range;
11
12#[derive(Clone, Debug, PartialEq)]
13pub enum FsckIssue {
14 Warning(FsckWarning),
17 Error(FsckError),
20 Fatal(FsckFatal),
23}
24
25impl FsckIssue {
26 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
90impl From<ObjectDescriptor> for Value {
93 fn from(d: ObjectDescriptor) -> Self {
94 Self(format!("{:?}", d))
95 }
96}
97
98impl<V: std::fmt::Debug> From<&V> for Value {
99 fn from(v: &V) -> Self {
100 Self(format!("{:?}", v))
101 }
102}
103
104#[derive(Clone, Debug, PartialEq)]
105pub enum FsckWarning {
106 ExtentForMissingAttribute(u64, u64, AttributeId),
107 ExtentForNonexistentObject(u64, u64),
108 GraveyardRecordForAbsentObject(u64, u64),
109 InvalidObjectIdInStore(u64, Key, Value),
110 LimitForNonExistentStore(u64, u64),
111 OrphanedAttribute(u64, u64, AttributeId),
112 OrphanedObject(u64, u64),
113 OrphanedKeys(u64, u64),
114 OrphanedExtendedAttribute(u64, u64, AttributeId),
115 OrphanedExtendedAttributeRecord(u64, u64),
116 OversizedInlineSymlink(u64, u64),
117 ProjectUsageInconsistent(u64, ProjectId, (i64, i64), (i64, i64)),
118}
119
120impl FsckWarning {
121 fn to_string(&self) -> String {
122 match self {
123 FsckWarning::ExtentForMissingAttribute(store_id, object_id, attr_id) => {
124 format!(
125 "Found an extent in store {store_id} for missing attribute {attr_id} on \
126 object {object_id}"
127 )
128 }
129 FsckWarning::ExtentForNonexistentObject(store_id, object_id) => {
130 format!("Found an extent in store {store_id} for a non-existent object {object_id}")
131 }
132 FsckWarning::GraveyardRecordForAbsentObject(store_id, object_id) => {
133 format!(
134 "Graveyard contains an entry for object {object_id} in store {store_id}, but \
135 that object is absent"
136 )
137 }
138 FsckWarning::InvalidObjectIdInStore(store_id, key, value) => {
139 format!("Store {store_id} has an invalid object ID ({key:?}, {value:?})")
140 }
141 FsckWarning::LimitForNonExistentStore(store_id, limit) => {
142 format!("Bytes limit of {limit} found for nonexistent store id {store_id}")
143 }
144 FsckWarning::OrphanedAttribute(store_id, object_id, attribute_id) => {
145 format!(
146 "Attribute {attribute_id} found for object {object_id} which doesn't exist in \
147 store {store_id}"
148 )
149 }
150 FsckWarning::OrphanedObject(store_id, object_id) => {
151 format!("Orphaned object {object_id} was found in store {store_id}")
152 }
153 FsckWarning::OrphanedKeys(store_id, object_id) => {
154 format!("Orphaned keys for object {object_id} were found in store {store_id}")
155 }
156 FsckWarning::OrphanedExtendedAttribute(store_id, object_id, attribute_id) => {
157 format!(
158 "Orphaned extended attribute for object {object_id} was found in store \
159 {store_id} with attribute id {attribute_id}"
160 )
161 }
162 FsckWarning::OrphanedExtendedAttributeRecord(store_id, object_id) => {
163 format!(
164 "Orphaned extended attribute record for object {object_id} was found in store \
165 {store_id}"
166 )
167 }
168 FsckWarning::OversizedInlineSymlink(store_id, object_id) => {
169 format!(
170 "Inline symlink {object_id} in store {store_id} exceeds maximum inline size"
171 )
172 }
173 FsckWarning::ProjectUsageInconsistent(
174 store_id,
175 project_id,
176 (stored_bytes, stored_nodes),
177 (used_bytes, used_nodes),
178 ) => {
179 format!(
180 "Project id {project_id} in store {store_id} expected usage \
181 ({stored_bytes}, {stored_nodes}) found ({used_bytes}, {used_nodes})"
182 )
183 }
184 }
185 }
186
187 fn log(&self) {
188 match self {
189 FsckWarning::ExtentForMissingAttribute(store_id, oid, attr_id) => {
190 warn!(store_id, oid, attr_id; "Found an extent for a missing attribute");
191 }
192 FsckWarning::ExtentForNonexistentObject(store_id, oid) => {
193 warn!(store_id, oid; "Extent for missing object");
194 }
195 FsckWarning::GraveyardRecordForAbsentObject(store_id, oid) => {
196 warn!(store_id, oid; "Graveyard entry for missing object");
197 }
198 FsckWarning::InvalidObjectIdInStore(store_id, key, value) => {
199 warn!(store_id, key:?, value:?; "Invalid object ID");
200 }
201 FsckWarning::LimitForNonExistentStore(store_id, limit) => {
202 warn!(store_id, limit; "Found limit for non-existent owner store.");
203 }
204 FsckWarning::OrphanedAttribute(store_id, oid, attribute_id) => {
205 warn!(store_id, oid, attribute_id; "Attribute for missing object");
206 }
207 FsckWarning::OrphanedObject(store_id, oid) => {
208 warn!(oid, store_id; "Orphaned object");
209 }
210 FsckWarning::OrphanedKeys(store_id, oid) => {
211 warn!(oid, store_id; "Orphaned keys");
212 }
213 FsckWarning::OrphanedExtendedAttribute(store_id, oid, attribute_id) => {
214 warn!(oid, store_id, attribute_id; "Orphaned extended attribute");
215 }
216 FsckWarning::OrphanedExtendedAttributeRecord(store_id, oid) => {
217 warn!(oid, store_id; "Orphaned extended attribute record");
218 }
219 FsckWarning::OversizedInlineSymlink(store_id, oid) => {
220 warn!(oid, store_id; "Oversized inline symlink");
221 }
222 FsckWarning::ProjectUsageInconsistent(store_id, project_id, stored, used) => {
223 warn!(project_id, store_id, stored:?, used:?; "Project Inconsistent");
224 }
225 }
226 }
227}
228
229#[derive(Clone, Debug, PartialEq)]
230pub enum FsckError {
231 AllocatedBytesMismatch(Vec<(u64, u64)>, Vec<(u64, u64)>),
232 AllocatedSizeMismatch(u64, u64, u64, u64),
233 AllocationForNonexistentOwner(Allocation),
234 AllocationMismatch(Allocation, Allocation),
235 BadCasefoldHash(u64, u64, u64, u32, u32),
236 BadGraveyardValue(u64, u64),
237 BadLastObjectId(u64, u64),
238 CasefoldInconsistency(u64, u64, u64),
239 ChildEncryptedWithDifferentWrappingKeyThanParent(u64, u64, u64, WrappingKeyId, WrappingKeyId),
240 ConflictingTypeForLink(u64, u64, Value, Value),
241 DuplicateKey(u64, u64, u64),
242 EncryptedChildDirectoryNoWrappingKey(u64, u64),
243 EncryptedDirectoryHasUnencryptedChild(u64, u64, u64),
244 ExtentExceedsLength(u64, u64, AttributeId, u64, Value),
245 ExtraAllocations(Vec<Allocation>),
246 IllegalKeyInRootStore(u64, u64),
247 IncorrectMerkleTreeSize(u64, u64, u64, u64),
248 LinkCycle(u64, u64),
249 MalformedAllocation(Allocation),
250 MalformedExtent(u64, u64, Range<u64>, u64),
251 MalformedObjectRecord(u64, Key, Value),
252 MisalignedAllocation(Allocation),
253 MisalignedExtent(u64, u64, Range<u64>, u64),
254 MissingAllocation(Allocation),
255 InvalidExtendedAttributeId(u64, u64, AttributeId),
256 MissingAttributeForExtendedAttribute(u64, u64, AttributeId),
257 MissingDataAttribute(u64, u64),
258 MissingEncryptionKeys(u64, u64),
259 MissingKey(u64, u64, u64),
260 MissingObjectInfo(u64, u64),
261 MissingOverwriteExtents(u64, u64, AttributeId),
262 MultipleLinksToDirectory(u64, u64),
263 NextObjectIdInUse(u64, u64),
264 NonFileMarkedAsVerified(u64, u64),
265 NonRootProjectIdMetadata(u64, u64, ProjectId),
266 ObjectCountMismatch(u64, u64, u64),
267 ObjectHasChildren(u64, u64),
268 OverwriteExtentFlagUnset(u64, u64, AttributeId),
269 ProjectOnGraveyard(u64, ProjectId, u64),
270 ProjectUsedWithNoUsageTracking(u64, ProjectId, u64),
271 RefCountMismatch(u64, u64, u64),
272 RootObjectHasParent(u64, u64, u64),
273 SubDirCountMismatch(u64, u64, u64, u64),
274 TombstonedAttributeDoesNotExist(u64, u64, AttributeId),
275 TombstonedObjectHasRecords(u64, u64),
276 TrimValueForGraveyardAttributeEntry(u64, u64, AttributeId),
277 UnencryptedDirectoryHasEncryptedChild(u64, u64, u64),
278 UnexpectedJournalFileOffset(u64),
279 UnexpectedObjectInGraveyard(u64),
280 UnexpectedRecordInObjectStore(u64, Key, Value),
281 VerifiedFileDoesNotHaveAMerkleAttribute(u64, u64),
282 VolumeInChildStore(u64, u64),
283 ZombieDir(u64, u64, u64),
284 ZombieFile(u64, u64, Vec<u64>),
285 ZombieSymlink(u64, u64, Vec<u64>),
286 InvalidInoLblk32KeyUsage(u64, u64),
287}
288
289impl FsckError {
290 fn to_string(&self) -> String {
291 match self {
292 FsckError::AllocatedBytesMismatch(observed, stored) => {
293 format!(
294 "Per-owner allocated bytes was {stored:?}, but sum of allocations gave \
295 {observed:?}"
296 )
297 }
298 FsckError::AllocatedSizeMismatch(store_id, oid, observed, stored) => {
299 format!(
300 "Expected {stored} bytes allocated for object {oid} in store {store_id}, but \
301 found {observed} bytes"
302 )
303 }
304 FsckError::AllocationForNonexistentOwner(alloc) => {
305 format!("Allocation {alloc:?} for non-existent owner")
306 }
307 FsckError::AllocationMismatch(observed, stored) => {
308 format!("Observed allocation {observed:?} but allocator has {stored:?}")
309 }
310 FsckError::BadCasefoldHash(store_id, parent_id, child_id, expected, actual) => {
311 format!(
312 "Bad casefold hash code for store {store_id}, directory {parent_id}, child \
313 {child_id}. Expected {expected:08x}, actual {actual:08x}",
314 )
315 }
316 FsckError::BadLastObjectId(highest, last_object_id) => {
317 format!("Last object ID {last_object_id} is less than highest found {highest}")
318 }
319 FsckError::CasefoldInconsistency(store_id, parent_id, child_id) => {
320 format!(
321 "CasefoldChild inconsistent for store {store_id}, directory {parent_id}, \
322 child {child_id}"
323 )
324 }
325 FsckError::ConflictingTypeForLink(store_id, object_id, expected, actual) => {
326 format!(
327 "Object {object_id} in store {store_id} is of type {expected:?} but has a \
328 link of type {actual:?}"
329 )
330 }
331 FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
332 format!(
333 "Extent {extent:?} exceeds length {size} of attr {attr_id} on object {oid} in \
334 store {store_id}"
335 )
336 }
337 FsckError::ExtraAllocations(allocations) => {
338 format!("Unexpected allocations {allocations:?}")
339 }
340 FsckError::IllegalKeyInRootStore(store_id, object_id) => {
341 format!("Object {object_id} in root store {store_id} uses an illegal key type")
342 }
343 FsckError::ObjectHasChildren(store_id, object_id) => {
344 format!("Object {object_id} in store {store_id} has unexpected children")
345 }
346 FsckError::UnexpectedJournalFileOffset(object_id) => {
347 format!(
348 "SuperBlock journal_file_offsets contains unexpected object_id \
349 ({object_id:?})."
350 )
351 }
352 FsckError::LinkCycle(store_id, object_id) => {
353 format!("Detected cycle involving object {object_id} in store {store_id}")
354 }
355 FsckError::MalformedAllocation(allocations) => {
356 format!("Malformed allocation {allocations:?}")
357 }
358 FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
359 format!(
360 "Extent {extent:?} (offset {device_offset}) for object {oid} in store \
361 {store_id} is malformed"
362 )
363 }
364 FsckError::MalformedObjectRecord(store_id, key, value) => {
365 format!(
366 "Object record in store {store_id} has mismatched key {key:?} and value \
367 {value:?}"
368 )
369 }
370 FsckError::MisalignedAllocation(allocations) => {
371 format!("Misaligned allocation {allocations:?}")
372 }
373 FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
374 format!(
375 "Extent {extent:?} (offset {device_offset}) for object {oid} in store \
376 {store_id} is misaligned"
377 )
378 }
379 FsckError::MissingAllocation(allocation) => {
380 format!("Observed {allocation:?} but didn't find record in allocator")
381 }
382 FsckError::InvalidExtendedAttributeId(store_id, oid, attribute_id) => {
383 format!(
384 "Object {oid} in store {store_id} has an extended attribute stored in an \
385 invalid attribute {attribute_id}"
386 )
387 }
388 FsckError::MissingAttributeForExtendedAttribute(store_id, oid, attribute_id) => {
389 format!(
390 "Object {oid} in store {store_id} has an extended attribute stored in a \
391 nonexistent attribute {attribute_id}"
392 )
393 }
394 FsckError::MissingDataAttribute(store_id, oid) => {
395 format!("File {oid} in store {store_id} didn't have the default data attribute")
396 }
397 FsckError::MissingObjectInfo(store_id, object_id) => {
398 format!("Object {object_id} in store {store_id} had no object record")
399 }
400 FsckError::MultipleLinksToDirectory(store_id, object_id) => {
401 format!("Directory {object_id} in store {store_id} has multiple links")
402 }
403 FsckError::NonRootProjectIdMetadata(store_id, object_id, project_id) => {
404 format!(
405 "Project Id {project_id} metadata in store {store_id} attached to object \
406 {object_id}"
407 )
408 }
409 FsckError::ObjectCountMismatch(store_id, observed, stored) => {
410 format!("Store {store_id} had {observed} objects, expected {stored}")
411 }
412 FsckError::ProjectOnGraveyard(store_id, project_id, object_id) => {
413 format!(
414 "Store {store_id} had graveyard object {object_id} with project id \
415 {project_id}"
416 )
417 }
418 FsckError::ProjectUsedWithNoUsageTracking(store_id, project_id, node_id) => {
419 format!(
420 "Store {store_id} had node {node_id} with project ids {project_id} but no \
421 usage tracking metadata"
422 )
423 }
424 FsckError::RefCountMismatch(oid, observed, stored) => {
425 format!("Object {oid} had {observed} references, expected {stored}")
426 }
427 FsckError::RootObjectHasParent(store_id, object_id, apparent_parent_id) => {
428 format!(
429 "Object {object_id} is child of {apparent_parent_id} but is a root object of \
430 store {store_id}"
431 )
432 }
433 FsckError::SubDirCountMismatch(store_id, object_id, observed, stored) => {
434 format!(
435 "Directory {object_id} in store {store_id} should have {stored} sub dirs but \
436 had {observed}"
437 )
438 }
439 FsckError::TombstonedObjectHasRecords(store_id, object_id) => {
440 format!(
441 "Tombstoned object {object_id} in store {store_id} was referenced by other \
442 records"
443 )
444 }
445 FsckError::UnexpectedObjectInGraveyard(object_id) => {
446 format!("Found a non-file object {object_id} in graveyard")
447 }
448 FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
449 format!("Unexpected record ({key:?}, {value:?}) in object store {store_id}")
450 }
451 FsckError::VolumeInChildStore(store_id, object_id) => {
452 format!("Volume {object_id} found in child store {store_id} instead of root store")
453 }
454 FsckError::BadGraveyardValue(store_id, object_id) => {
455 format!("Bad graveyard value with key <{store_id}, {object_id}>")
456 }
457 FsckError::MissingEncryptionKeys(store_id, object_id) => {
458 format!("Missing encryption keys for <{store_id}, {object_id}>")
459 }
460 FsckError::MissingKey(store_id, object_id, key_id) => {
461 format!("Missing encryption key for <{store_id}, {object_id}, {key_id}>")
462 }
463 FsckError::EncryptedChildDirectoryNoWrappingKey(store_id, object_id) => {
464 format!(
465 "Encrypted directory {object_id} in store {store_id} does not have a wrapping \
466 key id set"
467 )
468 }
469 FsckError::EncryptedDirectoryHasUnencryptedChild(store_id, parent_oid, child_oid) => {
470 format!(
471 "Encrypted parent directory {parent_oid} in store {store_id} has unencrypted \
472 child {child_oid}"
473 )
474 }
475 FsckError::UnencryptedDirectoryHasEncryptedChild(store_id, parent_oid, child_oid) => {
476 format!(
477 "Unencrypted parent directory {parent_oid} in store {store_id} has encrypted \
478 child {child_oid}"
479 )
480 }
481 FsckError::ChildEncryptedWithDifferentWrappingKeyThanParent(
482 store_id,
483 parent_id,
484 child_id,
485 parent_wrapping_key_id,
486 child_wrapping_key_id,
487 ) => {
488 format!(
489 "Parent directory {parent_id} in store {store_id} encrypted with \
490 {parent_wrapping_key_id:?}, child {child_id} encrypted with \
491 {child_wrapping_key_id:?}",
492 )
493 }
494 FsckError::DuplicateKey(store_id, object_id, key_id) => {
495 format!("Duplicate key for <{store_id}, {object_id}, {key_id}>")
496 }
497 FsckError::ZombieFile(store_id, object_id, parent_object_ids) => {
498 format!(
499 "File {object_id} in store {store_id} is in graveyard but still has links \
500 from {parent_object_ids:?}",
501 )
502 }
503 FsckError::ZombieDir(store_id, object_id, parent_object_id) => {
504 format!(
505 "Directory {object_id} in store {store_id} is in graveyard but still has \
506 a link from {parent_object_id}",
507 )
508 }
509 FsckError::ZombieSymlink(store_id, object_id, parent_object_ids) => {
510 format!(
511 "Symlink {object_id} in store {store_id} is in graveyard but still has \
512 links from {parent_object_ids:?}",
513 )
514 }
515 FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(store_id, object_id) => {
516 format!(
517 "Object {object_id} in store {store_id} is marked as fsverity-enabled but is \
518 missing a merkle attribute"
519 )
520 }
521 FsckError::NonFileMarkedAsVerified(store_id, object_id) => {
522 format!(
523 "Object {object_id} in store {store_id} is marked as verified but is not a \
524 file"
525 )
526 }
527 FsckError::InvalidInoLblk32KeyUsage(store_id, object_id) => {
528 format!("Object {object_id} in store {store_id} uses an InoLblk32 key invalidly")
529 }
530 FsckError::IncorrectMerkleTreeSize(store_id, object_id, expected_size, actual_size) => {
531 format!(
532 "Object {object_id} in store {store_id} has merkle tree of size \
533 {actual_size} expected {expected_size}"
534 )
535 }
536 FsckError::TombstonedAttributeDoesNotExist(store_id, object_id, attribute_id) => {
537 format!(
538 "Object {object_id} in store {store_id} has an attribute {attribute_id} that \
539 is tombstoned but does not exist.",
540 )
541 }
542 FsckError::TrimValueForGraveyardAttributeEntry(store_id, object_id, attribute_id) => {
543 format!(
544 "Object {object_id} in store {store_id} has a GraveyardAttributeEntry for \
545 attribute {attribute_id} that has ObjectValue::Trim",
546 )
547 }
548 FsckError::MissingOverwriteExtents(store_id, object_id, attribute_id) => {
549 format!(
550 "Object {object_id} in store {store_id} has an attribute {attribute_id} that \
551 indicated it had overwrite extents but none were found",
552 )
553 }
554 FsckError::OverwriteExtentFlagUnset(store_id, object_id, attribute_id) => {
555 format!(
556 "Object {object_id} in store {store_id} has an attribute {attribute_id} with \
557 overwrite extents but the metadata indicated it would not",
558 )
559 }
560 FsckError::NextObjectIdInUse(store_id, next_object_id) => {
561 format!("Next object ID {store_id} will use ({next_object_id}) is already in use",)
562 }
563 }
564 }
565
566 fn log(&self) {
567 match self {
568 FsckError::AllocatedBytesMismatch(observed, stored) => {
569 error!(observed:?, stored:?; "Unexpected allocated bytes");
570 }
571 FsckError::AllocatedSizeMismatch(store_id, oid, observed, stored) => {
572 error!(observed, oid, store_id, stored; "Unexpected allocated size");
573 }
574 FsckError::AllocationForNonexistentOwner(alloc) => {
575 error!(alloc:?; "Allocation for non-existent owner")
576 }
577 FsckError::AllocationMismatch(observed, stored) => {
578 error!(observed:?, stored:?; "Unexpected allocation");
579 }
580 FsckError::BadCasefoldHash(store_id, parent_id, child_id, expected, actual) => {
581 warn!(store_id, parent_id, child_id, expected, actual; "Bad casefold hash code");
582 }
583 FsckError::BadLastObjectId(highest, last_object_id) => {
584 error!(highest, last_object_id; "Last object ID is less than highest found");
585 }
586 FsckError::CasefoldInconsistency(store_id, parent_id, child_id) => {
587 error!(store_id:?, parent_id:?, child_id:?; "CasefoldChild inconsistent");
588 }
589 FsckError::ConflictingTypeForLink(store_id, oid, expected, actual) => {
590 error!(store_id, oid, expected:?, actual:?; "Bad link");
591 }
592 FsckError::ExtentExceedsLength(store_id, oid, attr_id, size, extent) => {
593 error!(store_id, oid, attr_id, size, extent:?; "Extent exceeds length");
594 }
595 FsckError::ExtraAllocations(allocations) => {
596 error!(allocations:?; "Unexpected allocations");
597 }
598 FsckError::IllegalKeyInRootStore(store_id, oid) => {
599 error!(store_id, oid; "Illegal key in root store");
600 }
601 FsckError::ObjectHasChildren(store_id, oid) => {
602 error!(store_id, oid; "Object has unexpected children");
603 }
604 FsckError::UnexpectedJournalFileOffset(object_id) => {
605 error!(
606 oid = object_id;
607 "SuperBlock journal_file_offsets contains unexpected object-id"
608 );
609 }
610 FsckError::LinkCycle(store_id, oid) => {
611 error!(store_id, oid; "Link cycle");
612 }
613 FsckError::MalformedAllocation(allocations) => {
614 error!(allocations:?; "Malformed allocations");
615 }
616 FsckError::MalformedExtent(store_id, oid, extent, device_offset) => {
617 error!(store_id, oid, extent:?, device_offset; "Malformed extent");
618 }
619 FsckError::MalformedObjectRecord(store_id, key, value) => {
620 error!(store_id, key:?, value:?; "Mismatched key and value");
621 }
622 FsckError::MisalignedAllocation(allocations) => {
623 error!(allocations:?; "Misaligned allocation");
624 }
625 FsckError::MisalignedExtent(store_id, oid, extent, device_offset) => {
626 error!(store_id, oid, extent:?, device_offset; "Misaligned extent");
627 }
628 FsckError::MissingAllocation(allocation) => {
629 error!(allocation:?; "Missing allocation");
630 }
631 FsckError::InvalidExtendedAttributeId(store_id, oid, attribute_id) => {
632 error!(store_id, oid, attribute_id; "Invalid extended attribute id");
633 }
634 FsckError::MissingAttributeForExtendedAttribute(store_id, oid, attribute_id) => {
635 error!(store_id, oid, attribute_id; "Missing attribute for extended attribute");
636 }
637 FsckError::MissingDataAttribute(store_id, oid) => {
638 error!(store_id, oid; "Missing default attribute");
639 }
640 FsckError::MissingObjectInfo(store_id, oid) => {
641 error!(store_id, oid; "Missing object record");
642 }
643 FsckError::MultipleLinksToDirectory(store_id, oid) => {
644 error!(store_id, oid; "Directory with multiple links");
645 }
646 FsckError::NonRootProjectIdMetadata(store_id, object_id, project_id) => {
647 error!(
648 store_id,
649 object_id, project_id; "Non root object in volume with project id metadata"
650 );
651 }
652 FsckError::ObjectCountMismatch(store_id, observed, stored) => {
653 error!(store_id, observed, stored; "Object count mismatch");
654 }
655 FsckError::ProjectOnGraveyard(store_id, project_id, object_id) => {
656 error!(store_id, project_id, object_id; "Project was set on graveyard object");
657 }
658 FsckError::ProjectUsedWithNoUsageTracking(store_id, project_id, node_id) => {
659 error!(store_id, project_id, node_id; "Project used without tracking metadata");
660 }
661 FsckError::RefCountMismatch(oid, observed, stored) => {
662 error!(oid, observed, stored; "Reference count mismatch");
663 }
664 FsckError::RootObjectHasParent(store_id, oid, apparent_parent_id) => {
665 error!(store_id, oid, apparent_parent_id; "Root object is a child");
666 }
667 FsckError::SubDirCountMismatch(store_id, oid, observed, stored) => {
668 error!(store_id, oid, observed, stored; "Sub-dir count mismatch");
669 }
670 FsckError::TombstonedObjectHasRecords(store_id, oid) => {
671 error!(store_id, oid; "Tombstoned object with references");
672 }
673 FsckError::UnexpectedObjectInGraveyard(oid) => {
674 error!(oid; "Unexpected object in graveyard");
675 }
676 FsckError::UnexpectedRecordInObjectStore(store_id, key, value) => {
677 error!(store_id, key:?, value:?; "Unexpected record");
678 }
679 FsckError::VolumeInChildStore(store_id, oid) => {
680 error!(store_id, oid; "Volume in child store");
681 }
682 FsckError::BadGraveyardValue(store_id, oid) => {
683 error!(store_id, oid; "Bad graveyard value");
684 }
685 FsckError::MissingEncryptionKeys(store_id, oid) => {
686 error!(store_id, oid; "Missing encryption keys");
687 }
688 FsckError::MissingKey(store_id, oid, key_id) => {
689 error!(store_id, oid, key_id; "Missing encryption key");
690 }
691 FsckError::EncryptedChildDirectoryNoWrappingKey(store_id, oid) => {
692 error!(store_id, oid; "Encrypted directory does not have a wrapping key id");
693 }
694 FsckError::EncryptedDirectoryHasUnencryptedChild(store_id, parent_oid, child_oid) => {
695 error!(
696 store_id,
697 parent_oid, child_oid; "Encrypted directory has unencrypted child"
698 );
699 }
700 FsckError::UnencryptedDirectoryHasEncryptedChild(store_id, parent_oid, child_oid) => {
701 error!(
702 store_id,
703 parent_oid, child_oid; "Unencrypted directory has encrypted child"
704 );
705 }
706 FsckError::ChildEncryptedWithDifferentWrappingKeyThanParent(
707 store_id,
708 parent_id,
709 child_id,
710 parent_wrapping_key_id,
711 child_wrapping_key_id,
712 ) => {
713 error!(
714 store_id,
715 parent_id,
716 child_id,
717 parent_wrapping_key_id:?,
718 child_wrapping_key_id:?;
719 "Child object encrypted with different wrapping key than parent"
720 );
721 }
722 FsckError::DuplicateKey(store_id, oid, key_id) => {
723 error!(store_id, oid, key_id; "Duplicate key")
724 }
725 FsckError::ZombieFile(store_id, oid, parent_oids) => {
726 error!(store_id, oid, parent_oids:?; "Links exist to file in graveyard")
727 }
728 FsckError::ZombieDir(store_id, oid, parent_oid) => {
729 error!(store_id, oid, parent_oid; "A link exists to directory in graveyard")
730 }
731 FsckError::ZombieSymlink(store_id, oid, parent_oids) => {
732 error!(store_id, oid, parent_oids:?; "Links exists to symlink in graveyard")
733 }
734 FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(store_id, oid) => {
735 error!(store_id, oid; "Verified file does not have a merkle attribute")
736 }
737 FsckError::NonFileMarkedAsVerified(store_id, oid) => {
738 error!(store_id, oid; "Non-file marked as verified")
739 }
740 FsckError::InvalidInoLblk32KeyUsage(store_id, oid) => {
741 error!(store_id, oid; "Invalid InoLblk32 key usage")
742 }
743 FsckError::IncorrectMerkleTreeSize(store_id, oid, expected_size, actual_size) => {
744 error!(
745 store_id,
746 oid, expected_size, actual_size; "Verified file has incorrect merkle tree size"
747 )
748 }
749 FsckError::TombstonedAttributeDoesNotExist(store_id, oid, attribute_id) => {
750 error!(store_id, oid, attribute_id; "Tombstoned attribute does not exist")
751 }
752 FsckError::TrimValueForGraveyardAttributeEntry(store_id, oid, attribute_id) => {
753 error!(
754 store_id,
755 oid, attribute_id; "Invalid Trim value for a graveyard attribute entry",
756 )
757 }
758 FsckError::MissingOverwriteExtents(store_id, oid, attribute_id) => {
759 error!(
760 store_id,
761 oid,
762 attribute_id;
763 "Overwrite extents indicated, but no overwrite extents were found",
764 )
765 }
766 FsckError::OverwriteExtentFlagUnset(store_id, oid, attribute_id) => {
767 error!(
768 store_id,
769 oid,
770 attribute_id;
771 "Overwrite extents were found, but metadata flag was not set",
772 )
773 }
774 FsckError::NextObjectIdInUse(store_id, next_object_id) => {
775 error!(store_id, next_object_id; "Next object ID is already in use");
776 }
777 }
778 }
779}
780
781#[derive(Clone, Debug, PartialEq)]
782pub enum FsckFatal {
783 MalformedGraveyard,
784 MalformedLayerFile(u64, u64),
785 MalformedStore(u64),
786 MisOrderedLayerFile(u64, u64),
787 MisOrderedObjectStore(u64),
788 OverlappingKeysInLayerFile(u64, u64, Key, Key),
789 InvalidBloomFilter(u64, u64, Key),
790}
791
792impl FsckFatal {
793 fn to_string(&self) -> String {
794 match self {
795 FsckFatal::MalformedGraveyard => {
796 "Graveyard is malformed; root store is inconsistent".to_string()
797 }
798 FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
799 format!("Layer file {layer_file_id} in object store {store_id} is malformed")
800 }
801 FsckFatal::MalformedStore(id) => {
802 format!("Object store {id} is malformed; root store is inconsistent")
803 }
804 FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
805 format!(
806 "Layer file {layer_file_id} for store/allocator {store_id} contains \
807 out-of-order records"
808 )
809 }
810 FsckFatal::MisOrderedObjectStore(store_id) => {
811 format!("Store/allocator {store_id} contains out-of-order or duplicate records")
812 }
813 FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
814 format!(
815 "Layer file {layer_file_id} for store/allocator {store_id} contains \
816 overlapping keys {key1:?} and {key2:?}"
817 )
818 }
819 FsckFatal::InvalidBloomFilter(store_id, layer_file_id, key) => {
820 format!(
821 "Filter for layer files is invalid: reported that key {key:?} in layer file \
822 {layer_file_id} for store/allocator {store_id} does not exist"
823 )
824 }
825 }
826 }
827
828 fn log(&self) {
829 match self {
830 FsckFatal::MalformedGraveyard => {
831 error!("Graveyard is malformed; root store is inconsistent");
832 }
833 FsckFatal::MalformedLayerFile(store_id, layer_file_id) => {
834 error!(store_id, layer_file_id; "Layer file malformed");
835 }
836 FsckFatal::MalformedStore(id) => {
837 error!(id; "Malformed store; root store is inconsistent");
838 }
839 FsckFatal::MisOrderedLayerFile(store_id, layer_file_id) => {
840 error!(oid = store_id, layer_file_id; "Layer file contains out-of-order records");
842 }
843 FsckFatal::MisOrderedObjectStore(store_id) => {
844 error!(
846 oid = store_id;
847 "Store/allocator contains out-of-order or duplicate records"
848 );
849 }
850 FsckFatal::OverlappingKeysInLayerFile(store_id, layer_file_id, key1, key2) => {
851 error!(oid = store_id, layer_file_id, key1:?, key2:?; "Overlapping keys");
853 }
854 FsckFatal::InvalidBloomFilter(store_id, layer_file_id, key) => {
855 error!(oid = store_id, layer_file_id, key:?; "Filter for layer files invalid");
856 }
857 }
858 }
859}
860
861#[cfg(test)]
862mod tests {
863 use super::*;
864 use crate::lsm_tree::types::Item;
865 use crate::object_store::ObjectDescriptor;
866
867 #[fuchsia::test]
868 fn test_type_conversions() {
869 let alloc_item = Item::new(
870 AllocatorKey { device_range: (0..4096).into() },
871 AllocatorValue::Abs { count: 1, owner_object_id: 2 },
872 );
873 let alloc = Allocation::from(alloc_item.as_item_ref());
874 assert_eq!(alloc.range, 0..4096);
875 assert_eq!(alloc.value, AllocatorValue::Abs { count: 1, owner_object_id: 2 });
876
877 let key_from_item = Key::from(alloc_item.as_item_ref());
878 let key_from_ref = Key::from(&alloc_item.key);
879 assert_eq!(key_from_item, key_from_ref);
880
881 let val_from_item = Value::from(alloc_item.as_item_ref());
882 let val_from_ref = Value::from(&alloc_item.value);
883 assert_eq!(val_from_item, val_from_ref);
884
885 let val_from_desc = Value::from(ObjectDescriptor::File);
886 assert_eq!(val_from_desc, Value::from(&ObjectDescriptor::File));
887 }
888
889 #[fuchsia::test]
890 fn test_fsck_issue() {
891 let warning = FsckIssue::Warning(FsckWarning::OrphanedObject(1, 2));
892 assert!(!warning.is_error());
893 assert!(warning.to_string().starts_with("WARNING: "));
894 warning.log();
895
896 let err = FsckIssue::Error(FsckError::UnexpectedObjectInGraveyard(3));
897 assert!(err.is_error());
898 assert!(err.to_string().starts_with("ERROR: "));
899 err.log();
900
901 let fatal = FsckIssue::Fatal(FsckFatal::MalformedGraveyard);
902 assert!(fatal.is_error());
903 assert!(fatal.to_string().starts_with("FATAL: "));
904 fatal.log();
905 }
906
907 #[fuchsia::test]
908 fn test_fsck_warnings() {
909 let k = Key::from(&1u64);
910 let v = Value::from(&2u64);
911 let attr_id = AttributeId(3);
912 let proj_id = ProjectId::new(2).unwrap();
913 let warnings = vec![
914 FsckWarning::ExtentForMissingAttribute(10, 20, attr_id),
915 FsckWarning::ExtentForNonexistentObject(10, 20),
916 FsckWarning::GraveyardRecordForAbsentObject(10, 20),
917 FsckWarning::InvalidObjectIdInStore(10, k, v),
918 FsckWarning::LimitForNonExistentStore(10, 100),
919 FsckWarning::OrphanedAttribute(10, 20, attr_id),
920 FsckWarning::OrphanedObject(10, 20),
921 FsckWarning::OrphanedKeys(10, 20),
922 FsckWarning::OrphanedExtendedAttribute(10, 20, attr_id),
923 FsckWarning::OrphanedExtendedAttributeRecord(10, 20),
924 FsckWarning::OversizedInlineSymlink(10, 20),
925 FsckWarning::ProjectUsageInconsistent(10, proj_id, (10, 1), (20, 2)),
926 ];
927 assert!(
928 FsckWarning::GraveyardRecordForAbsentObject(10, 20)
929 .to_string()
930 .contains("object 20 in store 10")
931 );
932 for warning in warnings {
933 assert!(!warning.to_string().is_empty());
934 warning.log();
935 }
936 }
937
938 #[fuchsia::test]
939 fn test_fsck_errors() {
940 let alloc = Allocation {
941 range: 0..4096,
942 value: AllocatorValue::Abs { count: 1, owner_object_id: 2 },
943 };
944 let k = Key::from(&1u64);
945 let v = Value::from(&2u64);
946 let attr_id = AttributeId(3);
947 let proj_id = ProjectId::new(2).unwrap();
948 let wk1: WrappingKeyId = u128::to_le_bytes(1);
949 let wk2: WrappingKeyId = u128::to_le_bytes(2);
950 let errors = vec![
951 FsckError::AllocatedBytesMismatch(vec![(1, 100)], vec![(1, 200)]),
952 FsckError::AllocatedSizeMismatch(10, 20, 100, 200),
953 FsckError::AllocationForNonexistentOwner(alloc.clone()),
954 FsckError::AllocationMismatch(alloc.clone(), alloc.clone()),
955 FsckError::BadCasefoldHash(10, 20, 3, 4, 5),
956 FsckError::BadGraveyardValue(10, 20),
957 FsckError::BadLastObjectId(10, 5),
958 FsckError::CasefoldInconsistency(10, 20, 3),
959 FsckError::ChildEncryptedWithDifferentWrappingKeyThanParent(10, 20, 3, wk1, wk2),
960 FsckError::ConflictingTypeForLink(10, 20, v.clone(), v.clone()),
961 FsckError::DuplicateKey(10, 20, 3),
962 FsckError::EncryptedChildDirectoryNoWrappingKey(10, 20),
963 FsckError::EncryptedDirectoryHasUnencryptedChild(10, 20, 3),
964 FsckError::ExtentExceedsLength(10, 20, attr_id, 100, v.clone()),
965 FsckError::ExtraAllocations(vec![alloc.clone()]),
966 FsckError::IllegalKeyInRootStore(10, 20),
967 FsckError::IncorrectMerkleTreeSize(10, 20, 100, 200),
968 FsckError::LinkCycle(10, 20),
969 FsckError::MalformedAllocation(alloc.clone()),
970 FsckError::MalformedExtent(10, 20, 0..4096, 8192),
971 FsckError::MalformedObjectRecord(10, k.clone(), v.clone()),
972 FsckError::MisalignedAllocation(alloc.clone()),
973 FsckError::MisalignedExtent(10, 20, 0..4096, 8192),
974 FsckError::MissingAllocation(alloc),
975 FsckError::InvalidExtendedAttributeId(10, 20, attr_id),
976 FsckError::MissingAttributeForExtendedAttribute(10, 20, attr_id),
977 FsckError::MissingDataAttribute(10, 20),
978 FsckError::MissingEncryptionKeys(10, 20),
979 FsckError::MissingKey(10, 20, 3),
980 FsckError::MissingObjectInfo(10, 20),
981 FsckError::MissingOverwriteExtents(10, 20, attr_id),
982 FsckError::MultipleLinksToDirectory(10, 20),
983 FsckError::NextObjectIdInUse(10, 20),
984 FsckError::NonFileMarkedAsVerified(10, 20),
985 FsckError::NonRootProjectIdMetadata(10, 20, proj_id),
986 FsckError::ObjectCountMismatch(10, 20, 3),
987 FsckError::ObjectHasChildren(10, 20),
988 FsckError::OverwriteExtentFlagUnset(10, 20, attr_id),
989 FsckError::ProjectOnGraveyard(10, proj_id, 3),
990 FsckError::ProjectUsedWithNoUsageTracking(10, proj_id, 3),
991 FsckError::RefCountMismatch(10, 20, 3),
992 FsckError::RootObjectHasParent(10, 20, 3),
993 FsckError::SubDirCountMismatch(10, 20, 3, 4),
994 FsckError::TombstonedAttributeDoesNotExist(10, 20, attr_id),
995 FsckError::TombstonedObjectHasRecords(10, 20),
996 FsckError::TrimValueForGraveyardAttributeEntry(10, 20, attr_id),
997 FsckError::UnencryptedDirectoryHasEncryptedChild(10, 20, 3),
998 FsckError::UnexpectedJournalFileOffset(10),
999 FsckError::UnexpectedObjectInGraveyard(10),
1000 FsckError::UnexpectedRecordInObjectStore(10, k, v.clone()),
1001 FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(10, 20),
1002 FsckError::VolumeInChildStore(10, 20),
1003 FsckError::ZombieDir(10, 20, 3),
1004 FsckError::ZombieFile(10, 20, vec![3]),
1005 FsckError::ZombieSymlink(10, 20, vec![3]),
1006 FsckError::InvalidInoLblk32KeyUsage(10, 20),
1007 ];
1008 assert!(
1009 FsckError::ConflictingTypeForLink(10, 20, v.clone(), v)
1010 .to_string()
1011 .contains("Object 20 in store 10")
1012 );
1013 assert!(FsckError::LinkCycle(10, 20).to_string().contains("object 20 in store 10"));
1014 assert!(
1015 FsckError::MissingAttributeForExtendedAttribute(10, 20, attr_id)
1016 .to_string()
1017 .contains("Object 20 in store 10")
1018 );
1019 assert!(
1020 FsckError::MissingDataAttribute(10, 20).to_string().contains("File 20 in store 10")
1021 );
1022 assert!(FsckError::MissingObjectInfo(10, 20).to_string().contains("Object 20 in store 10"));
1023 assert!(
1024 FsckError::MultipleLinksToDirectory(10, 20)
1025 .to_string()
1026 .contains("Directory 20 in store 10")
1027 );
1028 assert!(
1029 FsckError::TombstonedObjectHasRecords(10, 20)
1030 .to_string()
1031 .contains("object 20 in store 10")
1032 );
1033 assert!(
1034 FsckError::VerifiedFileDoesNotHaveAMerkleAttribute(10, 20)
1035 .to_string()
1036 .contains("Object 20 in store 10")
1037 );
1038 assert!(
1039 FsckError::NonFileMarkedAsVerified(10, 20)
1040 .to_string()
1041 .contains("Object 20 in store 10")
1042 );
1043 for err in errors {
1044 assert!(!err.to_string().is_empty());
1045 err.log();
1046 }
1047 }
1048
1049 #[fuchsia::test]
1050 fn test_fsck_fatals() {
1051 let k = Key::from(&1u64);
1052 let fatals = vec![
1053 FsckFatal::MalformedGraveyard,
1054 FsckFatal::MalformedLayerFile(1, 2),
1055 FsckFatal::MalformedStore(1),
1056 FsckFatal::MisOrderedLayerFile(1, 2),
1057 FsckFatal::MisOrderedObjectStore(1),
1058 FsckFatal::OverlappingKeysInLayerFile(1, 2, k.clone(), k.clone()),
1059 FsckFatal::InvalidBloomFilter(1, 2, k),
1060 ];
1061 for fatal in fatals {
1062 assert!(!fatal.to_string().is_empty());
1063 fatal.log();
1064 }
1065 }
1066}