]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/phase4.c
xfs_growfs: allow mounted device node as argument
[thirdparty/xfsprogs-dev.git] / repair / phase4.c
index 5e341167f513e33f376b26bea6f2c5973b430d7d..e1ba778fdf56c10d985228285693eb318c389451 100644 (file)
@@ -1,36 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
- * 
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * 
- * Further, this software is distributed without any warranty that it is
- * free of the rightful claim of any third person regarding infringement
- * or the like.  Any license provided herein, whether implied or
- * otherwise, applies only to this software file.  Patent licenses, if
- * any, provided herein do not apply to combinations of this program with
- * other software, or any other product whatsoever.
- * 
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston MA 02111-1307, USA.
- * 
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
- * Mountain View, CA  94043, or:
- * 
- * http://www.sgi.com 
- * 
- * For further information regarding this notice, see: 
- * 
- * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
  */
 
-#include <libxfs.h>
+#include "libxfs.h"
+#include "threads.h"
+#include "prefetch.h"
 #include "avl.h"
 #include "globals.h"
 #include "agheader.h"
 #include "protos.h"
 #include "err_protos.h"
 #include "dinode.h"
-#include "dir.h"
 #include "bmap.h"
 #include "versions.h"
 #include "dir2.h"
+#include "progress.h"
+#include "slab.h"
+#include "rmap.h"
 
