/* * Look for mapped directory blocks at or above the current offset. * Truncate down to the nearest directory block to start the scanning * operation.
*/
last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map)) return 0; if (map.br_startoff >= last_da) return 0;
xfs_trim_extent(&map, map_off, last_da - map_off);
/* Read the directory block of that first mapping. */
new_off = xfs_dir2_da_to_byte(geo, map.br_startoff); if (new_off > *curoff)
*curoff = new_off;
/* Call a function for every entry in a leaf directory. */ STATICint
xchk_dir_walk_leaf( struct xfs_scrub *sc, struct xfs_inode *dp,
xchk_dirent_fn dirent_fn, void *priv)
{ struct xfs_mount *mp = dp->i_mount; struct xfs_da_geometry *geo = mp->m_dir_geo; struct xfs_buf *bp = NULL;
xfs_dir2_off_t curoff = 0; unsignedint offset = 0; int error;
/* Iterate every directory offset in this directory. */ while (curoff < XFS_DIR2_LEAF_OFFSET) { struct xfs_name name = { }; struct xfs_dir2_data_unused *dup; struct xfs_dir2_data_entry *dep;
xfs_ino_t ino; unsignedint length;
xfs_dir2_dataptr_t dapos;
/* * If we have no buffer, or we're off the end of the * current buffer, need to get another one.
*/ if (!bp || offset >= geo->blksize) { if (bp) {
xfs_trans_brelse(sc->tp, bp);
bp = NULL;
}
/* Advance to the next entry. */
offset += length;
curoff += length;
}
if (bp)
xfs_trans_brelse(sc->tp, bp); return error;
}
/* * Call a function for every entry in a directory. * * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*.
*/ int
xchk_dir_walk( struct xfs_scrub *sc, struct xfs_inode *dp,
xchk_dirent_fn dirent_fn, void *priv)
{ struct xfs_da_args args = {
.dp = dp,
.geo = dp->i_mount->m_dir_geo,
.trans = sc->tp,
.owner = dp->i_ino,
}; int error;
switch (xfs_dir2_format(&args, &error)) { case XFS_DIR2_FMT_SF: return xchk_dir_walk_sf(sc, dp, dirent_fn, priv); case XFS_DIR2_FMT_BLOCK: return xchk_dir_walk_block(sc, dp, dirent_fn, priv); case XFS_DIR2_FMT_LEAF: case XFS_DIR2_FMT_NODE: return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv); default: return error;
}
}
/* * Look up the inode number for an exact name in a directory. * * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. Names are not * checked for correctness.
*/ int
xchk_dir_lookup( struct xfs_scrub *sc, struct xfs_inode *dp, conststruct xfs_name *name,
xfs_ino_t *ino)
{ struct xfs_da_args args = {
.dp = dp,
.geo = dp->i_mount->m_dir_geo,
.trans = sc->tp,
.name = name->name,
.namelen = name->len,
.filetype = name->type,
.hashval = xfs_dir2_hashname(dp->i_mount, name),
.whichfork = XFS_DATA_FORK,
.op_flags = XFS_DA_OP_OKNOENT,
.owner = dp->i_ino,
}; int error;
if (xfs_is_shutdown(dp->i_mount)) return -EIO;
/* * A temporary directory's block headers are written with the owner * set to sc->ip, so we must switch the owner here for the lookup.
*/ if (dp == sc->tempip)
args.owner = sc->ip->i_ino;
/* * Try to grab the IOLOCK and ILOCK of sc->ip and ip, returning @ip's lock * state. The caller may have a transaction, so we must use trylock for both * IOLOCKs.
*/ staticinlineunsignedint
xchk_dir_trylock_both( struct xfs_scrub *sc, struct xfs_inode *ip)
{ if (!xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL)) return 0;
if (!xfs_ilock_nowait(ip, XFS_IOLOCK_SHARED)) goto parent_iolock;
xchk_ilock(sc, XFS_ILOCK_EXCL); if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) goto parent_ilock;
/* * Try for a limited time to grab the IOLOCK and ILOCK of both the scrub target * (@sc->ip) and the inode at the other end (@ip) of a directory or parent * pointer link so that we can check that link. * * We do not know ahead of time that the directory tree is /not/ corrupt, so we * cannot use the "lock two inode" functions because we do not know that there * is not a racing thread trying to take the locks in opposite order. First * take IOLOCK_EXCL of the scrub target, and then try to take IOLOCK_SHARED * of @ip to synchronize with the VFS. Next, take ILOCK_EXCL of the scrub * target and @ip to synchronize with XFS. * * If the trylocks succeed, *lockmode will be set to the locks held for @ip; * @sc->ilock_flags will be set for the locks held for @sc->ip; and zero will * be returned. If not, returns -EDEADLOCK to try again; or -ETIMEDOUT if * XCHK_TRY_HARDER was set. Returns -EINTR if the process has been killed.
*/ int
xchk_dir_trylock_for_pptrs( struct xfs_scrub *sc, struct xfs_inode *ip, unsignedint *lockmode)
{ unsignedint nr; int error = 0;
ASSERT(sc->ilock_flags == 0);
for (nr = 0; nr < HZ; nr++) {
*lockmode = xchk_dir_trylock_both(sc, ip); if (*lockmode) return 0;
if (xchk_should_terminate(sc, &error)) return error;
delay(1);
}
if (sc->flags & XCHK_TRY_HARDER) {
xchk_set_incomplete(sc); return -ETIMEDOUT;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.