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