-
-/* ARGSUSED */
-int
-lf_block_delete_orphanage(xfs_mount_t          *mp,
-                       xfs_ino_t               ino,
-                       xfs_dir_leafblock_t     *leaf,
-                       int                     *dirty,
-                       xfs_buf_t               *rootino_bp,
-                       int                     *rbuf_dirty)
-{
-       xfs_dir_leaf_entry_t    *entry;
-       xfs_dinode_t            *dino;
-       xfs_buf_t               *bp;
-       ino_tree_node_t         *irec;
-       xfs_ino_t               lino;
-       xfs_dir_leaf_name_t     *namest;
-       xfs_agino_t             agino;
-       xfs_agnumber_t          agno;
-       xfs_agino_t             root_agino;
-       xfs_agnumber_t          root_agno;
-       int                     i;
-       int                     ino_offset;
-       int                     ino_dirty;
-       int                     use_rbuf;
-       int                     len;
-       char                    fname[MAXNAMELEN + 1];
-       int                     res;
-
-       entry = &leaf->entries[0];
-       *dirty = 0;
-       use_rbuf = 0;
-       res = 0;
-       root_agno = XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino);
-       root_agino = XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino);
-
-       for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); entry++, i++) {
-               namest = XFS_DIR_LEAF_NAMESTRUCT(leaf,
-                       INT_GET(entry->nameidx, ARCH_CONVERT));
-               XFS_DIR_SF_GET_DIRINO_ARCH(&namest->inumber, &lino, ARCH_CONVERT);
-               bcopy(namest->name, fname, entry->namelen);
-               fname[entry->namelen] = '\0';
-
-               if (fname[0] != '/' && !strcmp(fname, ORPHANAGE))  {
-                       agino = XFS_INO_TO_AGINO(mp, lino);
-                       agno = XFS_INO_TO_AGNO(mp, lino);
-
-                       old_orphanage_ino = lino;
-
-                       irec = find_inode_rec(agno, agino);
-
-                       /*
-                        * if the orphange inode is in the tree,
-                        * get it, clear it, and mark it free.
-                        * the inodes in the orphanage will get
-                        * reattached to the new orphanage.
-                        */
-                       if (irec != NULL)  {
-                               ino_offset = agino - irec->ino_startnum;
-
-                               /*
-                                * check if we have to use the root inode
-                                * buffer or read one in ourselves.  Note
-                                * that the root inode is always the first
-                                * inode of the chunk that it's in so there
-                                * are two possible cases where lost+found
-                                * might be in the same buffer as the root
-                                * inode.  One case is a large block
-                                * filesystem where the two inodes are
-                                * in different inode chunks but wind
-                                * up in the same block (multiple chunks
-                                * per block) and the second case (one or
-                                * more blocks per chunk) is where the two
-                                * inodes are in the same chunk. Note that
-                                * inodes are allocated on disk in units
-                                * of MAX(XFS_INODES_PER_CHUNK,sb_inopblock).
-                                */
-                               if (XFS_INO_TO_FSB(mp, mp->m_sb.sb_rootino)
-                                               == XFS_INO_TO_FSB(mp, lino) ||
-                                   (agno == root_agno &&
-                                    agino < root_agino + XFS_INODES_PER_CHUNK)) {
-                                       use_rbuf = 1;
-                                       bp = rootino_bp;
-                                       dino = XFS_MAKE_IPTR(mp, bp, agino -
-                                               XFS_INO_TO_AGINO(mp,
-                                                       mp->m_sb.sb_rootino));
-                               } else {
-                                       len = (int)XFS_FSB_TO_BB(mp,
-                                               MAX(1, XFS_INODES_PER_CHUNK/
-                                                       inodes_per_block));
-                                       bp = libxfs_readbuf(mp->m_dev,
-                                               XFS_AGB_TO_DADDR(mp, agno,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                               irec->ino_startnum)),
-                                               len, 0);
-                                       if (!bp)
-                                               do_error("couldn't read %s inode %llu\n",
-                                                       ORPHANAGE, lino);
-
-                                       /*
-                                        * get the agbno containing the first
-                                        * inode in the chunk.  In multi-block
-                                        * chunks, this gets us the offset
-                                        * relative to the beginning of a
-                                        * properly aligned buffer.  In
-                                        * multi-chunk blocks, this gets us
-                                        * the correct block number.  Then
-                                        * turn the block number back into
-                                        * an agino and calculate the offset
-                                        * from there to feed to make the iptr.
-                                        * the last term in effect rounds down
-                                        * to the first agino in the buffer.
-                                        */
-                                       dino = XFS_MAKE_IPTR(mp, bp,
-                                               agino - XFS_OFFBNO_TO_AGINO(mp,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                       irec->ino_startnum),
-                                                       0));
-                               }
-
-                               do_warn("        - clearing existing \"%s\" inode\n",
-                                       ORPHANAGE);
-
-                               ino_dirty = clear_dinode(mp, dino, lino);
-
-                               if (!use_rbuf)  {
-                                       ASSERT(ino_dirty == 0 ||
-                                               (ino_dirty && !no_modify));
-
-                                       if (ino_dirty && !no_modify)
-                                               libxfs_writebuf(bp, 0);
-                                       else
-                                               libxfs_putbuf(bp);
-                               } else  {
-                                       if (ino_dirty)
-                                               *rbuf_dirty = 1;
-                               }
-                               
-                               if (inode_isadir(irec, ino_offset))
-                                       clear_inode_isadir(irec, ino_offset);
-
-                               set_inode_free(irec, ino_offset);
-                       }
-
-                       /*
-                        * regardless of whether the inode num is good or
-                        * bad, mark the entry to be junked so the
-                        * createname in phase 6 will succeed.
-                        */
-                       namest->name[0] = '/';
-                       *dirty = 1;
-                       do_warn("        - marking entry \"%s\" to be deleted\n", fname);
-                       res++;
-               }
-       }
-
-       return(res);
-}
-
-int
-longform_delete_orphanage(xfs_mount_t  *mp,
-                       xfs_ino_t       ino,
-                       xfs_dinode_t    *dino,
-                       xfs_buf_t       *rootino_bp,
-                       int             *rbuf_dirty)
-{
-       xfs_dir_leafblock_t     *leaf;
-       xfs_buf_t               *bp;
-       xfs_dfsbno_t            fsbno;
-       xfs_dablk_t             da_bno;
-       int                     dirty;
-       int                     res;
-
-       da_bno = 0;
-       *rbuf_dirty = 0;
-
-       if ((fsbno = get_first_dblock_fsbno(mp, ino, dino)) == NULLDFSBNO)  {
-               do_error("couldn't map first leaf block of directory inode %llu\n", ino);
-               exit(1);
-       }
-
-       /*
-        * cycle through the entire directory looking to delete
-        * every "lost+found" entry.  make sure to catch duplicate
-        * entries.
-        *
-        * We could probably speed this up by doing a smarter lookup
-        * to get us to the first block that contains the hashvalue
-        * of "lost+found" but what the heck.  that would require a
-        * double lookup for each level.  and how big can '/' get???
-        * It's probably not worth it.
-        */
-       res = 0;
-
-       do {
-               if (fsbno == NULLDFSBNO)
-                       break;
-               bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
-                                       XFS_FSB_TO_BB(mp, 1), 0);
-               if (!bp) {
-                       do_error("can't read block %u (fsbno %llu) for directory inode "
-                               "%llu\n", da_bno, fsbno, ino);
-                       exit(1);
-               }
-
-               leaf = (xfs_dir_leafblock_t *)XFS_BUF_PTR(bp);
-
-               if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC) {
-                       do_error("bad magic # (0x%x) for directory leaf block "
-                               "(bno %u fsbno %llu)\n",
-                               INT_GET(leaf->hdr.info.magic, ARCH_CONVERT),
-                               da_bno, fsbno);
-                       exit(1);
-               }
-
-               da_bno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT);
-
-               res += lf_block_delete_orphanage(mp, ino, leaf, &dirty,
-                                       rootino_bp, rbuf_dirty);
-
-               ASSERT(dirty == 0 || (dirty && !no_modify));
-
-               if (dirty && !no_modify)
-                       libxfs_writebuf(bp, 0);
-               else
-                       libxfs_putbuf(bp);
-
-               if (da_bno != 0)
-                       fsbno = get_bmapi(mp, dino, ino, da_bno, XFS_DATA_FORK);
-
-       } while (da_bno != 0);
-
-       return(res);
-}
-
-/*
- * returns 1 if a deletion happened, 0 otherwise.
- */
-/* ARGSUSED */
-int
-shortform_delete_orphanage(xfs_mount_t *mp,
-                       xfs_ino_t       ino,
-                       xfs_dinode_t    *root_dino,
-                       xfs_buf_t       *rootino_bp,
-                       int             *ino_dirty)
-{
-       xfs_dir_shortform_t     *sf;
-       xfs_dinode_t            *dino;
-       xfs_dir_sf_entry_t      *sf_entry, *next_sfe, *tmp_sfe;
-       xfs_buf_t               *bp;
-       xfs_ino_t               lino;
-       xfs_agino_t             agino;
-       xfs_agino_t             root_agino;
-       int                     max_size;
-       xfs_agnumber_t          agno;
-       xfs_agnumber_t          root_agno;
-       int                     ino_dir_size;
-       ino_tree_node_t         *irec;
-       int                     ino_offset;
-       int                     i;
-       int                     dirty;
-       int                     tmp_len;
-       int                     tmp_elen;
-       int                     len;
-       int                     use_rbuf;
-       char                    fname[MAXNAMELEN + 1];
-       int                     res;
-
-       sf = &root_dino->di_u.di_dirsf;
-       *ino_dirty = 0;
-       res = 0;
-       irec = NULL;
-       ino_dir_size = INT_GET(root_dino->di_core.di_size, ARCH_CONVERT);
-       max_size = XFS_DFORK_DSIZE_ARCH(root_dino, mp, ARCH_CONVERT);
-       use_rbuf = 0;
-       root_agno = XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino);
-       root_agino = XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino);
-
-       /*
-        * run through entries looking for "lost+found".
-        */
-       sf_entry = next_sfe = &sf->list[0];
-       for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT) && ino_dir_size >
-                       (__psint_t)next_sfe - (__psint_t)sf; i++)  {
-               tmp_sfe = NULL;
-               sf_entry = next_sfe;
-               XFS_DIR_SF_GET_DIRINO_ARCH(&sf_entry->inumber, &lino, ARCH_CONVERT);
-               bcopy(sf_entry->name, fname, sf_entry->namelen);
-               fname[sf_entry->namelen] = '\0';
-
-               if (!strcmp(ORPHANAGE, fname))  {
-                       agno = XFS_INO_TO_AGNO(mp, lino);
-                       agino = XFS_INO_TO_AGINO(mp, lino);
-
-                       irec = find_inode_rec(agno, agino);
-
-                       /*
-                        * if the orphange inode is in the tree,
-                        * get it, clear it, and mark it free.
-                        * the inodes in the orphanage will get
-                        * reattached to the new orphanage.
-                        */
-                       if (irec != NULL) {
-                               do_warn("        - clearing existing \"%s\" inode\n",
-                                       ORPHANAGE);
-
-                               ino_offset = agino - irec->ino_startnum;
-
-                               /*
-                                * check if we have to use the root inode
-                                * buffer or read one in ourselves.  Note
-                                * that the root inode is always the first
-                                * inode of the chunk that it's in so there
-                                * are two possible cases where lost+found
-                                * might be in the same buffer as the root
-                                * inode.  One case is a large block
-                                * filesystem where the two inodes are
-                                * in different inode chunks but wind
-                                * up in the same block (multiple chunks
-                                * per block) and the second case (one or
-                                * more blocks per chunk) is where the two
-                                * inodes are in the same chunk. Note that
-                                * inodes are allocated on disk in units
-                                * of MAX(XFS_INODES_PER_CHUNK,sb_inopblock).
-                                */
-                               if (XFS_INO_TO_FSB(mp, mp->m_sb.sb_rootino)
-                                               == XFS_INO_TO_FSB(mp, lino) ||
-                                   (agno == root_agno &&
-                                    agino < root_agino + XFS_INODES_PER_CHUNK)) {
-                                       use_rbuf = 1;
-                                       bp = rootino_bp;
-
-                                       dino = XFS_MAKE_IPTR(mp, bp, agino -
-                                               XFS_INO_TO_AGINO(mp,
-                                                       mp->m_sb.sb_rootino));
-                               } else {
-                                       len = (int)XFS_FSB_TO_BB(mp,
-                                               MAX(1, XFS_INODES_PER_CHUNK/
-                                                       inodes_per_block));
-                                       bp = libxfs_readbuf(mp->m_dev,
-                                               XFS_AGB_TO_DADDR(mp, agno,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                               irec->ino_startnum)),
-                                               len, 0);
-                                       if (!bp)
-                                               do_error("could not read %s inode "
-                                                       "%llu\n", ORPHANAGE, lino);
-                                       /*
-                                        * get the agbno containing the first
-                                        * inode in the chunk.  In multi-block
-                                        * chunks, this gets us the offset
-                                        * relative to the beginning of a
-                                        * properly aligned buffer.  In
-                                        * multi-chunk blocks, this gets us
-                                        * the correct block number.  Then
-                                        * turn the block number back into
-                                        * an agino and calculate the offset
-                                        * from there to feed to make the iptr.
-                                        * the last term in effect rounds down
-                                        * to the first agino in the buffer.
-                                        */
-                                       dino = XFS_MAKE_IPTR(mp, bp,
-                                               agino - XFS_OFFBNO_TO_AGINO(mp,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                       irec->ino_startnum),
-                                                       0));
-                               }
-
-                               dirty = clear_dinode(mp, dino, lino);
-
-                               ASSERT(dirty == 0 || (dirty && !no_modify));
-
-                               /*
-                                * if we read the lost+found inode in to
-                                * it, get rid of it here.  if the lost+found
-                                * inode is in the root inode buffer, the
-                                * buffer will be marked dirty anyway since
-                                * the lost+found entry in the root inode is
-                                * also being deleted which makes the root
-                                * inode buffer automatically dirty.
-                                */
-                               if (!use_rbuf)  {
-                                       dino = NULL;
-                                       if (dirty && !no_modify)
-                                               libxfs_writebuf(bp, 0);
-                                       else
-                                               libxfs_putbuf(bp);
-                               }
-
-                               if (inode_isadir(irec, ino_offset))
-                                       clear_inode_isadir(irec, ino_offset);
-
-                               set_inode_free(irec, ino_offset);
-                       }
-
-                       do_warn("        - deleting existing \"%s\" entry\n",
-                               ORPHANAGE);
-
-                       /*
-                        * note -- exactly the same deletion code as in
-                        * process_shortform_dir()
-                        */
-                       tmp_elen = XFS_DIR_SF_ENTSIZE_BYENTRY(sf_entry);
-                       INT_MOD(root_dino->di_core.di_size, ARCH_CONVERT, -(tmp_elen));
-
-                       tmp_sfe = (xfs_dir_sf_entry_t *)
-                               ((__psint_t) sf_entry + tmp_elen);
-                       tmp_len = max_size - ((__psint_t) tmp_sfe
-                                       - (__psint_t) sf);
-
-                       memmove(sf_entry, tmp_sfe, tmp_len);
-
-                       INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
-
-                       bzero((void *) ((__psint_t) sf_entry + tmp_len),
-                               tmp_elen);
-
-                       /*
-                        * set the tmp value to the current
-                        * pointer so we'll process the entry
-                        * we just moved up
-                        */
-                       tmp_sfe = sf_entry;
-
-                       /*
-                        * WARNING:  drop the index i by one
-                        * so it matches the decremented count for
-                        * accurate comparisons in the loop test.
-                        * mark root inode as dirty to make deletion
-                        * permanent.
-                        */
-                       i--;
-
-                       *ino_dirty = 1;
-                       res++;
-
-               }
-               next_sfe = (tmp_sfe == NULL)
-                       ? (xfs_dir_sf_entry_t *) ((__psint_t) sf_entry +
-                               XFS_DIR_SF_ENTSIZE_BYENTRY(sf_entry))
-                       : tmp_sfe;
-       }
-
-       return(res);
-}
-
-/* ARGSUSED */
-int
-lf2_block_delete_orphanage(xfs_mount_t         *mp,
-                       xfs_ino_t               ino,
-                       xfs_dir2_data_t         *data,
-                       int                     *dirty,
-                       xfs_buf_t               *rootino_bp,
-                       int                     *rbuf_dirty)
-{
-       xfs_dinode_t            *dino;
-       xfs_buf_t               *bp;
-       ino_tree_node_t         *irec;
-       xfs_ino_t               lino;
-       xfs_agino_t             agino;
-       xfs_agnumber_t          agno;
-       xfs_agino_t             root_agino;
-       xfs_agnumber_t          root_agno;
-       int                     ino_offset;
-       int                     ino_dirty;
-       int                     use_rbuf;
-       int                     len;
-       char                    fname[MAXNAMELEN + 1];
-       int                     res;
-       char                    *ptr;
-       char                    *endptr;
-       xfs_dir2_block_tail_t   *btp;
-       xfs_dir2_data_entry_t   *dep;
-       xfs_dir2_data_unused_t  *dup;
-
-       ptr = (char *)data->u;
-       if (INT_GET(data->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
-               btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)data);
-               endptr = (char *)XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
-       } else
-               endptr = (char *)data + mp->m_dirblksize;
-       *dirty = 0;
-       use_rbuf = 0;
-       res = 0;
-       root_agno = XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino);
-       root_agino = XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino);
-
-       while (ptr < endptr) {
-               dup = (xfs_dir2_data_unused_t *)ptr;
-               if (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) {
-                       if (ptr + INT_GET(dup->length, ARCH_CONVERT) > endptr ||
-                               INT_GET(dup->length, ARCH_CONVERT) == 0 ||
-                               (INT_GET(dup->length, ARCH_CONVERT) &
-                                               (XFS_DIR2_DATA_ALIGN - 1)))
-                               break;
-                       ptr += INT_GET(dup->length, ARCH_CONVERT);
-                       continue;
-               }
-               dep = (xfs_dir2_data_entry_t *)ptr;
-               lino = INT_GET(dep->inumber, ARCH_CONVERT);
-               bcopy(dep->name, fname, dep->namelen);
-               fname[dep->namelen] = '\0';
-
-               if (fname[0] != '/' && !strcmp(fname, ORPHANAGE))  {
-                       agino = XFS_INO_TO_AGINO(mp, lino);
-                       agno = XFS_INO_TO_AGNO(mp, lino);
-
-                       old_orphanage_ino = lino;
-
-                       irec = find_inode_rec(agno, agino);
-
-                       /*
-                        * if the orphange inode is in the tree,
-                        * get it, clear it, and mark it free.
-                        * the inodes in the orphanage will get
-                        * reattached to the new orphanage.
-                        */
-                       if (irec != NULL)  {
-                               ino_offset = agino - irec->ino_startnum;
-
-                               /*
-                                * check if we have to use the root inode
-                                * buffer or read one in ourselves.  Note
-                                * that the root inode is always the first
-                                * inode of the chunk that it's in so there
-                                * are two possible cases where lost+found
-                                * might be in the same buffer as the root
-                                * inode.  One case is a large block
-                                * filesystem where the two inodes are
-                                * in different inode chunks but wind
-                                * up in the same block (multiple chunks
-                                * per block) and the second case (one or
-                                * more blocks per chunk) is where the two
-                                * inodes are in the same chunk. Note that
-                                * inodes are allocated on disk in units
-                                * of MAX(XFS_INODES_PER_CHUNK,sb_inopblock).
-                                */
-                               if (XFS_INO_TO_FSB(mp, mp->m_sb.sb_rootino)
-                                               == XFS_INO_TO_FSB(mp, lino) ||
-                                   (agno == root_agno &&
-                                    agino < root_agino + XFS_INODES_PER_CHUNK)) {
-                                       use_rbuf = 1;
-                                       bp = rootino_bp;
-                                       dino = XFS_MAKE_IPTR(mp, bp, agino -
-                                               XFS_INO_TO_AGINO(mp,
-                                                       mp->m_sb.sb_rootino));
-                               } else  {
-                                       len = (int)XFS_FSB_TO_BB(mp,
-                                               MAX(1, XFS_INODES_PER_CHUNK/
-                                                       inodes_per_block));
-                                       bp = libxfs_readbuf(mp->m_dev,
-                                               XFS_AGB_TO_DADDR(mp, agno,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                               irec->ino_startnum)),
-                                               len, 0);
-                                       if (!bp)
-                                               do_error("couldn't read %s inode %llu\n",
-                                                       ORPHANAGE, lino);
-
-                                       /*
-                                        * get the agbno containing the first
-                                        * inode in the chunk.  In multi-block
-                                        * chunks, this gets us the offset
-                                        * relative to the beginning of a
-                                        * properly aligned buffer.  In
-                                        * multi-chunk blocks, this gets us
-                                        * the correct block number.  Then
-                                        * turn the block number back into
-                                        * an agino and calculate the offset
-                                        * from there to feed to make the iptr.
-                                        * the last term in effect rounds down
-                                        * to the first agino in the buffer.
-                                        */
-                                       dino = XFS_MAKE_IPTR(mp, bp,
-                                               agino - XFS_OFFBNO_TO_AGINO(mp,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                       irec->ino_startnum),
-                                                       0));
-                               }
-
-                               do_warn("        - clearing existing \"%s\" inode\n",
-                                       ORPHANAGE);
-
-                               ino_dirty = clear_dinode(mp, dino, lino);
-
-                               if (!use_rbuf) {
-                                       ASSERT(ino_dirty == 0 ||
-                                               (ino_dirty && !no_modify));
-
-                                       if (ino_dirty && !no_modify)
-                                               libxfs_writebuf(bp, 0);
-                                       else
-                                               libxfs_putbuf(bp);
-                               } else {
-                                       if (ino_dirty)
-                                               *rbuf_dirty = 1;
-                               }
-                               
-                               if (inode_isadir(irec, ino_offset))
-                                       clear_inode_isadir(irec, ino_offset);
-
-                               set_inode_free(irec, ino_offset);
-
-                       }
-
-                       /*
-                        * regardless of whether the inode num is good or
-                        * bad, mark the entry to be junked so the
-                        * createname in phase 6 will succeed.
-                        */
-                       dep->name[0] = '/';
-                       *dirty = 1;
-                       do_warn(
-                       "        - marking entry \"%s\" to be deleted\n",
-                                               fname);
-                       res++;
-               }
-               ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
-       }
-
-       return(res);
-}
-
-int
-longform2_delete_orphanage(xfs_mount_t *mp,
-                       xfs_ino_t       ino,
-                       xfs_dinode_t    *dino,
-                       xfs_buf_t       *rootino_bp,
-                       int             *rbuf_dirty)
-{
-       xfs_dir2_data_t         *data;
-       xfs_dabuf_t             *bp;
-       xfs_dfsbno_t            fsbno;
-       xfs_dablk_t             da_bno;
-       int                     dirty;
-       int                     res;
-       bmap_ext_t              *bmp;
-       int                     i;
-
-       da_bno = 0;
-       *rbuf_dirty = 0;
-       fsbno = NULLDFSBNO;
-       bmp = malloc(mp->m_dirblkfsbs * sizeof(*bmp));
-       if (!bmp) {
-               do_error(
-       "malloc failed (%u bytes) in longform2_delete_orphanage, ino %llu\n",
-                       mp->m_dirblkfsbs * sizeof(*bmp), ino);
-               exit(1);
-       }
-
-       /*
-        * cycle through the entire directory looking to delete
-        * every "lost+found" entry.  make sure to catch duplicate
-        * entries.
-        *
-        * We could probably speed this up by doing a smarter lookup
-        * to get us to the first block that contains the hashvalue
-        * of "lost+found" but what the heck.  that would require a
-        * double lookup for each level.  and how big can '/' get???
-        * It's probably not worth it.
-        */
-       res = 0;
-
-       for (da_bno = 0;
-            da_bno < XFS_B_TO_FSB(mp, INT_GET(dino->di_core.di_size, ARCH_CONVERT));
-            da_bno += mp->m_dirblkfsbs) {
-               for (i = 0; i < mp->m_dirblkfsbs; i++) {
-                       fsbno = get_bmapi(mp, dino, ino, da_bno + i,
-                                         XFS_DATA_FORK);
-                       if (fsbno == NULLDFSBNO)
-                               break;
-                       bmp[i].startoff = da_bno + i;
-                       bmp[i].startblock = fsbno;
-                       bmp[i].blockcount = 1;
-                       bmp[i].flag = 0;
-               }
-               if (fsbno == NULLDFSBNO)
-                       continue;
-               bp = da_read_buf(mp, mp->m_dirblkfsbs, bmp);
-               if (bp == NULL) {
-                       do_error(
-               "can't read block %u (fsbno %llu) for directory inode %llu\n",
-                                       da_bno, bmp[0].startblock, ino);
-                       exit(1);
-               }
-
-               data = (xfs_dir2_data_t *)bp->data;
-
-               if (INT_GET(data->hdr.magic, ARCH_CONVERT) != XFS_DIR2_DATA_MAGIC &&
-                   INT_GET(data->hdr.magic, ARCH_CONVERT) != XFS_DIR2_BLOCK_MAGIC)  {
-                       do_error(
-       "bad magic # (0x%x) for directory data block (bno %u fsbno %llu)\n",
-                               INT_GET(data->hdr.magic, ARCH_CONVERT), da_bno, bmp[0].startblock);
-                       exit(1);
-               }
-
-               res += lf2_block_delete_orphanage(mp, ino, data, &dirty,
-                                       rootino_bp, rbuf_dirty);
-
-               ASSERT(dirty == 0 || (dirty && !no_modify));
-
-               if (dirty && !no_modify)
-                       da_bwrite(mp, bp);
-               else
-                       da_brelse(bp);
-       }
-       free(bmp);
-
-       return(res);
-}
-
-/*
- * returns 1 if a deletion happened, 0 otherwise.
- */
-/* ARGSUSED */
-int
-shortform2_delete_orphanage(xfs_mount_t        *mp,
-                       xfs_ino_t       ino,
-                       xfs_dinode_t    *root_dino,
-                       xfs_buf_t       *rootino_bp,
-                       int             *ino_dirty)
-{
-       xfs_dir2_sf_t           *sf;
-       xfs_dinode_t            *dino;
-       xfs_dir2_sf_entry_t     *sf_entry, *next_sfe, *tmp_sfe;
-       xfs_buf_t               *bp;
-       xfs_ino_t               lino;
-       xfs_agino_t             agino;
-       xfs_agino_t             root_agino;
-       int                     max_size;
-       xfs_agnumber_t          agno;
-       xfs_agnumber_t          root_agno;
-       int                     ino_dir_size;
-       ino_tree_node_t         *irec;
-       int                     ino_offset;
-       int                     i;
-       int                     dirty;
-       int                     tmp_len;
-       int                     tmp_elen;
-       int                     len;
-       int                     use_rbuf;
-       char                    fname[MAXNAMELEN + 1];
-       int                     res;
-
-       sf = &root_dino->di_u.di_dir2sf;
-       *ino_dirty = 0;
-       irec = NULL;
-       ino_dir_size = INT_GET(root_dino->di_core.di_size, ARCH_CONVERT);
-       max_size = XFS_DFORK_DSIZE_ARCH(root_dino, mp, ARCH_CONVERT);
-       use_rbuf = 0;
-       res = 0;
-       root_agno = XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino);
-       root_agino = XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino);
-
-       /*
-        * run through entries looking for "lost+found".
-        */
-       sf_entry = next_sfe = XFS_DIR2_SF_FIRSTENTRY(sf);
-       for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT) && ino_dir_size >
-                       (__psint_t)next_sfe - (__psint_t)sf; i++)  {
-               tmp_sfe = NULL;
-               sf_entry = next_sfe;
-               lino = XFS_DIR2_SF_GET_INUMBER_ARCH(sf,
-                       XFS_DIR2_SF_INUMBERP(sf_entry), ARCH_CONVERT);
-               bcopy(sf_entry->name, fname, sf_entry->namelen);
-               fname[sf_entry->namelen] = '\0';
-
-               if (!strcmp(ORPHANAGE, fname))  {
-                       agno = XFS_INO_TO_AGNO(mp, lino);
-                       agino = XFS_INO_TO_AGINO(mp, lino);
-
-                       irec = find_inode_rec(agno, agino);
-
-                       /*
-                        * if the orphange inode is in the tree,
-                        * get it, clear it, and mark it free.
-                        * the inodes in the orphanage will get
-                        * reattached to the new orphanage.
-                        */
-                       if (irec != NULL)  {
-                               do_warn("        - clearing existing \"%s\" inode\n",
-                                       ORPHANAGE);
-
-                               ino_offset = agino - irec->ino_startnum;
-
-                               /*
-                                * check if we have to use the root inode
-                                * buffer or read one in ourselves.  Note
-                                * that the root inode is always the first
-                                * inode of the chunk that it's in so there
-                                * are two possible cases where lost+found
-                                * might be in the same buffer as the root
-                                * inode.  One case is a large block
-                                * filesystem where the two inodes are
-                                * in different inode chunks but wind
-                                * up in the same block (multiple chunks
-                                * per block) and the second case (one or
-                                * more blocks per chunk) is where the two
-                                * inodes are in the same chunk. Note that
-                                * inodes are allocated on disk in units
-                                * of MAX(XFS_INODES_PER_CHUNK,sb_inopblock).
-                                */
-                               if (XFS_INO_TO_FSB(mp, mp->m_sb.sb_rootino)
-                                               == XFS_INO_TO_FSB(mp, lino) ||
-                                   (agno == root_agno &&
-                                    agino < root_agino + XFS_INODES_PER_CHUNK)) {
-                                       use_rbuf = 1;
-                                       bp = rootino_bp;
-
-                                       dino = XFS_MAKE_IPTR(mp, bp, agino -
-                                               XFS_INO_TO_AGINO(mp,
-                                                       mp->m_sb.sb_rootino));
-                               } else  {
-                                       len = (int)XFS_FSB_TO_BB(mp,
-                                               MAX(1, XFS_INODES_PER_CHUNK/
-                                                       inodes_per_block));
-                                       bp = libxfs_readbuf(mp->m_dev,
-                                               XFS_AGB_TO_DADDR(mp, agno,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                               irec->ino_startnum)),
-                                               len, 0);
-                                       if (!bp)
-                                               do_error("could not read %s inode "
-                                                       "%llu\n", ORPHANAGE, lino);
-                                       /*
-                                        * get the agbno containing the first
-                                        * inode in the chunk.  In multi-block
-                                        * chunks, this gets us the offset
-                                        * relative to the beginning of a
-                                        * properly aligned buffer.  In
-                                        * multi-chunk blocks, this gets us
-                                        * the correct block number.  Then
-                                        * turn the block number back into
-                                        * an agino and calculate the offset
-                                        * from there to feed to make the iptr.
-                                        * the last term in effect rounds down
-                                        * to the first agino in the buffer.
-                                        */
-                                       dino = XFS_MAKE_IPTR(mp, bp,
-                                               agino - XFS_OFFBNO_TO_AGINO(mp,
-                                                       XFS_AGINO_TO_AGBNO(mp,
-                                                       irec->ino_startnum),
-                                                       0));
-                               }
-
-                               dirty = clear_dinode(mp, dino, lino);
-
-                               ASSERT(dirty == 0 || (dirty && !no_modify));
-
-                               /*
-                                * if we read the lost+found inode in to
-                                * it, get rid of it here.  if the lost+found
-                                * inode is in the root inode buffer, the
-                                * buffer will be marked dirty anyway since
-                                * the lost+found entry in the root inode is
-                                * also being deleted which makes the root
-                                * inode buffer automatically dirty.
-                                */
-                               if (!use_rbuf)  {
-                                       dino = NULL;
-                                       if (dirty && !no_modify)
-                                               libxfs_writebuf(bp, 0);
-                                       else
-                                               libxfs_putbuf(bp);
-                               }
-                               
-
-                               if (inode_isadir(irec, ino_offset))
-                                       clear_inode_isadir(irec, ino_offset);
-
-                               set_inode_free(irec, ino_offset);
-                       }
-
-                       do_warn("        - deleting existing \"%s\" entry\n",
-                               ORPHANAGE);
-
-                       /*
-                        * note -- exactly the same deletion code as in
-                        * process_shortform_dir()
-                        */
-                       tmp_elen = XFS_DIR2_SF_ENTSIZE_BYENTRY(sf, sf_entry);
-                       INT_MOD(root_dino->di_core.di_size, ARCH_CONVERT, -(tmp_elen));
-
-                       tmp_sfe = (xfs_dir2_sf_entry_t *)
-                               ((__psint_t) sf_entry + tmp_elen);
-                       tmp_len = max_size - ((__psint_t) tmp_sfe
-                                       - (__psint_t) sf);
-
-                       memmove(sf_entry, tmp_sfe, tmp_len);
-
-                       INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
-                       if (lino > XFS_DIR2_MAX_SHORT_INUM)
-                               sf->hdr.i8count--;
-
-                       bzero((void *) ((__psint_t) sf_entry + tmp_len),
-                               tmp_elen);
-
-                       /*
-                        * set the tmp value to the current
-                        * pointer so we'll process the entry
-                        * we just moved up
-                        */
-                       tmp_sfe = sf_entry;
-
-                       /*
-                        * WARNING:  drop the index i by one
-                        * so it matches the decremented count for
-                        * accurate comparisons in the loop test.
-                        * mark root inode as dirty to make deletion
-                        * permanent.
-                        */
-                       i--;
-
-                       *ino_dirty = 1;
-
-                       res++;
-               }
-               next_sfe = (tmp_sfe == NULL)
-                       ? (xfs_dir2_sf_entry_t *) ((__psint_t) sf_entry +
-                               XFS_DIR2_SF_ENTSIZE_BYENTRY(sf, sf_entry))
-                       : tmp_sfe;
-       }
-
-       return(res);
-}
-
-void
-delete_orphanage(xfs_mount_t *mp)
-{
-       xfs_ino_t ino;
-       xfs_dinode_t *dino;
-       xfs_buf_t *dbp;
-       int dirty, res, len;
-
-       ASSERT(!no_modify);
-
-       dbp = NULL;
-       dirty = res = 0;
-       ino = mp->m_sb.sb_rootino;
-
-       /*
-        * we know the root is in use or we wouldn't be here
-        */
-       len = (int)XFS_FSB_TO_BB(mp,
-                       MAX(1, XFS_INODES_PER_CHUNK/inodes_per_block));
-       dbp = libxfs_readbuf(mp->m_dev,
-                       XFS_FSB_TO_DADDR(mp, XFS_INO_TO_FSB(mp, ino)), len, 0);
-       if (!dbp) {
-               do_error("could not read buffer for root inode %llu "
-                       "(daddr %lld, size %d)\n", ino,
-                       XFS_FSB_TO_DADDR(mp, XFS_INO_TO_FSB(mp, ino)),
-                       XFS_FSB_TO_BB(mp, 1));
-       }
-
-       /*
-        * we also know that the root inode is always the first inode
-        * allocated in the system, therefore it'll be at the beginning
-        * of the root inode chunk
-        */
-       dino = XFS_MAKE_IPTR(mp, dbp, 0);
-
-       switch (dino->di_core.di_format)  {
-       case XFS_DINODE_FMT_EXTENTS:
-       case XFS_DINODE_FMT_BTREE:
-               if (XFS_SB_VERSION_HASDIRV2(&mp->m_sb))
-                       res = longform2_delete_orphanage(mp, ino, dino, dbp,
-                               &dirty);
-               else
-                       res = longform_delete_orphanage(mp, ino, dino, dbp,
-                               &dirty);
-               break;
-       case XFS_DINODE_FMT_LOCAL:
-               if (XFS_SB_VERSION_HASDIRV2(&mp->m_sb))
-                       res = shortform2_delete_orphanage(mp, ino, dino, dbp,
-                               &dirty);
-               else
-                       res = shortform_delete_orphanage(mp, ino, dino, dbp,
-                               &dirty);
-               ASSERT((res == 0 && dirty == 0) || (res > 0 && dirty == 1));
-               break;
-       default:
-               break;
-       }
-
-       if (res)  {
-               switch (dino->di_core.di_version)  {
-               case XFS_DINODE_VERSION_1:
-                       INT_MOD(dino->di_core.di_onlink, ARCH_CONVERT, -res);
-                       INT_SET(dino->di_core.di_nlink, ARCH_CONVERT,
-                               INT_GET(dino->di_core.di_onlink, ARCH_CONVERT));
-                       break;
-               case XFS_DINODE_VERSION_2:
-                       INT_MOD(dino->di_core.di_nlink, ARCH_CONVERT, -res);
-                       break;
-               default:
-                       do_error("unknown version #%d in root inode\n",
-                                       dino->di_core.di_version);
-               }
-
-               dirty = 1;
-       }
-
-       if (dirty)
-               libxfs_writebuf(dbp, 0);
-       else
-               libxfs_putbuf(dbp);
-}
+bool collect_rmaps;
 
 /*
  * null out quota inode fields in sb if they point to non-existent inodes.
@@ -1057,14 +30,18 @@ delete_orphanage(xfs_mount_t *mp)
  * free in which case they'd never be cleared so the fields wouldn't
  * be cleared by process_dinode().
  */
