]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - scrub/phase6.c
xfs_scrub: remove moveon from main program
[thirdparty/xfsprogs-dev.git] / scrub / phase6.c
index 91e4443efca7c172eb035ad3589f430db202aa4b..88c719b3fd5f10ed36522e926bfad59a38da864d 100644 (file)
@@ -8,11 +8,11 @@
 #include <dirent.h>
 #include <sys/statvfs.h>
 #include "handle.h"
-#include "path.h"
-#include "workqueue.h"
+#include "libfrog/paths.h"
+#include "libfrog/workqueue.h"
 #include "xfs_scrub.h"
 #include "common.h"
-#include "bitmap.h"
+#include "libfrog/bitmap.h"
 #include "disk.h"
 #include "filemap.h"
 #include "fscounters.h"
@@ -45,7 +45,7 @@ struct media_verify_state {
 
 /* Find the fd for a given device identifier. */
 static struct read_verify_pool *
-xfs_dev_to_pool(
+dev_to_pool(
        struct scrub_ctx                *ctx,
        struct media_verify_state       *vs,
        dev_t                           dev)
@@ -61,7 +61,7 @@ xfs_dev_to_pool(
 
 /* Find the device major/minor for a given file descriptor. */
 static dev_t
-xfs_disk_to_dev(
+disk_to_dev(
        struct scrub_ctx        *ctx,
        struct disk             *disk)
 {
@@ -74,6 +74,27 @@ xfs_disk_to_dev(
        abort();
 }
 
+/* Find the incore bad blocks bitmap for a given disk. */
+static struct bitmap *
+bitmap_for_disk(
+       struct scrub_ctx                *ctx,
+       struct disk                     *disk,
+       struct media_verify_state       *vs)
+{
+       dev_t                           dev = disk_to_dev(ctx, disk);
+
+       if (dev == ctx->fsinfo.fs_datadev)
+               return vs->d_bad;
+       else if (dev == ctx->fsinfo.fs_rtdev)
+               return vs->r_bad;
+       return NULL;
+}
+
+struct disk_ioerr_report {
+       struct scrub_ctx        *ctx;
+       struct disk             *disk;
+};
+
 struct owner_decode {
        uint64_t                owner;
        const char              *descr;
@@ -95,7 +116,7 @@ static const struct owner_decode special_owners[] = {
 
 /* Decode a special owner. */
 static const char *
-xfs_decode_special_owner(
+decode_special_owner(
        uint64_t                        owner)
 {
        const struct owner_decode       *od = special_owners;
@@ -111,82 +132,151 @@ xfs_decode_special_owner(
 
 /* Routines to translate bad physical extents into file paths and offsets. */
 
-struct xfs_verify_error_info {
-       struct bitmap                   *d_bad;         /* bytes */
-       struct bitmap                   *r_bad;         /* bytes */
+struct badfile_report {
+       struct scrub_ctx                *ctx;
+       const char                      *descr;
+       struct media_verify_state       *vs;
+       struct file_bmap                *bmap;
 };
 
+/* Report on bad extents found during a media scan. */
+static int
+report_badfile(
+       uint64_t                start,
+       uint64_t                length,
+       void                    *arg)
+{
+       struct badfile_report   *br = arg;
+       unsigned long long      bad_offset;
+       unsigned long long      bad_length;
+
+       /* Clamp the bad region to the file mapping. */
+       if (start < br->bmap->bm_physical) {
+               length -= br->bmap->bm_physical - start;
+               start = br->bmap->bm_physical;
+       }
+       length = min(length, br->bmap->bm_length);
+
+       /* Figure out how far into the bmap is the bad mapping and report it. */
+       bad_offset = start - br->bmap->bm_physical;
+       bad_length = min(start + length,
+                        br->bmap->bm_physical + br->bmap->bm_length) - start;
+
+       str_unfixable_error(br->ctx, br->descr,
+_("media error at data offset %llu length %llu."),
+                       br->bmap->bm_offset + bad_offset, bad_length);
+       return 0;
+}
+
 /* Report if this extent overlaps a bad region. */
-static bool
-xfs_report_verify_inode_bmap(
+static int
+report_data_loss(
        struct scrub_ctx                *ctx,
-       const char                      *descr,
        int                             fd,
        int                             whichfork,
        struct fsxattr                  *fsx,
-       struct xfs_bmap                 *bmap,
+       struct file_bmap                *bmap,
        void                            *arg)
 {
-       struct xfs_verify_error_info    *vei = arg;
+       struct badfile_report           *br = arg;
+       struct media_verify_state       *vs = br->vs;
        struct bitmap                   *bmp;
 
+       br->bmap = bmap;
+
        /* Only report errors for real extents. */
        if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC))
-               return true;
+               return 0;
 
        if (fsx->fsx_xflags & FS_XFLAG_REALTIME)
-               bmp = vei->r_bad;
+               bmp = vs->r_bad;
        else
-               bmp = vei->d_bad;
+               bmp = vs->d_bad;
+
+       return bitmap_iterate_range(bmp, bmap->bm_physical, bmap->bm_length,
+                       report_badfile, br);
+}
+
+/* Report if the extended attribute data overlaps a bad region. */
+static int
+report_attr_loss(
+       struct scrub_ctx                *ctx,
+       int                             fd,
+       int                             whichfork,
+       struct fsxattr                  *fsx,
+       struct file_bmap                *bmap,
+       void                            *arg)
+{
+       struct badfile_report           *br = arg;
+       struct media_verify_state       *vs = br->vs;
+       struct bitmap                   *bmp = vs->d_bad;
+
+       /* Complain about attr fork extents that don't look right. */
+       if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC)) {
+               str_info(ctx, br->descr,
+_("found unexpected unwritten/delalloc attr fork extent."));
+               return 0;
+       }
 
-       if (!bitmap_test(bmp, bmap->bm_physical, bmap->bm_length))
-               return true;
+       if (fsx->fsx_xflags & FS_XFLAG_REALTIME) {
+               str_info(ctx, br->descr,
+_("found unexpected realtime attr fork extent."));
+               return 0;
+       }
 
-       str_error(ctx, descr,
-_("offset %llu failed read verification."), bmap->bm_offset);
-       return true;
+       if (bitmap_test(bmp, bmap->bm_physical, bmap->bm_length))
+               str_corrupt(ctx, br->descr,
+_("media error in extended attribute data."));
+
+       return 0;
 }
 
 /* Iterate the extent mappings of a file to report errors. */
-static bool
-xfs_report_verify_fd(
+static int
+report_fd_loss(
        struct scrub_ctx                *ctx,
        const char                      *descr,
        int                             fd,
        void                            *arg)
 {
-       struct xfs_bmap                 key = {0};
-       bool                            moveon;
+       struct badfile_report           br = {
+               .ctx                    = ctx,
+               .vs                     = arg,
+               .descr                  = descr,
+       };
+       struct file_bmap                key = {0};
+       int                             ret;
 
        /* data fork */
-       moveon = xfs_iterate_filemaps(ctx, descr, fd, XFS_DATA_FORK, &key,
-                       xfs_report_verify_inode_bmap, arg);
-       if (!moveon)
-               return false;
+       ret = scrub_iterate_filemaps(ctx, fd, XFS_DATA_FORK, &key,
+                       report_data_loss, &br);
+       if (ret) {
+               str_liberror(ctx, ret, descr);
+               return ret;
+       }
 
        /* attr fork */
-       moveon = xfs_iterate_filemaps(ctx, descr, fd, XFS_ATTR_FORK, &key,
-                       xfs_report_verify_inode_bmap, arg);
-       if (!moveon)
-               return false;
-       return true;
+       ret = scrub_iterate_filemaps(ctx, fd, XFS_ATTR_FORK, &key,
+                       report_attr_loss, &br);
+       if (ret) {
+               str_liberror(ctx, ret, descr);
+               return ret;
+       }
+
+       return 0;
 }
 
 /* Report read verify errors in unlinked (but still open) files. */
 static int
-xfs_report_verify_inode(
+report_inode_loss(
        struct scrub_ctx                *ctx,
        struct xfs_handle               *handle,
-       struct xfs_bstat                *bstat,
+       struct xfs_bulkstat             *bstat,
        void                            *arg)
 {
        char                            descr[DESCR_BUFSZ];
-       bool                            moveon;
        int                             fd;
-       int                             error;
-
-       snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (unlinked)"),
-                       (uint64_t)bstat->bs_ino);
+       int                             error, err2;
 
        /* Ignore linked files and things we can't open. */
        if (bstat->bs_nlink != 0)
@@ -194,8 +284,11 @@ xfs_report_verify_inode(
        if (!S_ISREG(bstat->bs_mode) && !S_ISDIR(bstat->bs_mode))
                return 0;
 
+       scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ,
+                       bstat->bs_ino, bstat->bs_gen, _("(unlinked)"));
+
        /* Try to open the inode. */
-       fd = xfs_open_handle(handle);
+       fd = scrub_open_handle(handle);
        if (fd < 0) {
                error = errno;
                if (error == ESTALE)
@@ -207,31 +300,32 @@ _("Disappeared during read error reporting."));
        }
 
        /* Go find the badness. */
-       moveon = xfs_report_verify_fd(ctx, descr, fd, arg);
-       error = close(fd);
-       if (error)
+       error = report_fd_loss(ctx, descr, fd, arg);
+
+       err2 = close(fd);
+       if (err2)
                str_errno(ctx, descr);
 
-       return moveon ? 0 : XFS_ITERATE_INODES_ABORT;
+       return error;
 }
 
 /* Scan a directory for matches in the read verify error list. */
-static bool
-xfs_report_verify_dir(
+static int
+report_dir_loss(
        struct scrub_ctx        *ctx,
        const char              *path,
        int                     dir_fd,
        void                    *arg)
 {
-       return xfs_report_verify_fd(ctx, path, dir_fd, arg);
+       return report_fd_loss(ctx, path, dir_fd, arg);
 }
 
 /*
  * Scan the inode associated with a directory entry for matches with
  * the read verify error list.
  */
-static bool
-xfs_report_verify_dirent(
+static int
+report_dirent_loss(
        struct scrub_ctx        *ctx,
        const char              *path,
        int                     dir_fd,
@@ -239,17 +333,16 @@ xfs_report_verify_dirent(
        struct stat             *sb,
        void                    *arg)
 {
-       bool                    moveon;
        int                     fd;
-       int                     error;
+       int                     error, err2;
 
        /* Ignore things we can't open. */
        if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode))
-               return true;
+               return 0;
 
        /* Ignore . and .. */
        if (!strcmp(".", dirent->d_name) || !strcmp("..", dirent->d_name))
-               return true;
+               return 0;
 
        /*
         * If we were given a dirent, open the associated file under
@@ -258,70 +351,63 @@ xfs_report_verify_dirent(
         */
        fd = openat(dir_fd, dirent->d_name,
                        O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
-       if (fd < 0)
-               return true;
+       if (fd < 0) {
+               if (errno == ENOENT)
+                       return 0;
+               str_errno(ctx, path);
+               return errno;
+       }
 
        /* Go find the badness. */
-       moveon = xfs_report_verify_fd(ctx, path, fd, arg);
-       if (moveon)
-               goto out;
+       error = report_fd_loss(ctx, path, fd, arg);
 
-out:
-       error = close(fd);
-       if (error)
+       err2 = close(fd);
+       if (err2)
                str_errno(ctx, path);
-       return moveon;
-}
-
-/* Given bad extent lists for the data & rtdev, find bad files. */
-static bool
-xfs_report_verify_errors(
-       struct scrub_ctx                *ctx,
-       struct bitmap                   *d_bad,
-       struct bitmap                   *r_bad)
-{
-       struct xfs_verify_error_info    vei;
-       bool                            moveon;
-
-       vei.d_bad = d_bad;
-       vei.r_bad = r_bad;
-
-       /* Scan the directory tree to get file paths. */
-       moveon = scan_fs_tree(ctx, xfs_report_verify_dir,
-                       xfs_report_verify_dirent, &vei);
-       if (!moveon)
-               return false;
+       if (!error && err2)
+               error = err2;
 
-       /* Scan for unlinked files. */
-       return xfs_scan_all_inodes(ctx, xfs_report_verify_inode, &vei);
+       return error;
 }
 
-/* Report an IO error resulting from read-verify based off getfsmap. */
-static bool
-xfs_check_rmap_error_report(
+/* Use a fsmap to report metadata lost to a media error. */
+static int
+report_ioerr_fsmap(
        struct scrub_ctx        *ctx,
-       const char              *descr,
        struct fsmap            *map,
        void                    *arg)
 {
        const char              *type;
-       char                    buf[32];
+       char                    buf[DESCR_BUFSZ];
        uint64_t                err_physical = *(uint64_t *)arg;
        uint64_t                err_off;
 
+       /* Don't care about unwritten extents. */
+       if (map->fmr_flags & FMR_OF_PREALLOC)
+               return 0;
+
        if (err_physical > map->fmr_physical)
                err_off = err_physical - map->fmr_physical;
        else
                err_off = 0;
 
-       snprintf(buf, 32, _("disk offset %"PRIu64),
-                       (uint64_t)BTOBB(map->fmr_physical + err_off));
-
+       /* Report special owners */
        if (map->fmr_flags & FMR_OF_SPECIAL_OWNER) {
-               type = xfs_decode_special_owner(map->fmr_owner);
-               str_error(ctx, buf,
-_("%s failed read verification."),
-                               type);
+               snprintf(buf, DESCR_BUFSZ, _("disk offset %"PRIu64),
+                               (uint64_t)map->fmr_physical + err_off);
+               type = decode_special_owner(map->fmr_owner);
+               str_corrupt(ctx, buf, _("media error in %s."), type);
+       }
+
+       /* Report extent maps */
+       if (map->fmr_flags & FMR_OF_EXTENT_MAP) {
+               bool            attr = (map->fmr_flags & FMR_OF_ATTR_FORK);
+
+               scrub_render_ino_descr(ctx, buf, DESCR_BUFSZ,
+                               map->fmr_owner, 0, " %s",
+                               attr ? _("extended attribute") :
+                                      _("file data"));
+               str_corrupt(ctx, buf, _("media error in extent map"));
        }
 
        /*
@@ -330,49 +416,24 @@ _("%s failed read verification."),
         * to find the bad file's pathname.
         */
 
-       return true;
+       return 0;
 }
 
 /*
- * Remember a read error for later, and see if rmap will tell us about the
- * owner ahead of time.
+ * For a range of bad blocks, visit each space mapping that overlaps the bad
+ * range so that we can report lost metadata.
  */
-static void
-xfs_check_rmap_ioerr(
-       struct scrub_ctx                *ctx,
-       struct disk                     *disk,
+static int
+report_ioerr(
        uint64_t                        start,
        uint64_t                        length,
-       int                             error,
        void                            *arg)
 {
        struct fsmap                    keys[2];
-       char                            descr[DESCR_BUFSZ];
-       struct media_verify_state       *vs = arg;
-       struct bitmap                   *tree;
+       struct disk_ioerr_report        *dioerr = arg;
        dev_t                           dev;
-       bool                            moveon;
-
-       dev = xfs_disk_to_dev(ctx, disk);
-
-       /*
-        * If we don't have parent pointers, save the bad extent for
-        * later rescanning.
-        */
-       if (dev == ctx->fsinfo.fs_datadev)
-               tree = vs->d_bad;
-       else if (dev == ctx->fsinfo.fs_rtdev)
-               tree = vs->r_bad;
-       else
-               tree = NULL;
-       if (tree) {
-               moveon = bitmap_set(tree, start, length);
-               if (!moveon)
-                       str_errno(ctx, ctx->mntpoint);
-       }
 
-       snprintf(descr, DESCR_BUFSZ, _("dev %d:%d ioerr @ %"PRIu64":%"PRIu64" "),
-                       major(dev), minor(dev), start, length);
+       dev = disk_to_dev(dioerr->ctx, dioerr->disk);
 
        /* Go figure out which blocks are bad from the fsmap. */
        memset(keys, 0, sizeof(struct fsmap) * 2);
@@ -383,22 +444,72 @@ xfs_check_rmap_ioerr(
        (keys + 1)->fmr_owner = ULLONG_MAX;
        (keys + 1)->fmr_offset = ULLONG_MAX;
        (keys + 1)->fmr_flags = UINT_MAX;
-       xfs_iterate_fsmap(ctx, descr, keys, xfs_check_rmap_error_report,
+       return scrub_iterate_fsmap(dioerr->ctx, keys, report_ioerr_fsmap,
                        &start);
 }
 
+/* Report all the media errors found on a disk. */
+static int
+report_disk_ioerrs(
+       struct scrub_ctx                *ctx,
+       struct disk                     *disk,
+       struct media_verify_state       *vs)
+{
+       struct disk_ioerr_report        dioerr = {
+               .ctx                    = ctx,
+               .disk                   = disk,
+       };
+       struct bitmap                   *tree;
+
+       if (!disk)
+               return 0;
+       tree = bitmap_for_disk(ctx, disk, vs);
+       if (!tree)
+               return 0;
+       return bitmap_iterate(tree, report_ioerr, &dioerr);
+}
+
+/* Given bad extent lists for the data & rtdev, find bad files. */
+static int
+report_all_media_errors(
+       struct scrub_ctx                *ctx,
+       struct media_verify_state       *vs)
+{
+       int                             ret;
+
+       ret = report_disk_ioerrs(ctx, ctx->datadev, vs);
+       if (ret) {
+               str_liberror(ctx, ret, _("walking datadev io errors"));
+               return ret;
+       }
+
+       ret = report_disk_ioerrs(ctx, ctx->rtdev, vs);
+       if (ret) {
+               str_liberror(ctx, ret, _("walking rtdev io errors"));
+               return ret;
+       }
+
+       /* Scan the directory tree to get file paths. */
+       ret = scan_fs_tree(ctx, report_dir_loss, report_dirent_loss, vs);
+       if (ret)
+               return ret;
+
+       /* Scan for unlinked files. */
+       return scrub_scan_all_inodes(ctx, report_inode_loss, vs);
+}
+
 /* Schedule a read-verify of a (data block) extent. */
-static bool
-xfs_check_rmap(
+static int
+check_rmap(
        struct scrub_ctx                *ctx,
-       const char                      *descr,
        struct fsmap                    *map,
        void                            *arg)
 {
        struct media_verify_state       *vs = arg;
        struct read_verify_pool         *rvp;
+       int                             ret;
 
-       rvp = xfs_dev_to_pool(ctx, vs, map->fmr_device);
+       rvp = dev_to_pool(ctx, vs, map->fmr_device);
 
        dbg_printf("rmap dev %d:%d phys %"PRIu64" owner %"PRId64
                        " offset %"PRIu64" len %"PRIu64" flags 0x%x\n",
@@ -420,37 +531,76 @@ xfs_check_rmap(
         */
        if (map->fmr_flags & (FMR_OF_PREALLOC | FMR_OF_ATTR_FORK |
                              FMR_OF_EXTENT_MAP | FMR_OF_SPECIAL_OWNER))
-               goto out;
+               return 0;
 
        /* XXX: Filter out directory data blocks. */
 
        /* Schedule the read verify command for (eventual) running. */
-       read_verify_schedule_io(rvp, map->fmr_physical, map->fmr_length, vs);
-
-out:
-       /* Is this the last extent?  Fire off the read. */
-       if (map->fmr_flags & FMR_OF_LAST)
-               read_verify_force_io(rvp);
+       ret = read_verify_schedule_io(rvp, map->fmr_physical, map->fmr_length,
+                       vs);
+       if (ret) {
+               str_liberror(ctx, ret, _("scheduling media verify command"));
+               return ret;
+       }
 
-       return true;
+       return 0;
 }
 
 /* Wait for read/verify actions to finish, then return # bytes checked. */
-static uint64_t
+static int
 clean_pool(
-       struct read_verify_pool *rvp)
+       struct read_verify_pool *rvp,
+       unsigned long long      *bytes_checked)
 {
-       uint64_t                ret;
+       uint64_t                pool_checked;
+       int                     ret;
 
        if (!rvp)
                return 0;
 
-       read_verify_pool_flush(rvp);
-       ret = read_verify_bytes(rvp);
+       ret = read_verify_force_io(rvp);
+       if (ret)
+               return ret;
+
+       ret = read_verify_pool_flush(rvp);
+       if (ret)
+               goto out_destroy;
+
+       ret = read_verify_bytes(rvp, &pool_checked);
+       if (ret)
+               goto out_destroy;
+
+       *bytes_checked += pool_checked;
+out_destroy:
        read_verify_pool_destroy(rvp);
        return ret;
 }
 
+/* Remember a media error for later. */
+static void
+remember_ioerr(
+       struct scrub_ctx                *ctx,
+       struct disk                     *disk,
+       uint64_t                        start,
+       uint64_t                        length,
+       int                             error,
+       void                            *arg)
+{
+       struct media_verify_state       *vs = arg;
+       struct bitmap                   *tree;
+       int                             ret;
+
+       tree = bitmap_for_disk(ctx, disk, vs);
+       if (!tree) {
+               str_liberror(ctx, ENOENT, _("finding bad block bitmap"));
+               return;
+       }
+
+       ret = bitmap_set(tree, start, length);
+       if (ret)
+               str_liberror(ctx, ret, _("setting bad block bitmap"));
+}
+
 /*
  * Read verify all the file data blocks in a filesystem.  Since XFS doesn't
  * do data checksums, we trust that the underlying storage will pass back
@@ -459,90 +609,107 @@ clean_pool(
  * scan the extent maps of the entire fs tree to figure (and the unlinked
  * inodes) out which files are now broken.
  */
-bool
-xfs_scan_blocks(
+int
+phase6_func(
        struct scrub_ctx                *ctx)
 {
        struct media_verify_state       vs = { NULL };
-       bool                            moveon;
+       int                             ret, ret2, ret3;
 
-       moveon = bitmap_init(&vs.d_bad);
-       if (!moveon) {
-               str_errno(ctx, ctx->mntpoint);
-               goto out;
+       ret = bitmap_alloc(&vs.d_bad);
+       if (ret) {
+               str_liberror(ctx, ret, _("creating datadev badblock bitmap"));
+               return ret;
        }
 
-       moveon = bitmap_init(&vs.r_bad);
-       if (!moveon) {
-               str_errno(ctx, ctx->mntpoint);
+       ret = bitmap_alloc(&vs.r_bad);
+       if (ret) {
+               str_liberror(ctx, ret, _("creating realtime badblock bitmap"));
                goto out_dbad;
        }
 
-       vs.rvp_data = read_verify_pool_init(ctx, ctx->datadev,
-                       ctx->geo.blocksize, xfs_check_rmap_ioerr,
-                       scrub_nproc(ctx));
-       if (!vs.rvp_data) {
-               moveon = false;
-               str_info(ctx, ctx->mntpoint,
-_("Could not create data device media verifier."));
+       ret = read_verify_pool_alloc(ctx, ctx->datadev,
+                       ctx->mnt.fsgeom.blocksize, remember_ioerr,
+                       scrub_nproc(ctx), &vs.rvp_data);
+       if (ret) {
+               str_liberror(ctx, ret, _("creating datadev media verifier"));
                goto out_rbad;
        }
        if (ctx->logdev) {
-               vs.rvp_log = read_verify_pool_init(ctx, ctx->logdev,
-                               ctx->geo.blocksize, xfs_check_rmap_ioerr,
-                               scrub_nproc(ctx));
-               if (!vs.rvp_log) {
-                       moveon = false;
-                       str_info(ctx, ctx->mntpoint,
-       _("Could not create log device media verifier."));
+               ret = read_verify_pool_alloc(ctx, ctx->logdev,
+                               ctx->mnt.fsgeom.blocksize, remember_ioerr,
+                               scrub_nproc(ctx), &vs.rvp_log);
+               if (ret) {
+                       str_liberror(ctx, ret,
+                                       _("creating logdev media verifier"));
                        goto out_datapool;
                }
        }
        if (ctx->rtdev) {
-               vs.rvp_realtime = read_verify_pool_init(ctx, ctx->rtdev,
-                               ctx->geo.blocksize, xfs_check_rmap_ioerr,
-                               scrub_nproc(ctx));
-               if (!vs.rvp_realtime) {
-                       moveon = false;
-                       str_info(ctx, ctx->mntpoint,
-       _("Could not create realtime device media verifier."));
+               ret = read_verify_pool_alloc(ctx, ctx->rtdev,
+                               ctx->mnt.fsgeom.blocksize, remember_ioerr,
+                               scrub_nproc(ctx), &vs.rvp_realtime);
+               if (ret) {
+                       str_liberror(ctx, ret,
+                                       _("creating rtdev media verifier"));
                        goto out_logpool;
                }
        }
-       moveon = xfs_scan_all_spacemaps(ctx, xfs_check_rmap, &vs);
-       if (!moveon)
+       ret = scrub_scan_all_spacemaps(ctx, check_rmap, &vs);
+       if (ret)
                goto out_rtpool;
-       ctx->bytes_checked += clean_pool(vs.rvp_data);
-       ctx->bytes_checked += clean_pool(vs.rvp_log);
-       ctx->bytes_checked += clean_pool(vs.rvp_realtime);
+
+       ret = clean_pool(vs.rvp_data, &ctx->bytes_checked);
+       if (ret)
+               str_liberror(ctx, ret, _("flushing datadev verify pool"));
+
+       ret2 = clean_pool(vs.rvp_log, &ctx->bytes_checked);
+       if (ret2)
+               str_liberror(ctx, ret2, _("flushing logdev verify pool"));
+
+       ret3 = clean_pool(vs.rvp_realtime, &ctx->bytes_checked);
+       if (ret3)
+               str_liberror(ctx, ret3, _("flushing rtdev verify pool"));
+
+       /*
+        * If the verify flush didn't work or we found no bad blocks, we're
+        * done!  No errors detected.
+        */
+       if (ret || ret2 || ret3)
+               goto out_rbad;
+       if (bitmap_empty(vs.d_bad) && bitmap_empty(vs.r_bad))
+               goto out_rbad;
 
        /* Scan the whole dir tree to see what matches the bad extents. */
-       if (!bitmap_empty(vs.d_bad) || !bitmap_empty(vs.r_bad))
-               moveon = xfs_report_verify_errors(ctx, vs.d_bad, vs.r_bad);
+       ret = report_all_media_errors(ctx, &vs);
 
        bitmap_free(&vs.r_bad);
        bitmap_free(&vs.d_bad);
-       return moveon;
+       return ret;
 
 out_rtpool:
-       if (vs.rvp_realtime)
+       if (vs.rvp_realtime) {
+               read_verify_pool_abort(vs.rvp_realtime);
                read_verify_pool_destroy(vs.rvp_realtime);
+       }
 out_logpool:
-       if (vs.rvp_log)
+       if (vs.rvp_log) {
+               read_verify_pool_abort(vs.rvp_log);
                read_verify_pool_destroy(vs.rvp_log);
+       }
 out_datapool:
+       read_verify_pool_abort(vs.rvp_data);
        read_verify_pool_destroy(vs.rvp_data);
 out_rbad:
        bitmap_free(&vs.r_bad);
 out_dbad:
        bitmap_free(&vs.d_bad);
-out:
-       return moveon;
+       return ret;
 }
 
 /* Estimate how much work we're going to do. */
-bool
-xfs_estimate_verify_work(
+int
+phase6_estimate(
        struct scrub_ctx        *ctx,
        uint64_t                *items,
        unsigned int            *nr_threads,
@@ -554,15 +721,18 @@ xfs_estimate_verify_work(
        unsigned long long      r_bfree;
        unsigned long long      f_files;
        unsigned long long      f_free;
-       bool                    moveon;
+       int                     ret;
 
-       moveon = xfs_scan_estimate_blocks(ctx, &d_blocks, &d_bfree,
+       ret = scrub_scan_estimate_blocks(ctx, &d_blocks, &d_bfree,
                                &r_blocks, &r_bfree, &f_files, &f_free);
-       if (!moveon)
-               return moveon;
+       if (ret) {
+               str_liberror(ctx, ret, _("estimating verify work"));
+               return ret;
+       }
 
-       *items = ((d_blocks - d_bfree) + (r_blocks - r_bfree)) << ctx->blocklog;
+       *items = cvt_off_fsb_to_b(&ctx->mnt,
+                       (d_blocks - d_bfree) + (r_blocks - r_bfree));
        *nr_threads = disk_heads(ctx->datadev);
        *rshift = 20;
-       return moveon;
+       return 0;
 }