Commit 3f31406a authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: fix corruptions in the directory tree

Repair corruptions in the directory tree itself.  Cycles are broken by
removing an incoming parent->child link.  Multiply-owned directories are
fixed by pruning the extra parent -> child links  Disconnected subtrees
are reconnected to the lost and found.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 37056912
......@@ -204,6 +204,7 @@ xfs-y += $(addprefix scrub/, \
bmap_repair.o \
cow_repair.o \
dir_repair.o \
dirtree_repair.o \
findparent.o \
fscounters_repair.o \
ialloc_repair.o \
......
......@@ -26,6 +26,8 @@
#include "scrub/xfblob.h"
#include "scrub/listxattr.h"
#include "scrub/trace.h"
#include "scrub/repair.h"
#include "scrub/orphanage.h"
#include "scrub/dirtree.h"
/*
......@@ -95,6 +97,12 @@ xchk_setup_dirtree(
xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
if (xchk_could_repair(sc)) {
error = xrep_setup_dirtree(sc);
if (error)
return error;
}
dl = kvzalloc(sizeof(struct xchk_dirtree), XCHK_GFP_FLAGS);
if (!dl)
return -ENOMEM;
......@@ -104,6 +112,7 @@ xchk_setup_dirtree(
INIT_LIST_HEAD(&dl->path_list);
dl->root_ino = NULLFSINO;
dl->scan_ino = NULLFSINO;
dl->parent_ino = NULLFSINO;
mutex_init(&dl->lock);
......@@ -142,7 +151,7 @@ xchk_setup_dirtree(
* Add the parent pointer described by @dl->pptr to the given path as a new
* step. Returns -ELNRNG if the path is too deep.
*/
STATIC int
int
xchk_dirpath_append(
struct xchk_dirtree *dl,
struct xfs_inode *ip,
......@@ -609,6 +618,22 @@ xchk_dirpath_step_is_stale(
if (memcmp(dl->hook_xname.name, p->name->name, p->name->len) != 0)
return 0;
/*
* If the update comes from the repair code itself, walk the state
* machine forward.
*/
if (p->ip->i_ino == dl->scan_ino &&
path->outcome == XREP_DIRPATH_ADOPTING) {
xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_ADOPTED);
return 0;
}
if (p->ip->i_ino == dl->scan_ino &&
path->outcome == XREP_DIRPATH_DELETING) {
xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETED);
return 0;
}
/* Exact match, scan data is out of date. */
trace_xchk_dirpath_changed(dl->sc, path->path_nr, step_nr, p->dp,
p->ip, p->name);
......@@ -747,7 +772,7 @@ xchk_dirtree_load_path(
* path was too deep; -ENOSR if there were too many parent pointers; or
* a negative errno.
*/
STATIC int
int
xchk_dirtree_find_paths_to_root(
struct xchk_dirtree *dl)
{
......@@ -819,7 +844,7 @@ xchk_dirtree_find_paths_to_root(
* Figure out what to do with the paths we tried to find. Do not call this
* if the scan results are stale.
*/
STATIC void
void
xchk_dirtree_evaluate(
struct xchk_dirtree *dl,
struct xchk_dirtree_outcomes *oc)
......@@ -856,6 +881,13 @@ xchk_dirtree_evaluate(
/* This path got all the way to the root. */
oc->good++;
break;
case XREP_DIRPATH_DELETING:
case XREP_DIRPATH_DELETED:
case XREP_DIRPATH_ADOPTING:
case XREP_DIRPATH_ADOPTED:
/* These should not be in progress! */
ASSERT(0);
break;
}
}
......
......@@ -26,6 +26,11 @@ enum xchk_dirpath_outcome {
XCHK_DIRPATH_LOOP, /* cycle detected further up */
XCHK_DIRPATH_STALE, /* path is stale */
XCHK_DIRPATH_OK, /* path reaches the root */
XREP_DIRPATH_DELETING, /* path is being deleted */
XREP_DIRPATH_DELETED, /* path has been deleted */
XREP_DIRPATH_ADOPTING, /* path is being adopted */
XREP_DIRPATH_ADOPTED, /* path has been adopted */
};
/*
......@@ -64,6 +69,9 @@ struct xchk_dirtree_outcomes {
/* Number of XCHK_DIRPATH_OK */
unsigned int good;
/* Directory needs to be added to lost+found */
bool needs_adoption;
};
struct xchk_dirtree {
......@@ -79,6 +87,14 @@ struct xchk_dirtree {
*/
xfs_ino_t scan_ino;
/*
* If we start deleting redundant paths to this subdirectory, this is
* the inode number of the surviving parent and the dotdot entry will
* be set to this value. If the value is NULLFSINO, then use @root_ino
* as a stand-in until the orphanage can adopt the subdirectory.
*/
xfs_ino_t parent_ino;
/* Scratch buffer for scanning pptr xattrs */
struct xfs_parent_rec pptr_rec;
struct xfs_da_args pptr_args;
......@@ -87,12 +103,18 @@ struct xchk_dirtree {
struct xfs_name xname;
char namebuf[MAXNAMELEN];
/* Information for reparenting this directory. */
struct xrep_adoption adoption;
/*
* Hook into directory updates so that we can receive live updates
* from other writer threads.
*/
struct xfs_dir_hook dhook;
/* Parent pointer update arguments. */
struct xfs_parent_args ppargs;
/* lock for everything below here */
struct mutex lock;
......@@ -146,4 +168,11 @@ xchk_dirtree_parentless(const struct xchk_dirtree *dl)
return false;
}
int xchk_dirtree_find_paths_to_root(struct xchk_dirtree *dl);
int xchk_dirpath_append(struct xchk_dirtree *dl, struct xfs_inode *ip,
struct xchk_dirpath *path, const struct xfs_name *name,
const struct xfs_parent_rec *pptr);
void xchk_dirtree_evaluate(struct xchk_dirtree *dl,
struct xchk_dirtree_outcomes *oc);
#endif /* __XFS_SCRUB_DIRTREE_H__ */
This diff is collapsed.
......@@ -570,6 +570,12 @@ xrep_adoption_move(
xfs_bumplink(sc->tp, sc->orphanage);
xfs_trans_log_inode(sc->tp, sc->orphanage, XFS_ILOG_CORE);
/* Bump the link count of the child. */
if (adopt->bump_child_nlink) {
xfs_bumplink(sc->tp, sc->ip);
xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
}
/* Replace the dotdot entry if the child is a subdirectory. */
if (isdir) {
error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,
......
......@@ -60,6 +60,14 @@ struct xrep_adoption {
/* Block reservations for orphanage and child (if directory). */
unsigned int orphanage_blkres;
unsigned int child_blkres;
/*
* Does the caller want us to bump the child link count? This is not
* needed when reattaching files that have become disconnected but have
* nlink > 1. It is necessary when changing the directory tree
* structure.
*/
bool bump_child_nlink:1;
};
bool xrep_orphanage_can_adopt(struct xfs_scrub *sc);
......
......@@ -95,6 +95,7 @@ int xrep_setup_directory(struct xfs_scrub *sc);
int xrep_setup_parent(struct xfs_scrub *sc);
int xrep_setup_nlinks(struct xfs_scrub *sc);
int xrep_setup_symlink(struct xfs_scrub *sc, unsigned int *resblks);
int xrep_setup_dirtree(struct xfs_scrub *sc);
/* Repair setup functions */
int xrep_setup_ag_allocbt(struct xfs_scrub *sc);
......@@ -132,6 +133,7 @@ int xrep_xattr(struct xfs_scrub *sc);
int xrep_directory(struct xfs_scrub *sc);
int xrep_parent(struct xfs_scrub *sc);
int xrep_symlink(struct xfs_scrub *sc);
int xrep_dirtree(struct xfs_scrub *sc);
#ifdef CONFIG_XFS_RT
int xrep_rtbitmap(struct xfs_scrub *sc);
......@@ -205,6 +207,7 @@ xrep_setup_nothing(
#define xrep_setup_directory xrep_setup_nothing
#define xrep_setup_parent xrep_setup_nothing
#define xrep_setup_nlinks xrep_setup_nothing
#define xrep_setup_dirtree xrep_setup_nothing
#define xrep_setup_inode(sc, imap) ((void)0)
......@@ -239,6 +242,7 @@ static inline int xrep_setup_symlink(struct xfs_scrub *sc, unsigned int *x)
#define xrep_directory xrep_notsupported
#define xrep_parent xrep_notsupported
#define xrep_symlink xrep_notsupported
#define xrep_dirtree xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */
......
......@@ -441,7 +441,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.setup = xchk_setup_dirtree,
.scrub = xchk_dirtree,
.has = xfs_has_parent,
.repair = xrep_notsupported,
.repair = xrep_dirtree,
},
};
......
......@@ -1685,6 +1685,10 @@ TRACE_DEFINE_ENUM(XCHK_DIRPATH_CORRUPT);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_LOOP);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_STALE);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_OK);
TRACE_DEFINE_ENUM(XREP_DIRPATH_DELETING);
TRACE_DEFINE_ENUM(XREP_DIRPATH_DELETED);
TRACE_DEFINE_ENUM(XREP_DIRPATH_ADOPTING);
TRACE_DEFINE_ENUM(XREP_DIRPATH_ADOPTED);
#define XCHK_DIRPATH_OUTCOME_STRINGS \
{ XCHK_DIRPATH_SCANNING, "scanning" }, \
......@@ -1692,7 +1696,11 @@ TRACE_DEFINE_ENUM(XCHK_DIRPATH_OK);
{ XCHK_DIRPATH_CORRUPT, "corrupt" }, \
{ XCHK_DIRPATH_LOOP, "loop" }, \
{ XCHK_DIRPATH_STALE, "stale" }, \
{ XCHK_DIRPATH_OK, "ok" }
{ XCHK_DIRPATH_OK, "ok" }, \
{ XREP_DIRPATH_DELETING, "deleting" }, \
{ XREP_DIRPATH_DELETED, "deleted" }, \
{ XREP_DIRPATH_ADOPTING, "adopting" }, \
{ XREP_DIRPATH_ADOPTED, "adopted" }
DECLARE_EVENT_CLASS(xchk_dirpath_outcome_class,
TP_PROTO(struct xfs_scrub *sc, unsigned long long path_nr,
......@@ -1738,6 +1746,7 @@ DECLARE_EVENT_CLASS(xchk_dirtree_evaluate_class,
__field(unsigned int, bad)
__field(unsigned int, suspect)
__field(unsigned int, good)
__field(bool, needs_adoption)
),
TP_fast_assign(
__entry->dev = dl->sc->mp->m_super->s_dev;
......@@ -1747,15 +1756,17 @@ DECLARE_EVENT_CLASS(xchk_dirtree_evaluate_class,
__entry->bad = oc->bad;
__entry->suspect = oc->suspect;
__entry->good = oc->good;
__entry->needs_adoption = oc->needs_adoption ? 1 : 0;
),
TP_printk("dev %d:%d ino 0x%llx rootino 0x%llx nr_paths %u bad %u suspect %u good %u",
TP_printk("dev %d:%d ino 0x%llx rootino 0x%llx nr_paths %u bad %u suspect %u good %u adopt? %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->rootino,
__entry->nr_paths,
__entry->bad,
__entry->suspect,
__entry->good)
__entry->good,
__entry->needs_adoption)
);
#define DEFINE_XCHK_DIRTREE_EVALUATE_EVENT(name) \
DEFINE_EVENT(xchk_dirtree_evaluate_class, name, \
......@@ -3181,6 +3192,7 @@ DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_child);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_alias);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_dentry);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_invalidate_child);
DEFINE_REPAIR_DENTRY_EVENT(xrep_dirtree_delete_child);
TRACE_EVENT(xrep_symlink_salvage_target,
TP_PROTO(struct xfs_inode *ip, char *target, unsigned int targetlen),
......@@ -3483,6 +3495,11 @@ TRACE_EVENT(xrep_iunlink_commit_bucket,
__entry->agino)
);
DEFINE_XCHK_DIRPATH_OUTCOME_EVENT(xrep_dirpath_set_outcome);
DEFINE_XCHK_DIRTREE_EVENT(xrep_dirtree_delete_path);
DEFINE_XCHK_DIRTREE_EVENT(xrep_dirtree_create_adoption);
DEFINE_XCHK_DIRTREE_EVALUATE_EVENT(xrep_dirtree_decided_fate);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#endif /* _TRACE_XFS_SCRUB_TRACE_H */
......
......@@ -893,7 +893,7 @@ xfs_init_new_inode(
* link count to go to zero, move the inode to AGI unlinked list so that it can
* be freed when the last active reference goes away via xfs_inactive().
*/
static int /* error */
int
xfs_droplink(
struct xfs_trans *tp,
struct xfs_inode *ip)
......
......@@ -626,6 +626,7 @@ void xfs_end_io(struct work_struct *work);
int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
void xfs_iunlock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
void xfs_iunlock2_remapping(struct xfs_inode *ip1, struct xfs_inode *ip2);
int xfs_droplink(struct xfs_trans *tp, struct xfs_inode *ip);
void xfs_bumplink(struct xfs_trans *tp, struct xfs_inode *ip);
void xfs_lock_inodes(struct xfs_inode **ips, int inodes, uint lock_mode);
void xfs_sort_inodes(struct xfs_inode **i_tab, unsigned int num_inodes);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment