mirror of
https://github.com/torvalds/linux.git
synced 2026-09-13 15:40:03 +02:00
xfs: handle reconnecting metadir subdirectories
A longstanding weakness of the metapath repair code is that it can only reattach non-directories to the metadata directory tree. Let's fix that by allowing reconnection of subdirectories. Note that with the initial users of metadir (rtgroups and quota), there's no way to mount a filesystem with broken /rtgroups or /quota subdirectories, so this code won't be all that useful until something adds deeper directory trees. But we shouldn't leave a logic bomb for those futures users wherein we get the link count wrong for a subdir. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
parent
cdc4a083ad
commit
9dd6cc92a7
|
|
@ -23,6 +23,7 @@
|
|||
#include "xfs_rtgroup.h"
|
||||
#include "xfs_rtrmap_btree.h"
|
||||
#include "xfs_rtrefcount_btree.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/trace.h"
|
||||
|
|
@ -348,12 +349,78 @@ xchk_metapath(
|
|||
}
|
||||
|
||||
#ifdef CONFIG_XFS_ONLINE_REPAIR
|
||||
/*
|
||||
* Given a directory @dp, an existing inode @ip, and a @name, link @ip into @dp
|
||||
* under the given @name.
|
||||
*/
|
||||
static int
|
||||
xrep_metadir_add_child(
|
||||
struct xchk_metapath *mpath,
|
||||
xfs_ino_t old_dotdot)
|
||||
{
|
||||
struct xfs_trans *tp = mpath->sc->tp;
|
||||
struct xfs_dir_update *du = &mpath->du;
|
||||
struct xfs_inode *dp = du->dp;
|
||||
const struct xfs_name *name = du->name;
|
||||
struct xfs_inode *ip = du->ip;
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
const unsigned int resblks = mpath->link_resblks;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* The metadata file shouldn't be on the unlinked list, but we'll fix
|
||||
* it if that is the case.
|
||||
*/
|
||||
if (VFS_I(ip)->i_nlink == 0) {
|
||||
struct xfs_perag *pag;
|
||||
|
||||
pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, I_INO(ip)));
|
||||
error = xfs_iunlink_remove(tp, pag, ip);
|
||||
xfs_perag_put(pag);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = xfs_dir_createname(tp, dp, name, I_INO(ip), resblks);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
|
||||
|
||||
xfs_bumplink(tp, ip);
|
||||
|
||||
/* update dotdot entry in child */
|
||||
if (S_ISDIR(VFS_I(ip)->i_mode)) {
|
||||
xfs_bumplink(tp, dp);
|
||||
|
||||
/* Replace the dotdot entry in the child */
|
||||
if (old_dotdot != I_INO(dp)) {
|
||||
error = xfs_dir_replace(tp, ip, &xfs_name_dotdot,
|
||||
I_INO(dp), resblks);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the child's parent pointer */
|
||||
if (du->ppargs) {
|
||||
error = xfs_parent_addname(tp, du->ppargs, dp, name, ip);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
xfs_dir_update_hook(dp, ip, 1, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the dirent represented by the final component of the path. */
|
||||
STATIC int
|
||||
xrep_metapath_link(
|
||||
struct xchk_metapath *mpath)
|
||||
{
|
||||
struct xfs_scrub *sc = mpath->sc;
|
||||
xfs_ino_t old_dotdot = NULLFSINO;
|
||||
int error;
|
||||
|
||||
mpath->du.dp = mpath->dp;
|
||||
mpath->du.name = &mpath->xname;
|
||||
|
|
@ -366,7 +433,21 @@ xrep_metapath_link(
|
|||
|
||||
trace_xrep_metapath_link(sc, mpath->path, mpath->dp, I_INO(sc->ip));
|
||||
|
||||
return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
|
||||
if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {
|
||||
error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,
|
||||
&old_dotdot);
|
||||
if (error && error != -ENOENT)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* subdir didn't give us a dotdot entry, so we just give up
|
||||
* and let the repair get marked as failed.
|
||||
*/
|
||||
if (old_dotdot == NULLFSINO)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return xrep_metadir_add_child(mpath, old_dotdot);
|
||||
}
|
||||
|
||||
/* Remove the dirent at the final component of the path. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user