+// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2000-2002,2005 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);
-
- /*
- * 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);
-
- 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);
-
- 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);
-
- /*
- * 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);
-
- 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);
-
- 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.
* 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)) {
}
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)) {
mp->m_sb.sb_gquotino = NULLFSINO;
- if (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT)
- lost_gquotino = 1;
- else
- lost_pquotino = 1;
+ lost_gquotino = 1;
+ } 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_gquotino = lost_pquotino = 0;
+ lost_pquotino = 0;
}
}
-void
+static void
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);
do_log(_("Phase 4 - check for duplicate blocks...\n"));
do_log(_(" - setting up duplicate extent list...\n"));
- irec = find_inode_rec(XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
+ set_progress_msg(PROG_FMT_DUP_EXTENT, (uint64_t) glob_agcount);
+
+ 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));
/*
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) {
- do_log(_(" - clear lost+found (if it exists) ...\n"));
- if (!need_root_inode)
- delete_orphanage(mp);
- }
-
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(
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
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"),
+ do_warn(
+ _("unknown rt extent state, extent %" PRIu64 "\n"),
bno);
/* fall through .. */
case XR_E_UNKNOWN:
/*
* 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);
do_log(_(" - 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);
+ 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