1use crate::filesystem::FxFilesystem;
6use crate::fsck::errors::{FsckError, FsckFatal, FsckIssue, FsckWarning};
7use crate::log::*;
8use crate::lsm_tree::Query;
9use crate::lsm_tree::skip_list_layer::SkipListLayer;
10use crate::lsm_tree::types::{
11 BoxedLayerIterator, Item, Key, Layer, LayerIterator, LayerKey, Value,
12};
13use crate::object_handle::INVALID_OBJECT_ID;
14use crate::object_store::allocator::{AllocatorKey, AllocatorValue, CoalescingIterator};
15use crate::object_store::journal::super_block::SuperBlockInstance;
16use crate::object_store::volume::root_volume;
17use crate::object_store::{ObjectStore, load_store_info};
18use anyhow::{Context, Error, anyhow};
19use futures::try_join;
20use fxfs_crypto::Crypt;
21use rustc_hash::FxHashSet as HashSet;
22use std::collections::BTreeMap;
23use std::iter::zip;
24use std::ops::Bound;
25use std::sync::Arc;
26use std::sync::atomic::{AtomicU64, Ordering};
27
28pub mod errors;
29
30mod store_scanner;
31
32#[cfg(test)]
33mod tests;
34
35pub const NUM_FRAGMENTATION_HISTOGRAM_SLOTS: usize = 12;
37#[derive(Default, Debug)]
38pub struct FragmentationStats {
39 pub extent_size: [u64; NUM_FRAGMENTATION_HISTOGRAM_SLOTS],
41 pub extent_count: [u64; NUM_FRAGMENTATION_HISTOGRAM_SLOTS],
43 pub free_space: [u64; NUM_FRAGMENTATION_HISTOGRAM_SLOTS],
45}
46
47impl FragmentationStats {
48 pub fn get_histogram_bucket_for_size(size: u64) -> usize {
50 return Self::get_histogram_bucket_for_count(size / 4096);
51 }
52 pub fn get_histogram_bucket_for_count(count: u64) -> usize {
54 let log_count = (64 - count.leading_zeros()) as usize;
55 return log_count.clamp(1, NUM_FRAGMENTATION_HISTOGRAM_SLOTS) - 1;
56 }
57}
58
59#[derive(Default, Debug)]
61pub struct FsckResult {
62 pub fragmentation: FragmentationStats,
63}
64
65pub struct FsckOptions<'a> {
66 pub fail_on_warning: bool,
68 pub halt_on_error: bool,
70 pub do_slow_passes: bool,
72 pub on_error: Box<dyn Fn(&FsckIssue) + Send + Sync + 'a>,
74 pub quiet: bool,
76 pub verbose: bool,
78 pub no_lock: bool,
80}
81
82impl Default for FsckOptions<'_> {
83 fn default() -> Self {
84 Self {
85 fail_on_warning: false,
86 halt_on_error: false,
87 do_slow_passes: true,
88 on_error: Box::new(FsckIssue::log),
89 quiet: false,
90 verbose: false,
91 no_lock: false,
92 }
93 }
94}
95
96pub async fn fsck(filesystem: Arc<FxFilesystem>) -> Result<FsckResult, Error> {
104 fsck_with_options(filesystem, &FsckOptions::default()).await
105}
106
107pub async fn fsck_with_options(
108 filesystem: Arc<FxFilesystem>,
109 options: &FsckOptions<'_>,
110) -> Result<FsckResult, Error> {
111 let mut result = FsckResult::default();
112
113 if !options.quiet {
114 info!("Starting fsck");
115 }
116
117 let _guard = if options.no_lock { None } else { Some(filesystem.lock_commits().await) };
118
119 let mut fsck = Fsck::new(options);
120
121 let object_manager = filesystem.object_manager();
122 let super_block_header = filesystem.super_block_header();
123
124 let mut journal_checkpoint_ids: HashSet<u64> = HashSet::default();
127 journal_checkpoint_ids.insert(super_block_header.allocator_object_id);
128 journal_checkpoint_ids.insert(super_block_header.root_store_object_id);
129
130 let mut root_objects =
132 vec![super_block_header.root_store_object_id, super_block_header.journal_object_id];
133 root_objects.append(&mut object_manager.root_store().parent_objects());
134 fsck.verbose("Scanning root parent store...");
135 store_scanner::scan_store(
136 &fsck,
137 object_manager.root_parent_store().as_ref(),
138 &root_objects,
139 &mut result,
140 )
141 .await?;
142 fsck.verbose("Scanning root parent store done");
143
144 let root_store = &object_manager.root_store();
145 let mut root_store_root_objects = Vec::new();
146 root_store_root_objects.append(&mut vec![
147 super_block_header.allocator_object_id,
148 SuperBlockInstance::A.object_id(),
149 SuperBlockInstance::B.object_id(),
150 ]);
151 root_store_root_objects.append(&mut root_store.root_objects());
152
153 let root_volume = root_volume(filesystem.clone()).await?;
154 let volume_directory = root_volume.volume_directory();
155 let layer_set = volume_directory.store().tree().layer_set();
156 let mut merger = layer_set.merger();
157 let mut iter = volume_directory.iter(&mut merger).await?;
158
159 while let Some((_, store_id, _)) = iter.get() {
161 journal_checkpoint_ids.insert(store_id);
162 fsck.check_child_store_metadata(
163 filesystem.as_ref(),
164 store_id,
165 &mut root_store_root_objects,
166 )
167 .await?;
168 iter.advance().await?;
169 }
170
171 let allocator = filesystem.allocator();
172 root_store_root_objects.append(&mut allocator.parent_objects());
173
174 if fsck.options.do_slow_passes {
175 let layer_set = root_store.tree().immutable_layer_set();
177 fsck.verbose(format!("Checking {} layers for root store...", layer_set.layers.len()));
178 for layer in layer_set.layers {
179 if let Some(handle) = layer.handle() {
180 fsck.verbose(format!(
181 "Layer file {} for root_store is {} bytes",
182 handle.object_id(),
183 handle.get_size()
184 ));
185 }
186 fsck.check_layer_file_contents(
187 root_store.store_object_id(),
188 layer.handle().map(|h| h.object_id()).unwrap_or(INVALID_OBJECT_ID),
189 layer.clone(),
190 )
191 .await?;
192 }
193
194 let layer_set = allocator.tree().immutable_layer_set();
196 fsck.verbose(format!("Checking {} layers for allocator...", layer_set.layers.len()));
197 for layer in layer_set.layers {
198 if let Some(handle) = layer.handle() {
199 fsck.verbose(format!(
200 "Layer file {} for allocator is {} bytes",
201 handle.object_id(),
202 handle.get_size()
203 ));
204 }
205 fsck.check_layer_file_contents(
206 allocator.object_id(),
207 layer.handle().map(|h| h.object_id()).unwrap_or(INVALID_OBJECT_ID),
208 layer.clone(),
209 )
210 .await?;
211 }
212 fsck.verbose("Checking layers done");
213 }
214
215 fsck.verbose("Scanning root object store...");
217 store_scanner::scan_store(&fsck, root_store.as_ref(), &root_store_root_objects, &mut result)
218 .await?;
219 fsck.verbose("Scanning root object store done");
220
221 fsck.verbose("Verifying allocations...");
223 let mut store_ids = HashSet::default();
224 store_ids.insert(root_store.store_object_id());
225 store_ids.insert(object_manager.root_parent_store().store_object_id());
226 fsck.verify_allocations(filesystem.as_ref(), &store_ids, &mut result).await?;
227 fsck.verbose("Verifying allocations done");
228
229 for object_id in object_manager.journal_file_offsets().0.keys() {
234 if !journal_checkpoint_ids.contains(object_id) {
235 fsck.error(FsckError::UnexpectedJournalFileOffset(*object_id))?;
236 }
237 }
238
239 let errors = fsck.errors();
240 let warnings = fsck.warnings();
241 if errors > 0 || (fsck.options.fail_on_warning && warnings > 0) {
242 Err(anyhow!("Fsck encountered {} errors, {} warnings", errors, warnings))
243 } else {
244 if warnings > 0 {
245 warn!(count = warnings; "Fsck encountered warnings");
246 } else {
247 if !options.quiet {
248 info!("No issues detected");
249 }
250 }
251 Ok(result)
252 }
253}
254
255pub async fn fsck_volume(
259 filesystem: &FxFilesystem,
260 store_id: u64,
261 crypt: Option<Arc<dyn Crypt>>,
262) -> Result<FsckResult, Error> {
263 fsck_volume_with_options(filesystem, &FsckOptions::default(), store_id, crypt).await
264}
265
266pub async fn fsck_volume_with_options(
267 filesystem: &FxFilesystem,
268 options: &FsckOptions<'_>,
269 store_id: u64,
270 crypt: Option<Arc<dyn Crypt>>,
271) -> Result<FsckResult, Error> {
272 let mut result = FsckResult::default();
273 if !options.quiet {
274 info!(store_id:?; "Starting volume fsck");
275 }
276
277 let store = filesystem.object_manager().store(store_id).context("open_store failed").unwrap();
278
279 let _relock_guard;
280 if store.is_locked() {
281 let crypt = crypt.ok_or_else(|| anyhow!("Invalid key"))?;
282 store.unlock_read_only(crypt).await?;
283 _relock_guard = scopeguard::guard(store.clone(), |store| {
284 store.lock_read_only();
285 });
286 }
287
288 let _guard = if options.no_lock { None } else { Some(filesystem.lock_commits().await) };
289
290 let mut fsck = Fsck::new(options);
291 fsck.check_child_store(store.as_ref(), &mut result).await?;
292 let mut store_ids = HashSet::default();
293 store_ids.insert(store_id);
294 fsck.verify_allocations(filesystem, &store_ids, &mut result).await?;
295
296 let errors = fsck.errors();
297 let warnings = fsck.warnings();
298 if errors > 0 || (fsck.options.fail_on_warning && warnings > 0) {
299 Err(anyhow!("Volume fsck encountered {} errors, {} warnings", errors, warnings))
300 } else {
301 if warnings > 0 {
302 warn!(count = warnings; "Volume fsck encountered warnings");
303 } else {
304 if !options.quiet {
305 info!("No issues detected");
306 }
307 }
308 Ok(result)
309 }
310}
311
312struct Fsck<'a> {
313 options: &'a FsckOptions<'a>,
314 allocations: Arc<SkipListLayer<AllocatorKey, AllocatorValue>>,
316 errors: AtomicU64,
317 warnings: AtomicU64,
318}
319
320impl<'a> Fsck<'a> {
321 fn new(options: &'a FsckOptions<'a>) -> Self {
322 Fsck {
323 options,
324 allocations: SkipListLayer::new(2048),
326 errors: AtomicU64::new(0),
327 warnings: AtomicU64::new(0),
328 }
329 }
330
331 fn verbose(&self, message: impl AsRef<str>) {
333 if self.options.verbose {
334 info!(message = message.as_ref(); "fsck");
335 }
336 }
337
338 fn errors(&self) -> u64 {
339 self.errors.load(Ordering::Relaxed)
340 }
341
342 fn warnings(&self) -> u64 {
343 self.warnings.load(Ordering::Relaxed)
344 }
345
346 fn assert<V>(&self, res: Result<V, Error>, error: FsckFatal) -> Result<V, Error> {
347 if res.is_err() {
348 (self.options.on_error)(&FsckIssue::Fatal(error.clone()));
349 return Err(anyhow!("{:?}", error)).context(res.err().unwrap());
350 }
351 res
352 }
353
354 fn warning(&self, error: FsckWarning) -> Result<(), Error> {
355 (self.options.on_error)(&FsckIssue::Warning(error));
356 self.warnings.fetch_add(1, Ordering::Relaxed);
357 Ok(())
358 }
359
360 fn error(&self, error: FsckError) -> Result<(), Error> {
361 (self.options.on_error)(&FsckIssue::Error(error.clone()));
362 self.errors.fetch_add(1, Ordering::Relaxed);
363 if self.options.halt_on_error { Err(anyhow!("{:?}", error)) } else { Ok(()) }
364 }
365
366 fn fatal(&self, error: FsckFatal) -> Result<(), Error> {
367 (self.options.on_error)(&FsckIssue::Fatal(error.clone()));
368 Err(anyhow!("{:?}", error))
369 }
370
371 async fn check_child_store_metadata(
373 &mut self,
374 filesystem: &FxFilesystem,
375 store_id: u64,
376 root_store_root_objects: &mut Vec<u64>,
377 ) -> Result<(), Error> {
378 let root_store = filesystem.root_store();
379
380 let info = self.assert(
382 load_store_info(&root_store, store_id).await,
383 FsckFatal::MalformedStore(store_id),
384 )?;
385 root_store_root_objects.append(&mut info.parent_objects());
386 Ok(())
387 }
388
389 async fn check_child_store(
390 &mut self,
391 store: &ObjectStore,
392 result: &mut FsckResult,
393 ) -> Result<(), Error> {
394 let store_id = store.store_object_id();
395 if self.options.do_slow_passes {
396 let layer_set = store.tree().immutable_layer_set();
397 for layer in layer_set.layers {
398 let (layer_object_id, layer_size) = if let Some(h) = layer.handle() {
399 (h.object_id(), h.get_size())
400 } else {
401 (0, 0)
402 };
403 self.verbose(format!(
404 "Layer file {} for store {} is {} bytes",
405 layer_object_id, store_id, layer_size,
406 ));
407 self.check_layer_file_contents(store_id, layer_object_id, layer.clone()).await?
408 }
409 }
410
411 store_scanner::scan_store(self, store, &store.root_objects(), result)
412 .await
413 .context("scan_store failed")
414 }
415
416 async fn check_layer_file_contents<K: Key + LayerKey, V: Value>(
417 &self,
418 allocator_or_store_object_id: u64,
420 layer_file_object_id: u64,
421 layer: Arc<dyn Layer<K, V>>,
422 ) -> Result<(), Error> {
423 let mut iter: BoxedLayerIterator<'_, K, V> = self.assert(
424 layer.seek(Bound::Unbounded).await,
425 FsckFatal::MalformedLayerFile(allocator_or_store_object_id, layer_file_object_id),
426 )?;
427
428 let mut last_item: Option<Item<K, V>> = None;
429 while let Some(item) = iter.get() {
430 if let Some(last) = last_item {
431 if !last.key.cmp_upper_bound(&item.key).is_le() {
432 self.fatal(FsckFatal::MisOrderedLayerFile(
433 allocator_or_store_object_id,
434 layer_file_object_id,
435 ))?;
436 }
437 if last.key.overlaps(&item.key) {
438 self.fatal(FsckFatal::OverlappingKeysInLayerFile(
439 allocator_or_store_object_id,
440 layer_file_object_id,
441 item.into(),
442 last.as_item_ref().into(),
443 ))?;
444 }
445 }
446 if !layer.maybe_contains_key(item.key) {
447 self.fatal(FsckFatal::InvalidBloomFilter(
449 allocator_or_store_object_id,
450 layer_file_object_id,
451 item.into(),
452 ))?;
453 }
454 last_item = Some(item.cloned());
455 self.assert(
456 iter.advance().await,
457 FsckFatal::MalformedLayerFile(allocator_or_store_object_id, layer_file_object_id),
458 )?;
459 }
460 Ok(())
461 }
462
463 async fn verify_allocations(
465 &self,
466 filesystem: &FxFilesystem,
467 store_object_ids: &HashSet<u64>,
468 result: &mut FsckResult,
469 ) -> Result<(), Error> {
470 let allocator = filesystem.allocator();
471 let layer_set = allocator.tree().layer_set();
472 let mut merger = layer_set.merger();
473 let mut stored_allocations = CoalescingIterator::new(
474 allocator.filter(merger.query(Query::FullScan).await?, true).await?,
475 )
476 .await
477 .expect("filter failed");
478 let mut observed_allocations =
479 CoalescingIterator::new(self.allocations.seek(Bound::Unbounded).await?).await?;
480 let mut observed_owner_allocated_bytes = BTreeMap::new();
481 let mut extra_allocations: Vec<errors::Allocation> = vec![];
482 let bs = filesystem.block_size();
483 let mut previous_allocation_end = 0;
484 while let Some(allocation) = stored_allocations.get() {
485 if allocation.key.device_range.start % bs > 0
486 || allocation.key.device_range.end % bs > 0
487 {
488 self.error(FsckError::MisalignedAllocation(allocation.into()))?;
489 } else if allocation.key.device_range.start >= allocation.key.device_range.end {
490 self.error(FsckError::MalformedAllocation(allocation.into()))?;
491 }
492 let owner_object_id = match allocation.value {
493 AllocatorValue::None => INVALID_OBJECT_ID,
494 AllocatorValue::Abs { owner_object_id, .. } => *owner_object_id,
495 };
496 let r = &allocation.key.device_range;
497
498 if allocation.value != &AllocatorValue::None {
500 if r.start > previous_allocation_end {
501 let size = r.start - previous_allocation_end;
502 result.fragmentation.free_space
503 [FragmentationStats::get_histogram_bucket_for_size(size)] += 1;
504 }
505 previous_allocation_end = r.end;
506 }
507
508 *observed_owner_allocated_bytes.entry(owner_object_id).or_insert(0) += r.end - r.start;
509 if !store_object_ids.contains(&owner_object_id) {
510 if filesystem.object_manager().store(owner_object_id).is_none() {
511 self.error(FsckError::AllocationForNonexistentOwner(allocation.into()))?;
512 }
513 stored_allocations.advance().await?;
514 continue;
515 }
516 match observed_allocations.get() {
518 None => extra_allocations.push(allocation.into()),
519 Some(observed_allocation) => {
520 if allocation.key.device_range.end <= observed_allocation.key.device_range.start
521 {
522 extra_allocations.push(allocation.into());
523 stored_allocations.advance().await?;
524 continue;
525 }
526 if observed_allocation.key.device_range.end <= allocation.key.device_range.start
527 {
528 self.error(FsckError::MissingAllocation(observed_allocation.into()))?;
529 observed_allocations.advance().await?;
530 continue;
531 }
532 if allocation.key != observed_allocation.key
534 || allocation.value != observed_allocation.value
535 {
536 self.error(FsckError::AllocationMismatch(
537 observed_allocation.into(),
538 allocation.into(),
539 ))?;
540 stored_allocations.advance().await?;
541 continue;
542 }
543 }
544 }
545 try_join!(stored_allocations.advance(), observed_allocations.advance())?;
546 }
547 let device_size =
548 filesystem.device().block_count() * filesystem.device().block_size() as u64;
549 if previous_allocation_end < device_size {
550 let size = device_size - previous_allocation_end;
551 result.fragmentation.free_space
552 [FragmentationStats::get_histogram_bucket_for_size(size)] += 1;
553 }
554 while let Some(allocation) = observed_allocations.get() {
555 self.error(FsckError::MissingAllocation(allocation.into()))?;
556 observed_allocations.advance().await?;
557 continue;
558 }
559 let expected_allocated_bytes = observed_owner_allocated_bytes.values().sum::<u64>();
560 self.verbose(format!(
561 "Found {} bytes allocated (expected {} bytes). Total device size is {} bytes.",
562 allocator.get_allocated_bytes(),
563 expected_allocated_bytes,
564 device_size,
565 ));
566 if !extra_allocations.is_empty() {
567 self.error(FsckError::ExtraAllocations(extra_allocations))?;
568 }
569 let owner_allocated_bytes = allocator
573 .get_owner_allocated_bytes()
574 .into_iter()
575 .filter(|(_, v)| *v > 0)
576 .collect::<BTreeMap<_, _>>();
577 if expected_allocated_bytes != allocator.get_allocated_bytes()
578 || observed_owner_allocated_bytes.len() != owner_allocated_bytes.len()
579 || zip(observed_owner_allocated_bytes.iter(), owner_allocated_bytes.iter())
580 .filter(|((k1, v1), (k2, v2))| (*k1, *v1) != (*k2, *v2))
581 .count()
582 != 0
583 {
584 self.error(FsckError::AllocatedBytesMismatch(
585 observed_owner_allocated_bytes.iter().map(|(k, v)| (*k, *v)).collect(),
586 owner_allocated_bytes.iter().map(|(k, v)| (*k, *v)).collect(),
587 ))?;
588 }
589 for (k, v) in allocator.owner_byte_limits() {
590 if !owner_allocated_bytes.contains_key(&k) {
591 self.warning(FsckWarning::LimitForNonExistentStore(k, v))?;
592 }
593 }
594 Ok(())
595 }
596}