-void
+static void
 quotino_check(xfs_mount_t *mp)
 {
        ino_tree_node_t *irec;
 
        if (mp->m_sb.sb_uquotino != NULLFSINO && mp->m_sb.sb_uquotino != 0)  {
-               irec = find_inode_rec(XFS_INO_TO_AGNO(mp, mp->m_sb.sb_uquotino),
-                       XFS_INO_TO_AGINO(mp, mp->m_sb.sb_uquotino));
+               if (verify_inum(mp, mp->m_sb.sb_uquotino))
+                       irec = NULL;
+               else
+                       irec = find_inode_rec(mp,
+                               XFS_INO_TO_AGNO(mp, mp->m_sb.sb_uquotino),
+                               XFS_INO_TO_AGINO(mp, mp->m_sb.sb_uquotino));
 
                if (irec == NULL || is_inode_free(irec,
                                mp->m_sb.sb_uquotino - irec->ino_startnum))  {
@@ -1075,8 +52,12 @@ quotino_check(xfs_mount_t *mp)
        }
 
        if (mp->m_sb.sb_gquotino != NULLFSINO && mp->m_sb.sb_gquotino != 0)  {
-               irec = find_inode_rec(XFS_INO_TO_AGNO(mp, mp->m_sb.sb_gquotino),
-                       XFS_INO_TO_AGINO(mp, mp->m_sb.sb_gquotino));
+               if (verify_inum(mp, mp->m_sb.sb_gquotino))
+                       irec = NULL;
+               else
+                       irec = find_inode_rec(mp,
+                               XFS_INO_TO_AGNO(mp, mp->m_sb.sb_gquotino),
+                               XFS_INO_TO_AGINO(mp, mp->m_sb.sb_gquotino));
 
                if (irec == NULL || is_inode_free(irec,
                                mp->m_sb.sb_gquotino - irec->ino_startnum))  {
@@ -1085,9 +66,25 @@ quotino_check(xfs_mount_t *mp)
                } else
                        lost_gquotino = 0;
        }
+
+       if (mp->m_sb.sb_pquotino != NULLFSINO && mp->m_sb.sb_pquotino != 0)  {
+               if (verify_inum(mp, mp->m_sb.sb_pquotino))
+                       irec = NULL;
+               else
+                       irec = find_inode_rec(mp,
+                               XFS_INO_TO_AGNO(mp, mp->m_sb.sb_pquotino),
+                               XFS_INO_TO_AGINO(mp, mp->m_sb.sb_pquotino));
+
+               if (irec == NULL || is_inode_free(irec,
+                               mp->m_sb.sb_pquotino - irec->ino_startnum))  {
+                       mp->m_sb.sb_pquotino = NULLFSINO;
+                       lost_pquotino = 1;
+               } else
+                       lost_pquotino = 0;
+       }
 }
 
-void
+static void
 quota_sb_check(xfs_mount_t *mp)
 {
        /*
@@ -1111,40 +108,179 @@ quota_sb_check(xfs_mount_t *mp)
 
        if (fs_quotas &&
            (mp->m_sb.sb_uquotino == NULLFSINO || mp->m_sb.sb_uquotino == 0) &&
-           (mp->m_sb.sb_gquotino == NULLFSINO || mp->m_sb.sb_gquotino == 0))  {
+           (mp->m_sb.sb_gquotino == NULLFSINO || mp->m_sb.sb_gquotino == 0) &&
+           (mp->m_sb.sb_pquotino == NULLFSINO || mp->m_sb.sb_pquotino == 0))  {
                lost_quotas = 1;
                fs_quotas = 0;
        } else if (!verify_inum(mp, mp->m_sb.sb_uquotino) &&
-                       !verify_inum(mp, mp->m_sb.sb_gquotino)) {
+                       !verify_inum(mp, mp->m_sb.sb_gquotino) &&
+                       !verify_inum(mp, mp->m_sb.sb_pquotino)) {
                fs_quotas = 1;
        }
 }
 
 
+static void
+process_ag_func(
+       struct workqueue        *wq,
+       xfs_agnumber_t          agno,
+       void                    *arg)
+{
+       wait_for_inode_prefetch(arg);
+       do_log(_("        - agno = %d\n"), agno);
+       process_aginodes(wq->wq_ctx, arg, agno, 0, 1, 0);
+       blkmap_free_final();
+       cleanup_inode_prefetch(arg);
+
+       /*
+        * now recycle the per-AG duplicate extent records
+        */
+       release_dup_extent_tree(agno);
+}
+
+static void
+process_ags(
+       xfs_mount_t             *mp)
+{
+       xfs_agnumber_t          i;
+       int                     error;
+
+       do_inode_prefetch(mp, ag_stride, process_ag_func, true, false);
+       for (i = 0; i < mp->m_sb.sb_agcount; i++) {
+               error = rmap_finish_collecting_fork_recs(mp, i);
+               if (error)
+                       do_error(
+_("unable to finish adding attr/data fork reverse-mapping data for AG %u.\n"),
+                               i);
+       }
+}
+
+static void
+check_rmap_btrees(
+       struct workqueue*wq,
+       xfs_agnumber_t  agno,
+       void            *arg)
+{
+       int             error;
+
+       error = rmap_add_fixed_ag_rec(wq->wq_ctx, agno);
+       if (error)
+               do_error(
+_("unable to add AG %u metadata reverse-mapping data.\n"), agno);
+
+       error = rmap_fold_raw_recs(wq->wq_ctx, agno);
+       if (error)
+               do_error(
+_("unable to merge AG %u metadata reverse-mapping data.\n"), agno);
+
+       error = rmaps_verify_btree(wq->wq_ctx, agno);
+       if (error)
+               do_error(
+_("%s while checking reverse-mappings"),
+                        strerror(-error));
+}
+
+static void
+compute_ag_refcounts(
+       struct workqueue*wq,
+       xfs_agnumber_t  agno,
+       void            *arg)
+{
+       int             error;
+
+       error = compute_refcounts(wq->wq_ctx, agno);
+       if (error)
+               do_error(
+_("%s while computing reference count records.\n"),
+                        strerror(-error));
+}
+
+static void
+process_inode_reflink_flags(
+       struct workqueue        *wq,
+       xfs_agnumber_t          agno,
+       void                    *arg)
+{
+       int                     error;
+
+       error = fix_inode_reflink_flags(wq->wq_ctx, agno);
+       if (error)
+               do_error(
+_("%s while fixing inode reflink flags.\n"),
+                        strerror(-error));
+}
+
+static void
+check_refcount_btrees(
+       struct workqueue*wq,
+       xfs_agnumber_t  agno,
+       void            *arg)
+{
+       int             error;
+
+       error = check_refcounts(wq->wq_ctx, agno);
+       if (error)
+               do_error(
+_("%s while checking reference counts"),
+                        strerror(-error));
+}
+
+static void
+process_rmap_data(
+       struct xfs_mount        *mp)
+{
+       struct workqueue        wq;
+       xfs_agnumber_t          i;
+
+       if (!rmap_needs_work(mp))
+               return;
+
+       create_work_queue(&wq, mp, platform_nproc());
+       for (i = 0; i < mp->m_sb.sb_agcount; i++)
+               queue_work(&wq, check_rmap_btrees, i, NULL);
+       destroy_work_queue(&wq);
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return;
+
+       create_work_queue(&wq, mp, platform_nproc());
+       for (i = 0; i < mp->m_sb.sb_agcount; i++)
+               queue_work(&wq, compute_ag_refcounts, i, NULL);
+       destroy_work_queue(&wq);
+
+       create_work_queue(&wq, mp, platform_nproc());
+       for (i = 0; i < mp->m_sb.sb_agcount; i++) {
+               queue_work(&wq, process_inode_reflink_flags, i, NULL);
+               queue_work(&wq, check_refcount_btrees, i, NULL);
+       }
+       destroy_work_queue(&wq);
+}
+
 void
 phase4(xfs_mount_t *mp)
 {
        ino_tree_node_t         *irec;
-       xfs_drtbno_t            bno;
-       xfs_drtbno_t            rt_start;
+       xfs_rtblock_t           bno;
+       xfs_rtblock_t           rt_start;
        xfs_extlen_t            rt_len;
        xfs_agnumber_t          i;
        xfs_agblock_t           j;
        xfs_agblock_t           ag_end;
-       xfs_agblock_t           extent_start;
-       xfs_extlen_t            extent_len;
+       xfs_extlen_t            blen;
        int                     ag_hdr_len = 4 * mp->m_sb.sb_sectsize;
        int                     ag_hdr_block;
        int                     bstate;
-       int                     count_bcnt_extents(xfs_agnumber_t agno);
-       int                     count_bno_extents(xfs_agnumber_t agno);
-       
+
+       if (rmap_needs_work(mp))
+               collect_rmaps = true;
        ag_hdr_block = howmany(ag_hdr_len, mp->m_sb.sb_blocksize);
 
-       printf("Phase 4 - check for duplicate blocks...\n");
-       printf("        - setting up duplicate extent list...\n");
+       do_log(_("Phase 4 - check for duplicate blocks...\n"));
+       do_log(_("        - setting up duplicate extent list...\n"));
+
+       set_progress_msg(PROG_FMT_DUP_EXTENT, (uint64_t) glob_agcount);
 
-       irec = find_inode_rec(XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
+       irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
                                XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino));
 
        /*
@@ -1154,38 +290,26 @@ phase4(xfs_mount_t *mp)
        if (is_inode_free(irec, 0) || !inode_isadir(irec, 0))  {
                need_root_inode = 1;
                if (no_modify)
-                       do_warn("root inode would be lost\n");
+                       do_warn(_("root inode would be lost\n"));
                else
-                       do_warn("root inode lost\n");
-       }
-
-       /*
-        * have to delete lost+found first so that blocks used
-        * by lost+found don't show up as used
-        */
-       if (!no_modify)  {
-               printf("        - clear lost+found (if it exists) ...\n");
-               if (!need_root_inode)
-                       delete_orphanage(mp);
+                       do_warn(_("root inode lost\n"));
        }
 
        for (i = 0; i < mp->m_sb.sb_agcount; i++)  {
                ag_end = (i < mp->m_sb.sb_agcount - 1) ? mp->m_sb.sb_agblocks :
                        mp->m_sb.sb_dblocks -
-                               (xfs_drfsbno_t) mp->m_sb.sb_agblocks * i;
-               extent_start = extent_len = 0;
+                               (xfs_rfsblock_t) mp->m_sb.sb_agblocks * i;
+
                /*
                 * set up duplicate extent list for this ag
                 */
-               for (j = ag_hdr_block; j < ag_end; j++)  {
-
-                       bstate = get_agbno_state(mp, i, j);
-
-                       switch (bstate)  {
+               for (j = ag_hdr_block; j < ag_end; j += blen)  {
+                       bstate = get_bmap_ext(i, j, ag_end, &blen);
+                       switch (bstate) {
                        case XR_E_BAD_STATE:
                        default:
-                               do_warn("unknown block state, ag %d, \
-block %d\n",
+                               do_warn(
+                               _("unknown block state, ag %d, block %d\n"),
                                        i, j);
                                /* fall through .. */
                        case XR_E_UNKNOWN:
@@ -1195,38 +319,16 @@ block %d\n",
                        case XR_E_INUSE_FS:
                        case XR_E_INO:
                        case XR_E_FS_MAP:
-                               if (extent_start == 0)
-                                       continue;
-                               else  {
-                                       /*
-                                        * add extent and reset extent state
-                                        */
-                                       add_dup_extent(i, extent_start,
-                                                       extent_len);
-                                       extent_start = 0;
-                                       extent_len = 0;
-                               }
                                break;
                        case XR_E_MULT:
-                               if (extent_start == 0)  {
-                                       extent_start = j;
-                                       extent_len = 1;
-                               } else if (extent_len == MAXEXTLEN)  {
-                                       add_dup_extent(i, extent_start,
-                                                       extent_len);
-                                       extent_start = j;
-                                       extent_len = 1;
-                               } else
-                                       extent_len++;
+                               add_dup_extent(i, j, blen);
                                break;
                        }
                }
-               /*
-                * catch tail-case, extent hitting the end of the ag
-                */
-               if (extent_start != 0)
-                       add_dup_extent(i, extent_start, extent_len);
+
+               PROG_RPT_INC(prog_rpt_done[i], 1);
        }
+       print_final_rpt();
 
        /*
         * initialize realtime bitmap
@@ -1235,13 +337,13 @@ block %d\n",
        rt_len = 0;
 
        for (bno = 0; bno < mp->m_sb.sb_rextents; bno++)  {
-
-               bstate = get_rtbno_state(mp, bno);
-
+               bstate = get_rtbmap(bno);
                switch (bstate)  {
                case XR_E_BAD_STATE:
                default:
-                       do_warn("unknown rt extent state, extent %llu\n", bno);
+                       do_warn(
+       _("unknown rt extent state, extent %" PRIu64 "\n"),
+                               bno);
                        /* fall through .. */
                case XR_E_UNKNOWN:
                case XR_E_FREE1:
@@ -1287,41 +389,31 @@ block %d\n",
        /*
         * initialize bitmaps for all AGs
         */
-       for (i = 0; i < mp->m_sb.sb_agcount; i++)  {
-               ag_end = (i < mp->m_sb.sb_agcount - 1) ? mp->m_sb.sb_agblocks :
-                       mp->m_sb.sb_dblocks -
-                               (xfs_drfsbno_t) mp->m_sb.sb_agblocks * i;
-               /*
-                * now reset the bitmap for all ags
-                */
-               bzero(ba_bmap[i], roundup(mp->m_sb.sb_agblocks*(NBBY/XR_BB),
-                                               sizeof(__uint64_t)));
-               for (j = 0; j < ag_hdr_block; j++)
-                       set_agbno_state(mp, i, j, XR_E_INUSE_FS);
-       }
-       set_bmap_rt(mp->m_sb.sb_rextents);
-       set_bmap_log(mp);
-       set_bmap_fs(mp);
+       reset_bmaps(mp);
 
-       printf("        - check for inodes claiming duplicate blocks...\n");
-       for (i = 0; i < mp->m_sb.sb_agcount; i++)  {
-               /*
-                * ok, now process the inodes -- signal 2-pass check per inode.
-                * first pass checks if the inode conflicts with a known
-                * duplicate extent.  if so, the inode is cleared and second
-                * pass is skipped.  second pass sets the block bitmap
-                * for all blocks claimed by the inode.  directory
-                * and attribute processing is turned OFF since we did that 
-                * already in phase 3.
-                */
-               do_log("        - agno = %d\n", i);
-               process_aginodes(mp, i, 0, 1, 0);
+       do_log(_("        - check for inodes claiming duplicate blocks...\n"));
+       set_progress_msg(PROG_FMT_DUP_BLOCKS, (uint64_t) mp->m_sb.sb_icount);
 
-               /*
-                * now recycle the per-AG duplicate extent records
-                */
-               release_dup_extent_tree(i);
-       }
+       /*
+        * ok, now process the inodes -- signal 2-pass check per inode.
+        * first pass checks if the inode conflicts with a known
+        * duplicate extent.  if so, the inode is cleared and second
+        * pass is skipped.  second pass sets the block bitmap
+        * for all blocks claimed by the inode.  directory
+        * and attribute processing is turned OFF since we did that
+        * already in phase 3.
+        */
+       process_ags(mp);
+
+       /*
+        * Process all the reverse-mapping data that we collected.  This
+        * involves checking the rmap data against the btree, computing
+        * reference counts based on the rmap data, and checking the counts
+        * against the refcount btree.
+        */
+       process_rmap_data(mp);
+
+       print_final_rpt();
 
        /*
         * free up memory used to track trealtime duplicate extents