Skip to content

Commit 3f31406

Browse files
author
Darrick J. Wong
committed
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: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 3705691 commit 3f31406

File tree

11 files changed

+927
-8
lines changed

11 files changed

+927
-8
lines changed

fs/xfs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ xfs-y += $(addprefix scrub/, \
204204
bmap_repair.o \
205205
cow_repair.o \
206206
dir_repair.o \
207+
dirtree_repair.o \
207208
findparent.o \
208209
fscounters_repair.o \
209210
ialloc_repair.o \

fs/xfs/scrub/dirtree.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "scrub/xfblob.h"
2727
#include "scrub/listxattr.h"
2828
#include "scrub/trace.h"
29+
#include "scrub/repair.h"
30+
#include "scrub/orphanage.h"
2931
#include "scrub/dirtree.h"
3032

3133
/*
@@ -95,6 +97,12 @@ xchk_setup_dirtree(
9597

9698
xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
9799

100+
if (xchk_could_repair(sc)) {
101+
error = xrep_setup_dirtree(sc);
102+
if (error)
103+
return error;
104+
}
105+
98106
dl = kvzalloc(sizeof(struct xchk_dirtree), XCHK_GFP_FLAGS);
99107
if (!dl)
100108
return -ENOMEM;
@@ -104,6 +112,7 @@ xchk_setup_dirtree(
104112
INIT_LIST_HEAD(&dl->path_list);
105113
dl->root_ino = NULLFSINO;
106114
dl->scan_ino = NULLFSINO;
115+
dl->parent_ino = NULLFSINO;
107116

108117
mutex_init(&dl->lock);
109118

@@ -142,7 +151,7 @@ xchk_setup_dirtree(
142151
* Add the parent pointer described by @dl->pptr to the given path as a new
143152
* step. Returns -ELNRNG if the path is too deep.
144153
*/
145-
STATIC int
154+
int
146155
xchk_dirpath_append(
147156
struct xchk_dirtree *dl,
148157
struct xfs_inode *ip,
@@ -609,6 +618,22 @@ xchk_dirpath_step_is_stale(
609618
if (memcmp(dl->hook_xname.name, p->name->name, p->name->len) != 0)
610619
return 0;
611620

621+
/*
622+
* If the update comes from the repair code itself, walk the state
623+
* machine forward.
624+
*/
625+
if (p->ip->i_ino == dl->scan_ino &&
626+
path->outcome == XREP_DIRPATH_ADOPTING) {
627+
xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_ADOPTED);
628+
return 0;
629+
}
630+
631+
if (p->ip->i_ino == dl->scan_ino &&
632+
path->outcome == XREP_DIRPATH_DELETING) {
633+
xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETED);
634+
return 0;
635+
}
636+
612637
/* Exact match, scan data is out of date. */
613638
trace_xchk_dirpath_changed(dl->sc, path->path_nr, step_nr, p->dp,
614639
p->ip, p->name);
@@ -747,7 +772,7 @@ xchk_dirtree_load_path(
747772
* path was too deep; -ENOSR if there were too many parent pointers; or
748773
* a negative errno.
749774
*/
750-
STATIC int
775+
int
751776
xchk_dirtree_find_paths_to_root(
752777
struct xchk_dirtree *dl)
753778
{
@@ -819,7 +844,7 @@ xchk_dirtree_find_paths_to_root(
819844
* Figure out what to do with the paths we tried to find. Do not call this
820845
* if the scan results are stale.
821846
*/
822-
STATIC void
847+
void
823848
xchk_dirtree_evaluate(
824849
struct xchk_dirtree *dl,
825850
struct xchk_dirtree_outcomes *oc)
@@ -856,6 +881,13 @@ xchk_dirtree_evaluate(
856881
/* This path got all the way to the root. */
857882
oc->good++;
858883
break;
884+
case XREP_DIRPATH_DELETING:
885+
case XREP_DIRPATH_DELETED:
886+
case XREP_DIRPATH_ADOPTING:
887+
case XREP_DIRPATH_ADOPTED:
888+
/* These should not be in progress! */
889+
ASSERT(0);
890+
break;
859891
}
860892
}
861893

fs/xfs/scrub/dirtree.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ enum xchk_dirpath_outcome {
2626
XCHK_DIRPATH_LOOP, /* cycle detected further up */
2727
XCHK_DIRPATH_STALE, /* path is stale */
2828
XCHK_DIRPATH_OK, /* path reaches the root */
29+
30+
XREP_DIRPATH_DELETING, /* path is being deleted */
31+
XREP_DIRPATH_DELETED, /* path has been deleted */
32+
XREP_DIRPATH_ADOPTING, /* path is being adopted */
33+
XREP_DIRPATH_ADOPTED, /* path has been adopted */
2934
};
3035

3136
/*
@@ -64,6 +69,9 @@ struct xchk_dirtree_outcomes {
6469

6570
/* Number of XCHK_DIRPATH_OK */
6671
unsigned int good;
72+
73+
/* Directory needs to be added to lost+found */
74+
bool needs_adoption;
6775
};
6876

6977
struct xchk_dirtree {
@@ -79,6 +87,14 @@ struct xchk_dirtree {
7987
*/
8088
xfs_ino_t scan_ino;
8189

90+
/*
91+
* If we start deleting redundant paths to this subdirectory, this is
92+
* the inode number of the surviving parent and the dotdot entry will
93+
* be set to this value. If the value is NULLFSINO, then use @root_ino
94+
* as a stand-in until the orphanage can adopt the subdirectory.
95+
*/
96+
xfs_ino_t parent_ino;
97+
8298
/* Scratch buffer for scanning pptr xattrs */
8399
struct xfs_parent_rec pptr_rec;
84100
struct xfs_da_args pptr_args;
@@ -87,12 +103,18 @@ struct xchk_dirtree {
87103
struct xfs_name xname;
88104
char namebuf[MAXNAMELEN];
89105

106+
/* Information for reparenting this directory. */
107+
struct xrep_adoption adoption;
108+
90109
/*
91110
* Hook into directory updates so that we can receive live updates
92111
* from other writer threads.
93112
*/
94113
struct xfs_dir_hook dhook;
95114

115+
/* Parent pointer update arguments. */
116+
struct xfs_parent_args ppargs;
117+
96118
/* lock for everything below here */
97119
struct mutex lock;
98120

@@ -146,4 +168,11 @@ xchk_dirtree_parentless(const struct xchk_dirtree *dl)
146168
return false;
147169
}
148170

171+
int xchk_dirtree_find_paths_to_root(struct xchk_dirtree *dl);
172+
int xchk_dirpath_append(struct xchk_dirtree *dl, struct xfs_inode *ip,
173+
struct xchk_dirpath *path, const struct xfs_name *name,
174+
const struct xfs_parent_rec *pptr);
175+
void xchk_dirtree_evaluate(struct xchk_dirtree *dl,
176+
struct xchk_dirtree_outcomes *oc);
177+
149178
#endif /* __XFS_SCRUB_DIRTREE_H__ */

0 commit comments

Comments
 (0)