]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/phase6.c
xfs_scrub: improve reporting of file metadata media errors
[thirdparty/xfsprogs-dev.git] / scrub / phase6.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
b364a9c0
DW
2/*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
b364a9c0 4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
b364a9c0 5 */
a440f877 6#include "xfs.h"
b364a9c0 7#include <stdint.h>
b364a9c0
DW
8#include <dirent.h>
9#include <sys/statvfs.h>
b364a9c0 10#include "handle.h"
42b4c8e8 11#include "libfrog/paths.h"
56598728 12#include "libfrog/workqueue.h"
b364a9c0
DW
13#include "xfs_scrub.h"
14#include "common.h"
a58400ed 15#include "libfrog/bitmap.h"
b364a9c0
DW
16#include "disk.h"
17#include "filemap.h"
ed60d210 18#include "fscounters.h"
b364a9c0
DW
19#include "inodes.h"
20#include "read_verify.h"
21#include "spacemap.h"
22#include "vfs.h"
23
24/*
25 * Phase 6: Verify data file integrity.
26 *
27 * Identify potential data block extents with GETFSMAP, then feed those
28 * extents to the read-verify pool to get the verify commands batched,
29 * issued, and (if there are problems) reported back to us. If there
30 * are errors, we'll record the bad regions and (if available) use rmap
31 * to tell us if metadata are now corrupt. Otherwise, we'll scan the
32 * whole directory tree looking for files that overlap the bad regions
33 * and report the paths of the now corrupt files.
34 */
35
f1bb1696
DW
36/* Verify disk blocks with GETFSMAP */
37
557f98d7 38struct media_verify_state {
f1bb1696
DW
39 struct read_verify_pool *rvp_data;
40 struct read_verify_pool *rvp_log;
41 struct read_verify_pool *rvp_realtime;
42 struct bitmap *d_bad; /* bytes */
43 struct bitmap *r_bad; /* bytes */
44};
45
b364a9c0 46/* Find the fd for a given device identifier. */
f1bb1696
DW
47static struct read_verify_pool *
48xfs_dev_to_pool(
49 struct scrub_ctx *ctx,
557f98d7 50 struct media_verify_state *vs,
f1bb1696 51 dev_t dev)
b364a9c0
DW
52{
53 if (dev == ctx->fsinfo.fs_datadev)
557f98d7 54 return vs->rvp_data;
b364a9c0 55 else if (dev == ctx->fsinfo.fs_logdev)
557f98d7 56 return vs->rvp_log;
b364a9c0 57 else if (dev == ctx->fsinfo.fs_rtdev)
557f98d7 58 return vs->rvp_realtime;
b364a9c0
DW
59 abort();
60}
61
62/* Find the device major/minor for a given file descriptor. */
63static dev_t
64xfs_disk_to_dev(
65 struct scrub_ctx *ctx,
66 struct disk *disk)
67{
68 if (disk == ctx->datadev)
69 return ctx->fsinfo.fs_datadev;
70 else if (disk == ctx->logdev)
71 return ctx->fsinfo.fs_logdev;
72 else if (disk == ctx->rtdev)
73 return ctx->fsinfo.fs_rtdev;
74 abort();
75}
76
77struct owner_decode {
78 uint64_t owner;
79 const char *descr;
80};
81
82static const struct owner_decode special_owners[] = {
83 {XFS_FMR_OWN_FREE, "free space"},
84 {XFS_FMR_OWN_UNKNOWN, "unknown owner"},
85 {XFS_FMR_OWN_FS, "static FS metadata"},
86 {XFS_FMR_OWN_LOG, "journalling log"},
87 {XFS_FMR_OWN_AG, "per-AG metadata"},
88 {XFS_FMR_OWN_INOBT, "inode btree blocks"},
89 {XFS_FMR_OWN_INODES, "inodes"},
90 {XFS_FMR_OWN_REFC, "refcount btree"},
91 {XFS_FMR_OWN_COW, "CoW staging"},
92 {XFS_FMR_OWN_DEFECTIVE, "bad blocks"},
93 {0, NULL},
94};
95
96/* Decode a special owner. */
97static const char *
98xfs_decode_special_owner(
99 uint64_t owner)
100{
101 const struct owner_decode *od = special_owners;
102
103 while (od->descr) {
104 if (od->owner == owner)
105 return od->descr;
106 od++;
107 }
108
109 return NULL;
110}
111
112/* Routines to translate bad physical extents into file paths and offsets. */
113
ed953d26
DW
114struct badfile_report {
115 struct scrub_ctx *ctx;
116 const char *descr;
117 struct xfs_bmap *bmap;
118};
119
120/* Report on bad extents found during a media scan. */
121static int
122report_badfile(
123 uint64_t start,
124 uint64_t length,
125 void *arg)
126{
127 struct badfile_report *br = arg;
128 unsigned long long bad_offset;
129 unsigned long long bad_length;
130
131 /* Clamp the bad region to the file mapping. */
132 if (start < br->bmap->bm_physical) {
133 length -= br->bmap->bm_physical - start;
134 start = br->bmap->bm_physical;
135 }
136 length = min(length, br->bmap->bm_length);
137
138 /* Figure out how far into the bmap is the bad mapping and report it. */
139 bad_offset = start - br->bmap->bm_physical;
140 bad_length = min(start + length,
141 br->bmap->bm_physical + br->bmap->bm_length) - start;
142
143 str_error(br->ctx, br->descr,
144_("media error at data offset %llu length %llu."),
145 br->bmap->bm_offset + bad_offset, bad_length);
146 return 0;
147}
148
b364a9c0
DW
149/* Report if this extent overlaps a bad region. */
150static bool
663e02a0 151report_data_loss(
b364a9c0
DW
152 struct scrub_ctx *ctx,
153 const char *descr,
154 int fd,
155 int whichfork,
156 struct fsxattr *fsx,
157 struct xfs_bmap *bmap,
158 void *arg)
159{
ed953d26
DW
160 struct badfile_report br = {
161 .ctx = ctx,
162 .descr = descr,
163 .bmap = bmap,
164 };
ed5f9cc7 165 struct media_verify_state *vs = arg;
b364a9c0 166 struct bitmap *bmp;
ed953d26 167 int ret;
b364a9c0
DW
168
169 /* Only report errors for real extents. */
170 if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC))
171 return true;
172
173 if (fsx->fsx_xflags & FS_XFLAG_REALTIME)
ed5f9cc7 174 bmp = vs->r_bad;
b364a9c0 175 else
ed5f9cc7 176 bmp = vs->d_bad;
b364a9c0 177
ed953d26
DW
178 ret = bitmap_iterate_range(bmp, bmap->bm_physical, bmap->bm_length,
179 report_badfile, &br);
180 if (ret) {
181 str_liberror(ctx, ret, descr);
182 return false;
183 }
b364a9c0
DW
184 return true;
185}
186
663e02a0
DW
187/* Report if the extended attribute data overlaps a bad region. */
188static bool
189report_attr_loss(
190 struct scrub_ctx *ctx,
191 const char *descr,
192 int fd,
193 int whichfork,
194 struct fsxattr *fsx,
195 struct xfs_bmap *bmap,
196 void *arg)
197{
198 struct media_verify_state *vs = arg;
199 struct bitmap *bmp = vs->d_bad;
200
201 /* Complain about attr fork extents that don't look right. */
202 if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC)) {
203 str_info(ctx, descr,
204_("found unexpected unwritten/delalloc attr fork extent."));
205 return true;
206 }
207
208 if (fsx->fsx_xflags & FS_XFLAG_REALTIME) {
209 str_info(ctx, descr,
210_("found unexpected realtime attr fork extent."));
211 return true;
212 }
213
214 if (bitmap_test(bmp, bmap->bm_physical, bmap->bm_length))
215 str_error(ctx, descr,
216_("media error in extended attribute data."));
217
218 return true;
219}
220
b364a9c0
DW
221/* Iterate the extent mappings of a file to report errors. */
222static bool
223xfs_report_verify_fd(
224 struct scrub_ctx *ctx,
225 const char *descr,
226 int fd,
227 void *arg)
228{
229 struct xfs_bmap key = {0};
230 bool moveon;
231
232 /* data fork */
233 moveon = xfs_iterate_filemaps(ctx, descr, fd, XFS_DATA_FORK, &key,
663e02a0 234 report_data_loss, arg);
b364a9c0
DW
235 if (!moveon)
236 return false;
237
238 /* attr fork */
663e02a0
DW
239 return xfs_iterate_filemaps(ctx, descr, fd, XFS_ATTR_FORK, &key,
240 report_attr_loss, arg);
b364a9c0
DW
241}
242
243/* Report read verify errors in unlinked (but still open) files. */
244static int
245xfs_report_verify_inode(
246 struct scrub_ctx *ctx,
247 struct xfs_handle *handle,
4cca629d 248 struct xfs_bulkstat *bstat,
b364a9c0
DW
249 void *arg)
250{
251 char descr[DESCR_BUFSZ];
b364a9c0
DW
252 bool moveon;
253 int fd;
254 int error;
255
b364a9c0
DW
256 /* Ignore linked files and things we can't open. */
257 if (bstat->bs_nlink != 0)
258 return 0;
259 if (!S_ISREG(bstat->bs_mode) && !S_ISDIR(bstat->bs_mode))
260 return 0;
261
15589f0a
DW
262 scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ,
263 bstat->bs_ino, bstat->bs_gen, _("(unlinked)"));
264
b364a9c0
DW
265 /* Try to open the inode. */
266 fd = xfs_open_handle(handle);
267 if (fd < 0) {
268 error = errno;
269 if (error == ESTALE)
270 return error;
271
bb5dbd06
DW
272 str_info(ctx, descr,
273_("Disappeared during read error reporting."));
b364a9c0
DW
274 return error;
275 }
276
277 /* Go find the badness. */
278 moveon = xfs_report_verify_fd(ctx, descr, fd, arg);
6c05cc5d
DW
279 error = close(fd);
280 if (error)
281 str_errno(ctx, descr);
b364a9c0
DW
282
283 return moveon ? 0 : XFS_ITERATE_INODES_ABORT;
284}
285
286/* Scan a directory for matches in the read verify error list. */
287static bool
288xfs_report_verify_dir(
289 struct scrub_ctx *ctx,
290 const char *path,
291 int dir_fd,
292 void *arg)
293{
294 return xfs_report_verify_fd(ctx, path, dir_fd, arg);
295}
296
297/*
298 * Scan the inode associated with a directory entry for matches with
299 * the read verify error list.
300 */
301static bool
302xfs_report_verify_dirent(
303 struct scrub_ctx *ctx,
304 const char *path,
305 int dir_fd,
306 struct dirent *dirent,
307 struct stat *sb,
308 void *arg)
309{
310 bool moveon;
311 int fd;
6c05cc5d 312 int error;
b364a9c0
DW
313
314 /* Ignore things we can't open. */
315 if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode))
316 return true;
317
318 /* Ignore . and .. */
319 if (!strcmp(".", dirent->d_name) || !strcmp("..", dirent->d_name))
320 return true;
321
322 /*
323 * If we were given a dirent, open the associated file under
324 * dir_fd for badblocks scanning. If dirent is NULL, then it's
325 * the directory itself we want to scan.
326 */
327 fd = openat(dir_fd, dirent->d_name,
328 O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
329 if (fd < 0)
330 return true;
331
332 /* Go find the badness. */
333 moveon = xfs_report_verify_fd(ctx, path, fd, arg);
334 if (moveon)
335 goto out;
336
337out:
6c05cc5d
DW
338 error = close(fd);
339 if (error)
340 str_errno(ctx, path);
b364a9c0
DW
341 return moveon;
342}
343
344/* Given bad extent lists for the data & rtdev, find bad files. */
345static bool
346xfs_report_verify_errors(
347 struct scrub_ctx *ctx,
ed5f9cc7 348 struct media_verify_state *vs)
b364a9c0 349{
b364a9c0
DW
350 bool moveon;
351
b364a9c0
DW
352 /* Scan the directory tree to get file paths. */
353 moveon = scan_fs_tree(ctx, xfs_report_verify_dir,
ed5f9cc7 354 xfs_report_verify_dirent, vs);
b364a9c0
DW
355 if (!moveon)
356 return false;
357
358 /* Scan for unlinked files. */
ed5f9cc7 359 return xfs_scan_all_inodes(ctx, xfs_report_verify_inode, vs);
b364a9c0
DW
360}
361
b364a9c0
DW
362/* Report an IO error resulting from read-verify based off getfsmap. */
363static bool
364xfs_check_rmap_error_report(
365 struct scrub_ctx *ctx,
366 const char *descr,
367 struct fsmap *map,
368 void *arg)
369{
370 const char *type;
f1f5fd3a 371 char buf[DESCR_BUFSZ];
b364a9c0
DW
372 uint64_t err_physical = *(uint64_t *)arg;
373 uint64_t err_off;
374
375 if (err_physical > map->fmr_physical)
376 err_off = err_physical - map->fmr_physical;
377 else
378 err_off = 0;
379
f1f5fd3a 380 /* Report special owners */
b364a9c0 381 if (map->fmr_flags & FMR_OF_SPECIAL_OWNER) {
f1f5fd3a
DW
382 snprintf(buf, DESCR_BUFSZ, _("disk offset %"PRIu64),
383 (uint64_t)map->fmr_physical + err_off);
b364a9c0 384 type = xfs_decode_special_owner(map->fmr_owner);
f1f5fd3a 385 str_error(ctx, buf, _("media error in %s."), type);
b364a9c0
DW
386 }
387
02d0069e
DW
388 /* Report extent maps */
389 if (map->fmr_flags & FMR_OF_EXTENT_MAP) {
390 bool attr = (map->fmr_flags & FMR_OF_ATTR_FORK);
391
392 scrub_render_ino_descr(ctx, buf, DESCR_BUFSZ,
393 map->fmr_owner, 0, " %s",
394 attr ? _("extended attribute") :
395 _("file data"));
396 str_error(ctx, buf, _("media error in extent map"));
397 }
398
b364a9c0
DW
399 /*
400 * XXX: If we had a getparent() call we could report IO errors
401 * efficiently. Until then, we'll have to scan the dir tree
402 * to find the bad file's pathname.
403 */
404
405 return true;
406}
407
408/*
409 * Remember a read error for later, and see if rmap will tell us about the
410 * owner ahead of time.
411 */
412static void
413xfs_check_rmap_ioerr(
414 struct scrub_ctx *ctx,
415 struct disk *disk,
416 uint64_t start,
417 uint64_t length,
418 int error,
419 void *arg)
420{
421 struct fsmap keys[2];
422 char descr[DESCR_BUFSZ];
557f98d7 423 struct media_verify_state *vs = arg;
b364a9c0
DW
424 struct bitmap *tree;
425 dev_t dev;
233fabee 426 int ret;
b364a9c0
DW
427
428 dev = xfs_disk_to_dev(ctx, disk);
429
430 /*
431 * If we don't have parent pointers, save the bad extent for
432 * later rescanning.
433 */
434 if (dev == ctx->fsinfo.fs_datadev)
557f98d7 435 tree = vs->d_bad;
b364a9c0 436 else if (dev == ctx->fsinfo.fs_rtdev)
557f98d7 437 tree = vs->r_bad;
b364a9c0
DW
438 else
439 tree = NULL;
440 if (tree) {
233fabee
DW
441 ret = bitmap_set(tree, start, length);
442 if (ret)
443 str_liberror(ctx, ret, _("setting bad block bitmap"));
b364a9c0
DW
444 }
445
446 snprintf(descr, DESCR_BUFSZ, _("dev %d:%d ioerr @ %"PRIu64":%"PRIu64" "),
447 major(dev), minor(dev), start, length);
448
449 /* Go figure out which blocks are bad from the fsmap. */
450 memset(keys, 0, sizeof(struct fsmap) * 2);
451 keys->fmr_device = dev;
452 keys->fmr_physical = start;
453 (keys + 1)->fmr_device = dev;
454 (keys + 1)->fmr_physical = start + length - 1;
455 (keys + 1)->fmr_owner = ULLONG_MAX;
456 (keys + 1)->fmr_offset = ULLONG_MAX;
457 (keys + 1)->fmr_flags = UINT_MAX;
458 xfs_iterate_fsmap(ctx, descr, keys, xfs_check_rmap_error_report,
459 &start);
460}
461
462/* Schedule a read-verify of a (data block) extent. */
463static bool
464xfs_check_rmap(
465 struct scrub_ctx *ctx,
466 const char *descr,
467 struct fsmap *map,
468 void *arg)
469{
557f98d7 470 struct media_verify_state *vs = arg;
f1bb1696 471 struct read_verify_pool *rvp;
8cab77d3 472 int ret;
f1bb1696 473
557f98d7 474 rvp = xfs_dev_to_pool(ctx, vs, map->fmr_device);
b364a9c0
DW
475
476 dbg_printf("rmap dev %d:%d phys %"PRIu64" owner %"PRId64
477 " offset %"PRIu64" len %"PRIu64" flags 0x%x\n",
478 major(map->fmr_device), minor(map->fmr_device),
479 (uint64_t)map->fmr_physical, (int64_t)map->fmr_owner,
480 (uint64_t)map->fmr_offset, (uint64_t)map->fmr_length,
481 map->fmr_flags);
482
483 /* "Unknown" extents should be verified; they could be data. */
484 if ((map->fmr_flags & FMR_OF_SPECIAL_OWNER) &&
485 map->fmr_owner == XFS_FMR_OWN_UNKNOWN)
486 map->fmr_flags &= ~FMR_OF_SPECIAL_OWNER;
487
488 /*
489 * We only care about read-verifying data extents that have been
490 * written to disk. This means we can skip "special" owners
491 * (metadata), xattr blocks, unwritten extents, and extent maps.
492 * These should all get checked elsewhere in the scrubber.
493 */
494 if (map->fmr_flags & (FMR_OF_PREALLOC | FMR_OF_ATTR_FORK |
495 FMR_OF_EXTENT_MAP | FMR_OF_SPECIAL_OWNER))
22d658ec 496 return true;
b364a9c0
DW
497
498 /* XXX: Filter out directory data blocks. */
499
500 /* Schedule the read verify command for (eventual) running. */
8cab77d3
DW
501 ret = read_verify_schedule_io(rvp, map->fmr_physical, map->fmr_length,
502 vs);
503 if (ret) {
504 str_liberror(ctx, ret, descr);
505 return false;
506 }
b364a9c0 507
b364a9c0
DW
508 return true;
509}
510
f1bb1696 511/* Wait for read/verify actions to finish, then return # bytes checked. */
8cab77d3 512static int
f1bb1696 513clean_pool(
8cab77d3
DW
514 struct read_verify_pool *rvp,
515 unsigned long long *bytes_checked)
f1bb1696 516{
8cab77d3
DW
517 uint64_t pool_checked;
518 int ret;
f1bb1696
DW
519
520 if (!rvp)
521 return 0;
522
22d658ec
DW
523 ret = read_verify_force_io(rvp);
524 if (ret)
525 return ret;
526
8cab77d3
DW
527 ret = read_verify_pool_flush(rvp);
528 if (ret)
529 goto out_destroy;
530
531 ret = read_verify_bytes(rvp, &pool_checked);
532 if (ret)
533 goto out_destroy;
534
535 *bytes_checked += pool_checked;
536out_destroy:
f1bb1696
DW
537 read_verify_pool_destroy(rvp);
538 return ret;
539}
540
b364a9c0
DW
541/*
542 * Read verify all the file data blocks in a filesystem. Since XFS doesn't
543 * do data checksums, we trust that the underlying storage will pass back
544 * an IO error if it can't retrieve whatever we previously stored there.
545 * If we hit an IO error, we'll record the bad blocks in a bitmap and then
546 * scan the extent maps of the entire fs tree to figure (and the unlinked
547 * inodes) out which files are now broken.
548 */
549bool
550xfs_scan_blocks(
551 struct scrub_ctx *ctx)
552{
557f98d7 553 struct media_verify_state vs = { NULL };
93ab49dd 554 bool moveon = false;
233fabee 555 int ret;
b364a9c0 556
233fabee
DW
557 ret = bitmap_alloc(&vs.d_bad);
558 if (ret) {
559 str_liberror(ctx, ret, _("creating datadev badblock bitmap"));
41c08606 560 goto out;
b364a9c0
DW
561 }
562
233fabee
DW
563 ret = bitmap_alloc(&vs.r_bad);
564 if (ret) {
565 str_liberror(ctx, ret, _("creating realtime badblock bitmap"));
b364a9c0
DW
566 goto out_dbad;
567 }
568
8cab77d3 569 ret = read_verify_pool_alloc(ctx, ctx->datadev,
3f9efb2e 570 ctx->mnt.fsgeom.blocksize, xfs_check_rmap_ioerr,
8cab77d3
DW
571 scrub_nproc(ctx), &vs.rvp_data);
572 if (ret) {
573 str_liberror(ctx, ret, _("creating datadev media verifier"));
b364a9c0
DW
574 goto out_rbad;
575 }
f1bb1696 576 if (ctx->logdev) {
8cab77d3 577 ret = read_verify_pool_alloc(ctx, ctx->logdev,
3f9efb2e 578 ctx->mnt.fsgeom.blocksize, xfs_check_rmap_ioerr,
8cab77d3
DW
579 scrub_nproc(ctx), &vs.rvp_log);
580 if (ret) {
581 str_liberror(ctx, ret,
582 _("creating logdev media verifier"));
f1bb1696
DW
583 goto out_datapool;
584 }
585 }
586 if (ctx->rtdev) {
8cab77d3 587 ret = read_verify_pool_alloc(ctx, ctx->rtdev,
3f9efb2e 588 ctx->mnt.fsgeom.blocksize, xfs_check_rmap_ioerr,
8cab77d3
DW
589 scrub_nproc(ctx), &vs.rvp_realtime);
590 if (ret) {
591 str_liberror(ctx, ret,
592 _("creating rtdev media verifier"));
f1bb1696
DW
593 goto out_logpool;
594 }
595 }
557f98d7 596 moveon = xfs_scan_all_spacemaps(ctx, xfs_check_rmap, &vs);
b364a9c0 597 if (!moveon)
f1bb1696 598 goto out_rtpool;
8cab77d3
DW
599
600 ret = clean_pool(vs.rvp_data, &ctx->bytes_checked);
601 if (ret) {
602 str_liberror(ctx, ret, _("flushing datadev verify pool"));
603 moveon = false;
604 }
605
606 ret = clean_pool(vs.rvp_log, &ctx->bytes_checked);
607 if (ret) {
608 str_liberror(ctx, ret, _("flushing logdev verify pool"));
609 moveon = false;
610 }
611
612 ret = clean_pool(vs.rvp_realtime, &ctx->bytes_checked);
613 if (ret) {
614 str_liberror(ctx, ret, _("flushing rtdev verify pool"));
615 moveon = false;
616 }
b364a9c0
DW
617
618 /* Scan the whole dir tree to see what matches the bad extents. */
8cab77d3 619 if (moveon && (!bitmap_empty(vs.d_bad) || !bitmap_empty(vs.r_bad)))
ed5f9cc7 620 moveon = xfs_report_verify_errors(ctx, &vs);
b364a9c0 621
557f98d7
DW
622 bitmap_free(&vs.r_bad);
623 bitmap_free(&vs.d_bad);
b364a9c0
DW
624 return moveon;
625
f1bb1696 626out_rtpool:
7668d01d 627 if (vs.rvp_realtime) {
4cd869e5 628 read_verify_pool_abort(vs.rvp_realtime);
557f98d7 629 read_verify_pool_destroy(vs.rvp_realtime);
7668d01d 630 }
f1bb1696 631out_logpool:
7668d01d 632 if (vs.rvp_log) {
4cd869e5 633 read_verify_pool_abort(vs.rvp_log);
557f98d7 634 read_verify_pool_destroy(vs.rvp_log);
7668d01d 635 }
f1bb1696 636out_datapool:
4cd869e5 637 read_verify_pool_abort(vs.rvp_data);
557f98d7 638 read_verify_pool_destroy(vs.rvp_data);
b364a9c0 639out_rbad:
557f98d7 640 bitmap_free(&vs.r_bad);
b364a9c0 641out_dbad:
557f98d7 642 bitmap_free(&vs.d_bad);
41c08606 643out:
b364a9c0
DW
644 return moveon;
645}
ed60d210
DW
646
647/* Estimate how much work we're going to do. */
648bool
649xfs_estimate_verify_work(
650 struct scrub_ctx *ctx,
651 uint64_t *items,
652 unsigned int *nr_threads,
653 int *rshift)
654{
655 unsigned long long d_blocks;
656 unsigned long long d_bfree;
657 unsigned long long r_blocks;
658 unsigned long long r_bfree;
659 unsigned long long f_files;
660 unsigned long long f_free;
661 bool moveon;
662
663 moveon = xfs_scan_estimate_blocks(ctx, &d_blocks, &d_bfree,
664 &r_blocks, &r_bfree, &f_files, &f_free);
665 if (!moveon)
666 return moveon;
667
a749451c
DW
668 *items = cvt_off_fsb_to_b(&ctx->mnt,
669 (d_blocks - d_bfree) + (r_blocks - r_bfree));
ed60d210
DW
670 *nr_threads = disk_heads(ctx->datadev);
671 *rshift = 20;
672 return moveon;
673}