diff options
author | Darrick J. Wong <djwong@kernel.org> | 2021-03-17 16:31:31 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2021-03-25 17:08:55 -0700 |
commit | 8b1498533cb77f0625f460963cc090d2a7207173 (patch) | |
tree | 07e1db2712da2d6f881a31b235eac6cc815bc71d /fs/xfs/scrub | |
parent | 8e45025becb250087a4bb20e82f549a9297ec15f (diff) |
xfs: teach repair to fix file nlinksscrub-nlinks_2021-03-25
Fix the nlinks now too.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs/scrub')
-rw-r--r-- | fs/xfs/scrub/nlinks_repair.c | 144 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.h | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/scrub.c | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/trace.h | 3 |
4 files changed, 150 insertions, 1 deletions
diff --git a/fs/xfs/scrub/nlinks_repair.c b/fs/xfs/scrub/nlinks_repair.c new file mode 100644 index 000000000000..81628172059e --- /dev/null +++ b/fs/xfs/scrub/nlinks_repair.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@kernel.org> + */ +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_log_format.h" +#include "xfs_trans.h" +#include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_bmap_util.h" +#include "xfs_iwalk.h" +#include "xfs_ialloc.h" +#include "xfs_sb.h" +#include "scrub/scrub.h" +#include "scrub/common.h" +#include "scrub/repair.h" +#include "scrub/array.h" +#include "scrub/nlinks.h" +#include "scrub/trace.h" + +/* + * Live Inode Link Count Repair + * ============================ + * + * Use the live inode link count information that we collected to replace the + * nlink values of the incore inodes. A scrub->repair cycle should have left + * the live data and hooks active, so this is safe so long as we make sure the + * inode is locked. + */ + +/* Commit new counters to an inode. */ +static int +xrep_nlinks_commit_inode( + struct xfs_mount *mp, + struct xfs_trans *tp, + xfs_ino_t ino, + void *data) +{ + struct xchk_nlinks *xnc = data; + struct xfs_inode *ip = NULL; + xfs_nlink_t live_nlink; + int error; + + error = xfs_iget(mp, tp, ino, XFS_IGET_UNTRUSTED | XFS_IGET_UNLINKED, + XFS_ILOCK_EXCL, &ip); + if (error == -ENOENT || error == -EINVAL) { + /* Inode wasn't found, so we'll check for zero nlink. */ + error = 0; + } + if (error) + return error; + + mutex_lock(&xnc->lock); + if (xnc->hook_dead) { + error = -ECANCELED; + goto out_unlock; + } + error = xchk_nlinks_get_shadow_count(xnc, ino, &live_nlink); + if (error) + goto out_unlock; + + if (ip == NULL) { + /* + * We couldn't get the inode, so the link count had better be + * zero! This means we cannot fix the filesystem. + */ + if (live_nlink != 0) { + trace_xrep_nlinks_unfixable_inode(mp, ino, 0, + live_nlink); + error = -EFSCORRUPTED; + } + goto out_unlock; + } + + /* Commit the new link count, if necessary. */ + if (live_nlink != VFS_I(ip)->i_nlink) { + trace_xrep_nlinks_commit_inode(mp, ino, VFS_I(ip)->i_nlink, + live_nlink); + + xfs_trans_ijoin(tp, ip, 0); + set_nlink(VFS_I(ip), live_nlink); + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + error = xrep_roll_trans(xnc->sc); + } + +out_unlock: + if (error) + xnc->hook_dead = true; + mutex_unlock(&xnc->lock); + if (ip) { + xfs_iunlock(ip, XFS_ILOCK_EXCL); + xchk_irele(xnc->sc, ip); + } + return error; +} + +/* Commit the new inode link counters. */ +int +xrep_nlinks( + struct xfs_scrub *sc) +{ + struct xchk_nlinks *xnc = sc->buf; + uint64_t nr = 0; + xfs_nlink_t nlink; + int error; + + /* Update the link count of every inode that the inobt knows about. */ + error = xfs_iwalk(sc->mp, sc->tp, 0, XFS_IWALK_METADIR, + xrep_nlinks_commit_inode, 0, xnc); + if (error) + return error; + + /* + * Make a second pass to find inodes for which we have positive link + * count but don't seem to exist on disk. We cannot resuscitate dead + * inodes, but we can at least signal failure. + */ + mutex_lock(&xnc->lock); + while (!(error = xfbma_iter_get(xnc->nlinks, &nr, &nlink))) { + mutex_unlock(&xnc->lock); + + if (xchk_should_terminate(xnc->sc, &error)) + return error; + + error = xrep_nlinks_commit_inode(sc->mp, sc->tp, nr - 1, xnc); + if (error) + return error; + + mutex_lock(&xnc->lock); + } + mutex_unlock(&xnc->lock); + + /* ENODATA means we hit the end of the array. */ + if (error == -ENODATA) + return 0; + + return error; +} diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index c8a4daf67fb2..640f7eb0c2d5 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h @@ -131,6 +131,7 @@ int xrep_fscounters(struct xfs_scrub *sc); int xrep_xattr(struct xfs_scrub *sc); int xrep_dir(struct xfs_scrub *sc); int xrep_parent(struct xfs_scrub *sc); +int xrep_nlinks(struct xfs_scrub *sc); #ifdef CONFIG_XFS_QUOTA int xrep_quota(struct xfs_scrub *sc); @@ -277,6 +278,7 @@ xrep_rmapbt_setup( #define xrep_rtbitmap xrep_notsupported #define xrep_rtrmapbt xrep_notsupported #define xrep_rtrefcountbt xrep_notsupported +#define xrep_nlinks xrep_notsupported #endif /* CONFIG_XFS_ONLINE_REPAIR */ diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 1d51a6002ede..c17b0ef24a0e 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -409,7 +409,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { .type = ST_FS, .setup = xchk_setup_nlinks, .scrub = xchk_nlinks, - .repair = xrep_notsupported, + .repair = xrep_nlinks, }, }; diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index bcfdcb4ccc16..bb9dc116dec3 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -1774,6 +1774,9 @@ TRACE_EVENT(xrep_rtrefc_found, __entry->refcount) ) +DEFINE_SCRUB_NLINK_DIFF_EVENT(xrep_nlinks_commit_inode); +DEFINE_SCRUB_NLINK_DIFF_EVENT(xrep_nlinks_unfixable_inode); + #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */ |