/* * Check that an inode's allocation status matches ir_free in the inobt * record. First we try querying the in-core inode state, and if the inode * isn't loaded we examine the on-disk inode directly. * * Since there can be 1:M and M:1 mappings between inobt records and inode * clusters, we pass in the inode location information as an inobt record; * the index of an inode cluster within the inobt record (as well as the * cluster buffer itself); and the index of the inode within the cluster. * * @irec is the inobt record. * @irec_ino is the inode offset from the start of the record. * @dip is the on-disk inode. */ STATIC int xchk_iallocbt_check_cluster_ifree( struct xchk_btree *bs, struct xfs_inobt_rec_incore *irec, unsigned int irec_ino, struct xfs_dinode *dip) { struct xfs_mount *mp = bs->cur->bc_mp; xfs_ino_t fsino; xfs_agino_t agino; bool irec_free; bool ino_inuse; bool freemask_ok; int error = 0; if (xchk_should_terminate(bs->sc, &error)) return error; /* * Given an inobt record and the offset of an inode from the start of * the record, compute which fs inode we're talking about. */ agino = irec->ir_startino + irec_ino; fsino = XFS_AGINO_TO_INO(mp, bs->cur->bc_private.a.agno, agino); irec_free = (irec->ir_free & XFS_INOBT_MASK(irec_ino)); if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC || (dip->di_version >= 3 && be64_to_cpu(dip->di_ino) != fsino)) { xchk_btree_set_corrupt(bs->sc, bs->cur, 0); goto out; } error = xfs_icache_inode_is_allocated(mp, bs->cur->bc_tp, fsino, &ino_inuse); if (error == -ENODATA) { /* Not cached, just read the disk buffer */ freemask_ok = irec_free ^ !!(dip->di_mode); if (!bs->sc->try_harder && !freemask_ok) return -EDEADLOCK; } else if (error < 0) { /* * Inode is only half assembled, or there was an IO error, * or the verifier failed, so don't bother trying to check. * The inode scrubber can deal with this. */ goto out; } else { /* Inode is all there. */ freemask_ok = irec_free ^ ino_inuse; } if (!freemask_ok) xchk_btree_set_corrupt(bs->sc, bs->cur, 0); out: return 0; }
/* * Lookup the inode chunk that the given inode lives in and then get the record * if we found the chunk. If the inode was not the last in the chunk and there * are some left allocated, update the data for the pointed-to record as well as * return the count of grabbed inodes. */ STATIC int xfs_bulkstat_grab_ichunk( struct xfs_btree_cur *cur, /* btree cursor */ xfs_agino_t agino, /* starting inode of chunk */ int *icount,/* return # of inodes grabbed */ struct xfs_inobt_rec_incore *irec) /* btree record */ { int idx; /* index into inode chunk */ int stat; int error = 0; /* Lookup the inode chunk that this inode lives in */ error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &stat); if (error) return error; if (!stat) { *icount = 0; return error; } /* Get the record, should always work */ error = xfs_inobt_get_rec(cur, irec, &stat); if (error) return error; XFS_WANT_CORRUPTED_RETURN(cur->bc_mp, stat == 1); /* Check if the record contains the inode in request */ if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino) { *icount = 0; return 0; } idx = agino - irec->ir_startino + 1; if (idx < XFS_INODES_PER_CHUNK && (xfs_inobt_maskn(idx, XFS_INODES_PER_CHUNK - idx) & ~irec->ir_free)) { int i; /* We got a right chunk with some left inodes allocated at it. * Grab the chunk record. Mark all the uninteresting inodes * free -- because they're before our start point. */ for (i = 0; i < idx; i++) { if (XFS_INOBT_MASK(i) & ~irec->ir_free) irec->ir_freecount++; } irec->ir_free |= xfs_inobt_maskn(0, idx); *icount = irec->ir_count - irec->ir_freecount; } return 0; }
/* Check a particular inode with ir_free. */ STATIC int xfs_scrub_iallocbt_check_cluster_freemask( struct xfs_scrub_btree *bs, xfs_ino_t fsino, xfs_agino_t chunkino, xfs_agino_t clusterino, struct xfs_inobt_rec_incore *irec, struct xfs_buf *bp) { struct xfs_dinode *dip; struct xfs_mount *mp = bs->cur->bc_mp; bool inode_is_free = false; bool freemask_ok; bool inuse; int error = 0; if (xfs_scrub_should_terminate(bs->sc, &error)) return error; dip = xfs_buf_offset(bp, clusterino * mp->m_sb.sb_inodesize); if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC || (dip->di_version >= 3 && be64_to_cpu(dip->di_ino) != fsino + clusterino)) { xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); goto out; } if (irec->ir_free & XFS_INOBT_MASK(chunkino + clusterino)) inode_is_free = true; error = xfs_icache_inode_is_allocated(mp, bs->cur->bc_tp, fsino + clusterino, &inuse); if (error == -ENODATA) { /* Not cached, just read the disk buffer */ freemask_ok = inode_is_free ^ !!(dip->di_mode); if (!bs->sc->try_harder && !freemask_ok) return -EDEADLOCK; } else if (error < 0) { /* * Inode is only half assembled, or there was an IO error, * or the verifier failed, so don't bother trying to check. * The inode scrubber can deal with this. */ goto out; } else { /* Inode is all there. */ freemask_ok = inode_is_free ^ inuse; } if (!freemask_ok) xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); out: return 0; }
/* * Make sure the finobt doesn't think this inode is free. * We don't have to check the inobt ourselves because we got the inode via * IGET_UNTRUSTED, which checks the inobt for us. */ static void xchk_inode_xref_finobt( struct xfs_scrub *sc, xfs_ino_t ino) { struct xfs_inobt_rec_incore rec; xfs_agino_t agino; int has_record; int error; if (!sc->sa.fino_cur || xchk_skip_xref(sc->sm)) return; agino = XFS_INO_TO_AGINO(sc->mp, ino); /* * Try to get the finobt record. If we can't get it, then we're * in good shape. */ error = xfs_inobt_lookup(sc->sa.fino_cur, agino, XFS_LOOKUP_LE, &has_record); if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) || !has_record) return; error = xfs_inobt_get_rec(sc->sa.fino_cur, &rec, &has_record); if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) || !has_record) return; /* * Otherwise, make sure this record either doesn't cover this inode, * or that it does but it's marked present. */ if (rec.ir_startino > agino || rec.ir_startino + XFS_INODES_PER_CHUNK <= agino) return; if (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino)) xchk_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0); }
/* * Check that the holemask and freemask of a hypothetical inode cluster match * what's actually on disk. If sparse inodes are enabled, the cluster does * not actually have to map to inodes if the corresponding holemask bit is set. * * @cluster_base is the first inode in the cluster within the @irec. */ STATIC int xchk_iallocbt_check_cluster( struct xchk_btree *bs, struct xfs_inobt_rec_incore *irec, unsigned int cluster_base) { struct xfs_imap imap; struct xfs_mount *mp = bs->cur->bc_mp; struct xfs_dinode *dip; struct xfs_buf *cluster_bp; unsigned int nr_inodes; xfs_agnumber_t agno = bs->cur->bc_private.a.agno; xfs_agblock_t agbno; unsigned int cluster_index; uint16_t cluster_mask = 0; uint16_t ir_holemask; int error = 0; nr_inodes = min_t(unsigned int, XFS_INODES_PER_CHUNK, mp->m_inodes_per_cluster); /* Map this inode cluster */ agbno = XFS_AGINO_TO_AGBNO(mp, irec->ir_startino + cluster_base); /* Compute a bitmask for this cluster that can be used for holemask. */ for (cluster_index = 0; cluster_index < nr_inodes; cluster_index += XFS_INODES_PER_HOLEMASK_BIT) cluster_mask |= XFS_INOBT_MASK((cluster_base + cluster_index) / XFS_INODES_PER_HOLEMASK_BIT); /* * Map the first inode of this cluster to a buffer and offset. * Be careful about inobt records that don't align with the start of * the inode buffer when block sizes are large enough to hold multiple * inode chunks. When this happens, cluster_base will be zero but * ir_startino can be large enough to make im_boffset nonzero. */ ir_holemask = (irec->ir_holemask & cluster_mask); imap.im_blkno = XFS_AGB_TO_DADDR(mp, agno, agbno); imap.im_len = XFS_FSB_TO_BB(mp, mp->m_blocks_per_cluster); imap.im_boffset = XFS_INO_TO_OFFSET(mp, irec->ir_startino); if (imap.im_boffset != 0 && cluster_base != 0) { ASSERT(imap.im_boffset == 0 || cluster_base == 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0); return 0; } trace_xchk_iallocbt_check_cluster(mp, agno, irec->ir_startino, imap.im_blkno, imap.im_len, cluster_base, nr_inodes, cluster_mask, ir_holemask, XFS_INO_TO_OFFSET(mp, irec->ir_startino + cluster_base)); /* The whole cluster must be a hole or not a hole. */ if (ir_holemask != cluster_mask && ir_holemask != 0) { xchk_btree_set_corrupt(bs->sc, bs->cur, 0); return 0; } /* If any part of this is a hole, skip it. */ if (ir_holemask) { xchk_xref_is_not_owned_by(bs->sc, agbno, mp->m_blocks_per_cluster, &XFS_RMAP_OINFO_INODES); return 0; } xchk_xref_is_owned_by(bs->sc, agbno, mp->m_blocks_per_cluster, &XFS_RMAP_OINFO_INODES); /* Grab the inode cluster buffer. */ error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap, &dip, &cluster_bp, 0, 0); if (!xchk_btree_xref_process_error(bs->sc, bs->cur, 0, &error)) return error; /* Check free status of each inode within this cluster. */ for (cluster_index = 0; cluster_index < nr_inodes; cluster_index++) { struct xfs_dinode *dip; if (imap.im_boffset >= BBTOB(cluster_bp->b_length)) { xchk_btree_set_corrupt(bs->sc, bs->cur, 0); break; } dip = xfs_buf_offset(cluster_bp, imap.im_boffset); error = xchk_iallocbt_check_cluster_ifree(bs, irec, cluster_base + cluster_index, dip); if (error) break; imap.im_boffset += mp->m_sb.sb_inodesize; } xfs_trans_brelse(bs->cur->bc_tp, cluster_bp); return error; }
xfs_inofree_t xfs_inobt_mask(int i) { return XFS_INOBT_MASK(i); }
/* * Process inodes in chunk with a pointer to a formatter function * that will iget the inode and fill in the appropriate structure. */ static int xfs_bulkstat_ag_ichunk( struct xfs_mount *mp, xfs_agnumber_t agno, struct xfs_inobt_rec_incore *irbp, bulkstat_one_pf formatter, size_t statstruct_size, struct xfs_bulkstat_agichunk *acp, xfs_agino_t *last_agino) { char __user **ubufp = acp->ac_ubuffer; int chunkidx; int error = 0; xfs_agino_t agino = irbp->ir_startino; for (chunkidx = 0; chunkidx < XFS_INODES_PER_CHUNK; chunkidx++, agino++) { int fmterror; int ubused; /* inode won't fit in buffer, we are done */ if (acp->ac_ubleft < statstruct_size) break; /* Skip if this inode is free */ if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) continue; /* Get the inode and fill in a single buffer */ ubused = statstruct_size; error = formatter(mp, XFS_AGINO_TO_INO(mp, agno, agino), *ubufp, acp->ac_ubleft, &ubused, &fmterror); if (fmterror == BULKSTAT_RV_GIVEUP || (error && error != -ENOENT && error != -EINVAL)) { acp->ac_ubleft = 0; ASSERT(error); break; } /* be careful not to leak error if at end of chunk */ if (fmterror == BULKSTAT_RV_NOTHING || error) { error = 0; continue; } *ubufp += ubused; acp->ac_ubleft -= ubused; acp->ac_ubelem++; } /* * Post-update *last_agino. At this point, agino will always point one * inode past the last inode we processed successfully. Hence we * substract that inode when setting the *last_agino cursor so that we * return the correct cookie to userspace. On the next bulkstat call, * the inode under the lastino cookie will be skipped as we have already * processed it here. */ *last_agino = agino - 1; return error; }
/* Make sure the free mask is consistent with what the inodes think. */ STATIC int xfs_scrub_iallocbt_check_freemask( struct xfs_scrub_btree *bs, struct xfs_inobt_rec_incore *irec) { struct xfs_owner_info oinfo; struct xfs_imap imap; struct xfs_mount *mp = bs->cur->bc_mp; struct xfs_dinode *dip; struct xfs_buf *bp; xfs_ino_t fsino; xfs_agino_t nr_inodes; xfs_agino_t agino; xfs_agino_t chunkino; xfs_agino_t clusterino; xfs_agblock_t agbno; int blks_per_cluster; uint16_t holemask; uint16_t ir_holemask; int error = 0; /* Make sure the freemask matches the inode records. */ blks_per_cluster = xfs_icluster_size_fsb(mp); nr_inodes = XFS_OFFBNO_TO_AGINO(mp, blks_per_cluster, 0); xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES); for (agino = irec->ir_startino; agino < irec->ir_startino + XFS_INODES_PER_CHUNK; agino += blks_per_cluster * mp->m_sb.sb_inopblock) { fsino = XFS_AGINO_TO_INO(mp, bs->cur->bc_private.a.agno, agino); chunkino = agino - irec->ir_startino; agbno = XFS_AGINO_TO_AGBNO(mp, agino); /* Compute the holemask mask for this cluster. */ for (clusterino = 0, holemask = 0; clusterino < nr_inodes; clusterino += XFS_INODES_PER_HOLEMASK_BIT) holemask |= XFS_INOBT_MASK((chunkino + clusterino) / XFS_INODES_PER_HOLEMASK_BIT); /* The whole cluster must be a hole or not a hole. */ ir_holemask = (irec->ir_holemask & holemask); if (ir_holemask != holemask && ir_holemask != 0) { xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0); continue; } /* If any part of this is a hole, skip it. */ if (ir_holemask) continue; /* Grab the inode cluster buffer. */ imap.im_blkno = XFS_AGB_TO_DADDR(mp, bs->cur->bc_private.a.agno, agbno); imap.im_len = XFS_FSB_TO_BB(mp, blks_per_cluster); imap.im_boffset = 0; error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap, &dip, &bp, 0, 0); if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error)) continue; /* Which inodes are free? */ for (clusterino = 0; clusterino < nr_inodes; clusterino++) { error = xfs_scrub_iallocbt_check_cluster_freemask(bs, fsino, chunkino, clusterino, irec, bp); if (error) { xfs_trans_brelse(bs->cur->bc_tp, bp); return error; } } xfs_trans_brelse(bs->cur->bc_tp, bp); } return error; }