/* Look for a parent pointer matching this dirent, if the child isn't busy. */ STATICint
xchk_dir_check_pptr_fast( struct xchk_dir *sd,
xfs_dir2_dataptr_t dapos, conststruct xfs_name *name, struct xfs_inode *ip)
{ struct xfs_scrub *sc = sd->sc; unsignedint lockmode; int error;
/* dot and dotdot entries do not have parent pointers */ if (xfs_dir2_samename(name, &xfs_name_dot) ||
xfs_dir2_samename(name, &xfs_name_dotdot)) return0;
/* No self-referential non-dot or dotdot dirents. */ if (ip == sc->ip) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); return -ECANCELED;
}
/* Try to lock the inode. */
lockmode = xchk_dir_lock_child(sc, ip); if (!lockmode) { struct xchk_dirent save_de = {
.namelen = name->len,
.ino = ip->i_ino,
};
/* Couldn't lock the inode, so save the dirent for later. */
trace_xchk_dir_defer(sc->ip, name, ip->i_ino);
if (xchk_should_terminate(sc, &error)) return error;
/* Does this inode number make sense? */ if (!xfs_verify_dir_ino(mp, ino)) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); return -ECANCELED;
}
/* Does this name make sense? */ if (!xfs_dir2_namecheck(name->name, name->len)) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); return -ECANCELED;
}
if (xfs_dir2_samename(name, &xfs_name_dot)) { /* If this is "." then check that the inum matches the dir. */ if (ino != dp->i_ino)
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
} elseif (xfs_dir2_samename(name, &xfs_name_dotdot)) { /* *Ifthisis".."intherootinode,checkthattheinum *matchesthisdir.
*/ if (xchk_inode_is_dirtree_root(dp) && ino != dp->i_ino)
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
}
/* Verify that we can look up this name by hash. */
error = xchk_dir_lookup(sc, dp, name, &lookup_ino); /* ENOENT means the hash lookup failed and the dir is corrupt */ if (error == -ENOENT)
error = -EFSCORRUPTED; if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error)) goto out; if (lookup_ino != ino) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); return -ECANCELED;
}
/* Unused entry is shorter than any of the bestfrees */ if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length)) return;
for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--) if (dup_length == be16_to_cpu(dfp->length)) return;
/* Unused entry should be in the bestfrees but wasn't found. */
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
}
/* Check free space info in a directory data block. */ STATICint
xchk_directory_data_bestfree( struct xfs_scrub *sc,
xfs_dablk_t lblk, bool is_block)
{ struct xfs_dir2_data_unused *dup; struct xfs_dir2_data_free *dfp; struct xfs_buf *bp; struct xfs_dir2_data_free *bf; struct xfs_mount *mp = sc->mp;
u16 tag; unsignedint nr_bestfrees = 0; unsignedint nr_frees = 0; unsignedint smallest_bestfree; int newlen; unsignedint offset; unsignedint end; int error;
if (is_block) { /* dir block format */ if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET))
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
error = xfs_dir3_block_read(sc->tp, sc->ip, sc->ip->i_ino, &bp);
} else { /* dir data format */
error = xfs_dir3_data_read(sc->tp, sc->ip, sc->ip->i_ino, lblk, 0, &bp);
} if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) goto out;
xchk_buffer_recheck(sc, bp);
/* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) goto out_buf;
/* Do the bestfrees correspond to actual free space? */
bf = xfs_dir2_data_bestfree_p(mp, bp->b_addr);
smallest_bestfree = UINT_MAX; for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
offset = be16_to_cpu(dfp->offset); if (offset == 0) continue; if (offset >= mp->m_dir_geo->blksize) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); goto out_buf;
}
dup = bp->b_addr + offset;
tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
/* bestfree doesn't match the entry it points at? */ if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) ||
be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) ||
tag != offset) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); goto out_buf;
}
/* bestfree records should be ordered largest to smallest */ if (smallest_bestfree < be16_to_cpu(dfp->length)) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); goto out_buf;
}
/* Make sure the bestfrees are actually the best free spaces. */
offset = mp->m_dir_geo->data_entry_offset;
end = xfs_dir3_data_end_offset(mp->m_dir_geo, bp->b_addr);
/* Iterate the entries, stopping when we hit or go past the end. */ while (offset < end) {
dup = bp->b_addr + offset;
/* Skip real entries */ if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) { struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
/* Move on. */
newlen = be16_to_cpu(dup->length); if (newlen <= 0) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); goto out_buf;
}
offset += newlen; if (offset <= end)
nr_frees++;
}
/* We're required to fill all the space. */ if (offset != end)
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
/* Did we see at least as many free slots as there are bestfrees? */ if (nr_frees < nr_bestfrees)
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
out_buf:
xfs_trans_brelse(sc->tp, bp);
out: return error;
}
/* Is the leaf count even remotely sane? */ if (leafhdr.count > geo->leaf_max_ents) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); goto out;
}
/* Leaves and bests don't overlap in leaf format. */ if ((char *)&leafhdr.ents[leafhdr.count] > (char *)bestp) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); goto out;
}
/* Check hash value order, count stale entries. */ for (i = 0; i < leafhdr.count; i++) {
hash = be32_to_cpu(leafhdr.ents[i].hashval); if (i > 0 && lasthash > hash)
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
lasthash = hash; if (leafhdr.ents[i].address ==
cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
stale++;
} if (leafhdr.stale != stale)
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) goto out;
/* Check all the bestfree entries. */ for (i = 0; i < bestcount; i++, bestp++) {
best = be16_to_cpu(*bestp);
error = xfs_dir3_data_read(sc->tp, sc->ip, args->owner,
xfs_dir2_db_to_da(args->geo, i),
XFS_DABUF_MAP_HOLE_OK, &dbp); if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
&error)) break;
if (!dbp) { if (best != NULLDATAOFF) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
lblk); break;
} continue;
}
/* Is this a block dir? */ if (xfs_dir2_format(&args, &error) == XFS_DIR2_FMT_BLOCK)
is_block = true; if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) goto out;
/* Iterate all the data extents in the directory... */
found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) { /* No more data blocks... */ if (got.br_startoff >= leaf_lblk) break;
/* Check that the deferred dirent still exists. */ if (sd->need_revalidate) {
error = xchk_dir_revalidate_dirent(sd, xname, dirent->ino); if (error == -ENOENT) return0; if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
&error)) return error;
}
/* Check all the dirents that we deferred the first time around. */ STATICint
xchk_dir_finish_slow_dirents( struct xchk_dir *sd)
{
xfarray_idx_t array_cur; int error;
/* Look up every name in this directory by hash. */
error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, sd); if (error == -ECANCELED)
error = 0; if (error) goto out_names;
if (xfs_has_parent(sc->mp)) {
error = xchk_dir_finish_slow_dirents(sd); if (error == -ETIMEDOUT) { /* Couldn't grab a lock, scrub was marked incomplete */
error = 0; goto out_names;
} if (error) goto out_names;
}
out_names: if (sd->dir_names)
xfblob_destroy(sd->dir_names);
out_entries: if (sd->dir_entries)
xfarray_destroy(sd->dir_entries);
out_sd:
kvfree(sd); if (error) return error;
/* If the dir is clean, it is clearly not zapped. */
xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_DIR_ZAPPED); return0;
}
/* *Decideifthisdirectoryhasbeenzappedtosatisfytheinodeandifork *verifiers.Checkingandrepairingshouldbepostponeduntilthedirectory *isfixed.
*/ bool
xchk_dir_looks_zapped( struct xfs_inode *dp)
{ /* Repair zapped this dir's data fork a short time ago */ if (xfs_ifork_zapped(dp, XFS_DATA_FORK)) returntrue;
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.