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 <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
37056912d5
commit
3f31406aef
@ -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 @@ out_dl:
|
||||
* 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__ */
|
||||
|
821
fs/xfs/scrub/dirtree_repair.c
Normal file
821
fs/xfs/scrub/dirtree_repair.c
Normal file
@ -0,0 +1,821 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023-2024 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_trans_space.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_icache.h"
|
||||
#include "xfs_dir2.h"
|
||||
#include "xfs_dir2_priv.h"
|
||||
#include "xfs_attr.h"
|
||||
#include "xfs_parent.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/bitmap.h"
|
||||
#include "scrub/ino_bitmap.h"
|
||||
#include "scrub/xfile.h"
|
||||
#include "scrub/xfarray.h"
|
||||
#include "scrub/xfblob.h"
|
||||
#include "scrub/listxattr.h"
|
||||
#include "scrub/trace.h"
|
||||
#include "scrub/repair.h"
|
||||
#include "scrub/orphanage.h"
|
||||
#include "scrub/dirtree.h"
|
||||
#include "scrub/readdir.h"
|
||||
|
||||
/*
|
||||
* Directory Tree Structure Repairs
|
||||
* ================================
|
||||
*
|
||||
* If we decide that the directory being scanned is participating in a
|
||||
* directory loop, the only change we can make is to remove directory entries
|
||||
* pointing down to @sc->ip. If that leaves it with no parents, the directory
|
||||
* should be adopted by the orphanage.
|
||||
*/
|
||||
|
||||
/* Set up to repair directory loops. */
|
||||
int
|
||||
xrep_setup_dirtree(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
return xrep_orphanage_try_create(sc);
|
||||
}
|
||||
|
||||
/* Change the outcome of this path. */
|
||||
static inline void
|
||||
xrep_dirpath_set_outcome(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirpath *path,
|
||||
enum xchk_dirpath_outcome outcome)
|
||||
{
|
||||
trace_xrep_dirpath_set_outcome(dl->sc, path->path_nr, path->nr_steps,
|
||||
outcome);
|
||||
|
||||
path->outcome = outcome;
|
||||
}
|
||||
|
||||
/* Delete all paths. */
|
||||
STATIC void
|
||||
xrep_dirtree_delete_all_paths(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirtree_outcomes *oc)
|
||||
{
|
||||
struct xchk_dirpath *path;
|
||||
|
||||
xchk_dirtree_for_each_path(dl, path) {
|
||||
switch (path->outcome) {
|
||||
case XCHK_DIRPATH_CORRUPT:
|
||||
case XCHK_DIRPATH_LOOP:
|
||||
oc->suspect--;
|
||||
oc->bad++;
|
||||
xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
|
||||
break;
|
||||
case XCHK_DIRPATH_OK:
|
||||
oc->good--;
|
||||
oc->bad++;
|
||||
xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(oc->suspect == 0);
|
||||
ASSERT(oc->good == 0);
|
||||
}
|
||||
|
||||
/* Since this is the surviving path, set the dotdot entry to this value. */
|
||||
STATIC void
|
||||
xrep_dirpath_retain_parent(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirpath *path)
|
||||
{
|
||||
struct xchk_dirpath_step step;
|
||||
int error;
|
||||
|
||||
error = xfarray_load(dl->path_steps, path->first_step, &step);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
dl->parent_ino = be64_to_cpu(step.pptr_rec.p_ino);
|
||||
}
|
||||
|
||||
/* Find the one surviving path so we know how to set dotdot. */
|
||||
STATIC void
|
||||
xrep_dirtree_find_surviving_path(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirtree_outcomes *oc)
|
||||
{
|
||||
struct xchk_dirpath *path;
|
||||
bool foundit = false;
|
||||
|
||||
xchk_dirtree_for_each_path(dl, path) {
|
||||
switch (path->outcome) {
|
||||
case XCHK_DIRPATH_CORRUPT:
|
||||
case XCHK_DIRPATH_LOOP:
|
||||
case XCHK_DIRPATH_OK:
|
||||
if (!foundit) {
|
||||
xrep_dirpath_retain_parent(dl, path);
|
||||
foundit = true;
|
||||
continue;
|
||||
}
|
||||
ASSERT(foundit == false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(oc->suspect + oc->good == 1);
|
||||
}
|
||||
|
||||
/* Delete all paths except for the one good one. */
|
||||
STATIC void
|
||||
xrep_dirtree_keep_one_good_path(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirtree_outcomes *oc)
|
||||
{
|
||||
struct xchk_dirpath *path;
|
||||
bool foundit = false;
|
||||
|
||||
xchk_dirtree_for_each_path(dl, path) {
|
||||
switch (path->outcome) {
|
||||
case XCHK_DIRPATH_CORRUPT:
|
||||
case XCHK_DIRPATH_LOOP:
|
||||
oc->suspect--;
|
||||
oc->bad++;
|
||||
xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
|
||||
break;
|
||||
case XCHK_DIRPATH_OK:
|
||||
if (!foundit) {
|
||||
xrep_dirpath_retain_parent(dl, path);
|
||||
foundit = true;
|
||||
continue;
|
||||
}
|
||||
oc->good--;
|
||||
oc->bad++;
|
||||
xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(oc->suspect == 0);
|
||||
ASSERT(oc->good < 2);
|
||||
}
|
||||
|
||||
/* Delete all paths except for one suspect one. */
|
||||
STATIC void
|
||||
xrep_dirtree_keep_one_suspect_path(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirtree_outcomes *oc)
|
||||
{
|
||||
struct xchk_dirpath *path;
|
||||
bool foundit = false;
|
||||
|
||||
xchk_dirtree_for_each_path(dl, path) {
|
||||
switch (path->outcome) {
|
||||
case XCHK_DIRPATH_CORRUPT:
|
||||
case XCHK_DIRPATH_LOOP:
|
||||
if (!foundit) {
|
||||
xrep_dirpath_retain_parent(dl, path);
|
||||
foundit = true;
|
||||
continue;
|
||||
}
|
||||
oc->suspect--;
|
||||
oc->bad++;
|
||||
xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
|
||||
break;
|
||||
case XCHK_DIRPATH_OK:
|
||||
ASSERT(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(oc->suspect == 1);
|
||||
ASSERT(oc->good == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out what to do with the paths we tried to find. Returns -EDEADLOCK
|
||||
* if the scan results have become stale.
|
||||
*/
|
||||
STATIC void
|
||||
xrep_dirtree_decide_fate(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirtree_outcomes *oc)
|
||||
{
|
||||
xchk_dirtree_evaluate(dl, oc);
|
||||
|
||||
/* Parentless directories should not have any paths at all. */
|
||||
if (xchk_dirtree_parentless(dl)) {
|
||||
xrep_dirtree_delete_all_paths(dl, oc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* One path is exactly the number of paths we want. */
|
||||
if (oc->good + oc->suspect == 1) {
|
||||
xrep_dirtree_find_surviving_path(dl, oc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Zero paths means we should reattach the subdir to the orphanage. */
|
||||
if (oc->good + oc->suspect == 0) {
|
||||
if (dl->sc->orphanage)
|
||||
oc->needs_adoption = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, this subdirectory has too many parents. If there's at
|
||||
* least one good path, keep it and delete the others.
|
||||
*/
|
||||
if (oc->good > 0) {
|
||||
xrep_dirtree_keep_one_good_path(dl, oc);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are no good paths and there are too many suspect paths.
|
||||
* Keep the first suspect path and delete the rest.
|
||||
*/
|
||||
xrep_dirtree_keep_one_suspect_path(dl, oc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the first step of this path into @step and @dl->xname/pptr
|
||||
* for later repair work.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_dirtree_prep_path(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirpath *path,
|
||||
struct xchk_dirpath_step *step)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = xfarray_load(dl->path_steps, path->first_step, step);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfblob_loadname(dl->path_names, step->name_cookie, &dl->xname,
|
||||
step->name_len);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
dl->pptr_rec = step->pptr_rec; /* struct copy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delete the VFS dentry for a removed child. */
|
||||
STATIC int
|
||||
xrep_dirtree_purge_dentry(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xfs_inode *dp,
|
||||
const struct xfs_name *name)
|
||||
{
|
||||
struct qstr qname = QSTR_INIT(name->name, name->len);
|
||||
struct dentry *parent_dentry, *child_dentry;
|
||||
int error = 0;
|
||||
|
||||
/*
|
||||
* Find the dentry for the parent directory. If there isn't one, we're
|
||||
* done. Caller already holds i_rwsem for parent and child.
|
||||
*/
|
||||
parent_dentry = d_find_alias(VFS_I(dp));
|
||||
if (!parent_dentry)
|
||||
return 0;
|
||||
|
||||
/* The VFS thinks the parent is a directory, right? */
|
||||
if (!d_is_dir(parent_dentry)) {
|
||||
ASSERT(d_is_dir(parent_dentry));
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_dput_parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find the dirent pointing to the child. If there isn't one,
|
||||
* we're done.
|
||||
*/
|
||||
qname.hash = full_name_hash(parent_dentry, name->name, name->len);
|
||||
child_dentry = d_lookup(parent_dentry, &qname);
|
||||
if (!child_dentry) {
|
||||
error = 0;
|
||||
goto out_dput_parent;
|
||||
}
|
||||
|
||||
trace_xrep_dirtree_delete_child(dp->i_mount, child_dentry);
|
||||
|
||||
/* Child is not a directory? We're screwed. */
|
||||
if (!d_is_dir(child_dentry)) {
|
||||
ASSERT(d_is_dir(child_dentry));
|
||||
error = -EFSCORRUPTED;
|
||||
goto out_dput_child;
|
||||
}
|
||||
|
||||
/* Replace the child dentry with a negative one. */
|
||||
d_delete(child_dentry);
|
||||
|
||||
out_dput_child:
|
||||
dput(child_dentry);
|
||||
out_dput_parent:
|
||||
dput(parent_dentry);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare to delete a link by taking the IOLOCK of the parent and the child
|
||||
* (scrub target). Caller must hold IOLOCK_EXCL on @sc->ip. Returns 0 if we
|
||||
* took both locks, or a negative errno if we couldn't lock the parent in time.
|
||||
*/
|
||||
static inline int
|
||||
xrep_dirtree_unlink_iolock(
|
||||
struct xfs_scrub *sc,
|
||||
struct xfs_inode *dp)
|
||||
{
|
||||
int error;
|
||||
|
||||
ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
|
||||
|
||||
if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL))
|
||||
return 0;
|
||||
|
||||
xchk_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
do {
|
||||
xfs_ilock(dp, XFS_IOLOCK_EXCL);
|
||||
if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
|
||||
break;
|
||||
xfs_iunlock(dp, XFS_IOLOCK_EXCL);
|
||||
|
||||
if (xchk_should_terminate(sc, &error)) {
|
||||
xchk_ilock(sc, XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
}
|
||||
|
||||
delay(1);
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a link from the directory tree and update the dcache. Returns
|
||||
* -ESTALE if the scan data are now out of date.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_dirtree_unlink(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xfs_inode *dp,
|
||||
struct xchk_dirpath *path,
|
||||
struct xchk_dirpath_step *step)
|
||||
{
|
||||
struct xfs_scrub *sc = dl->sc;
|
||||
struct xfs_mount *mp = sc->mp;
|
||||
xfs_ino_t dotdot_ino;
|
||||
xfs_ino_t parent_ino = dl->parent_ino;
|
||||
unsigned int resblks;
|
||||
int dontcare;
|
||||
int error;
|
||||
|
||||
/* Take IOLOCK_EXCL of the parent and child. */
|
||||
error = xrep_dirtree_unlink_iolock(sc, dp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Create the transaction that we need to sever the path. Ignore
|
||||
* EDQUOT and ENOSPC being returned via nospace_error because the
|
||||
* directory code can handle a reservationless update.
|
||||
*/
|
||||
resblks = xfs_remove_space_res(mp, step->name_len);
|
||||
error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip,
|
||||
&resblks, &sc->tp, &dontcare);
|
||||
if (error)
|
||||
goto out_iolock;
|
||||
|
||||
/*
|
||||
* Cancel if someone invalidate the paths while we were trying to get
|
||||
* the ILOCK.
|
||||
*/
|
||||
mutex_lock(&dl->lock);
|
||||
if (dl->stale) {
|
||||
mutex_unlock(&dl->lock);
|
||||
error = -ESTALE;
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
xrep_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETING);
|
||||
mutex_unlock(&dl->lock);
|
||||
|
||||
trace_xrep_dirtree_delete_path(dl->sc, sc->ip, path->path_nr,
|
||||
&dl->xname, &dl->pptr_rec);
|
||||
|
||||
/*
|
||||
* Decide if we need to reset the dotdot entry. Rules:
|
||||
*
|
||||
* - If there's a surviving parent, we want dotdot to point there.
|
||||
* - If we don't have any surviving parents, then point dotdot at the
|
||||
* root dir.
|
||||
* - If dotdot is already set to the value we want, pass in NULLFSINO
|
||||
* for no change necessary.
|
||||
*
|
||||
* Do this /before/ we dirty anything, in case the dotdot lookup
|
||||
* fails.
|
||||
*/
|
||||
error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &dotdot_ino);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
if (parent_ino == NULLFSINO)
|
||||
parent_ino = dl->root_ino;
|
||||
if (dotdot_ino == parent_ino)
|
||||
parent_ino = NULLFSINO;
|
||||
|
||||
/* Drop the link from sc->ip's dotdot entry. */
|
||||
error = xfs_droplink(sc->tp, dp);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
|
||||
/* Reset the dotdot entry to a surviving parent. */
|
||||
if (parent_ino != NULLFSINO) {
|
||||
error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,
|
||||
parent_ino, 0);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
|
||||
/* Drop the link from dp to sc->ip. */
|
||||
error = xfs_droplink(sc->tp, sc->ip);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
|
||||
error = xfs_dir_removename(sc->tp, dp, &dl->xname, sc->ip->i_ino,
|
||||
resblks);
|
||||
if (error) {
|
||||
ASSERT(error != -ENOENT);
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
|
||||
if (xfs_has_parent(sc->mp)) {
|
||||
error = xfs_parent_removename(sc->tp, &dl->ppargs, dp,
|
||||
&dl->xname, sc->ip);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify dirent hooks that we removed the bad link, invalidate the
|
||||
* dcache, and commit the repair.
|
||||
*/
|
||||
xfs_dir_update_hook(dp, sc->ip, -1, &dl->xname);
|
||||
error = xrep_dirtree_purge_dentry(dl, dp, &dl->xname);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
|
||||
error = xrep_trans_commit(sc);
|
||||
goto out_ilock;
|
||||
|
||||
out_trans_cancel:
|
||||
xchk_trans_cancel(sc);
|
||||
out_ilock:
|
||||
xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);
|
||||
xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
||||
out_iolock:
|
||||
xfs_iunlock(dp, XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a directory entry that points to this directory. Returns -ESTALE
|
||||
* if the scan data are now out of date.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_dirtree_delete_path(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirpath *path)
|
||||
{
|
||||
struct xchk_dirpath_step step;
|
||||
struct xfs_scrub *sc = dl->sc;
|
||||
struct xfs_inode *dp;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Load the parent pointer and directory inode for this path, then
|
||||
* drop the scan lock, the ILOCK, and the transaction so that
|
||||
* _delete_path can reserve the proper transaction. This sets up
|
||||
* @dl->xname for the deletion.
|
||||
*/
|
||||
error = xrep_dirtree_prep_path(dl, path, &step);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xchk_iget(sc, be64_to_cpu(step.pptr_rec.p_ino), &dp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_unlock(&dl->lock);
|
||||
xchk_trans_cancel(sc);
|
||||
xchk_iunlock(sc, XFS_ILOCK_EXCL);
|
||||
|
||||
/* Delete the directory link and release the parent. */
|
||||
error = xrep_dirtree_unlink(dl, dp, path, &step);
|
||||
xchk_irele(sc, dp);
|
||||
|
||||
/*
|
||||
* Retake all the resources we had at the beginning even if the repair
|
||||
* failed or the scan data are now stale. This keeps things simple for
|
||||
* the caller.
|
||||
*/
|
||||
xchk_trans_alloc_empty(sc);
|
||||
xchk_ilock(sc, XFS_ILOCK_EXCL);
|
||||
mutex_lock(&dl->lock);
|
||||
|
||||
if (!error && dl->stale)
|
||||
error = -ESTALE;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Add a new path to represent our in-progress adoption. */
|
||||
STATIC int
|
||||
xrep_dirtree_create_adoption_path(
|
||||
struct xchk_dirtree *dl)
|
||||
{
|
||||
struct xfs_scrub *sc = dl->sc;
|
||||
struct xchk_dirpath *path;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* We should have capped the number of paths at XFS_MAXLINK-1 in the
|
||||
* scanner.
|
||||
*/
|
||||
if (dl->nr_paths > XFS_MAXLINK) {
|
||||
ASSERT(dl->nr_paths <= XFS_MAXLINK);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new xchk_path structure to remember this parent pointer
|
||||
* and record the first name step.
|
||||
*/
|
||||
path = kmalloc(sizeof(struct xchk_dirpath), XCHK_GFP_FLAGS);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&path->list);
|
||||
xino_bitmap_init(&path->seen_inodes);
|
||||
path->nr_steps = 0;
|
||||
path->outcome = XREP_DIRPATH_ADOPTING;
|
||||
|
||||
/*
|
||||
* Record the new link that we just created in the orphanage. Because
|
||||
* adoption is the last repair that we perform, we don't bother filling
|
||||
* in the path all the way back to the root.
|
||||
*/
|
||||
xfs_inode_to_parent_rec(&dl->pptr_rec, sc->orphanage);
|
||||
|
||||
error = xino_bitmap_set(&path->seen_inodes, sc->orphanage->i_ino);
|
||||
if (error)
|
||||
goto out_path;
|
||||
|
||||
trace_xrep_dirtree_create_adoption(sc, sc->ip, dl->nr_paths,
|
||||
&dl->xname, &dl->pptr_rec);
|
||||
|
||||
error = xchk_dirpath_append(dl, sc->ip, path, &dl->xname,
|
||||
&dl->pptr_rec);
|
||||
if (error)
|
||||
goto out_path;
|
||||
|
||||
path->first_step = xfarray_length(dl->path_steps) - 1;
|
||||
path->second_step = XFARRAY_NULLIDX;
|
||||
path->path_nr = dl->nr_paths;
|
||||
|
||||
list_add_tail(&path->list, &dl->path_list);
|
||||
dl->nr_paths++;
|
||||
return 0;
|
||||
|
||||
out_path:
|
||||
kfree(path);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare to move a file to the orphanage by taking the IOLOCK of the
|
||||
* orphanage and the child (scrub target). Caller must hold IOLOCK_EXCL on
|
||||
* @sc->ip. Returns 0 if we took both locks, or a negative errno if we
|
||||
* couldn't lock the orphanage in time.
|
||||
*/
|
||||
static inline int
|
||||
xrep_dirtree_adopt_iolock(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
int error;
|
||||
|
||||
ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
|
||||
|
||||
if (xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL))
|
||||
return 0;
|
||||
|
||||
xchk_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
do {
|
||||
xrep_orphanage_ilock(sc, XFS_IOLOCK_EXCL);
|
||||
if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
|
||||
break;
|
||||
xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
|
||||
if (xchk_should_terminate(sc, &error)) {
|
||||
xchk_ilock(sc, XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
}
|
||||
|
||||
delay(1);
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reattach this orphaned directory to the orphanage. Do not call this with
|
||||
* any resources held. Returns -ESTALE if the scan data have become out of
|
||||
* date.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_dirtree_adopt(
|
||||
struct xchk_dirtree *dl)
|
||||
{
|
||||
struct xfs_scrub *sc = dl->sc;
|
||||
int error;
|
||||
|
||||
/* Take the IOLOCK of the orphanage and the scrub target. */
|
||||
error = xrep_dirtree_adopt_iolock(sc);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Set up for an adoption. The directory tree fixer runs after the
|
||||
* link counts have been corrected. Therefore, we must bump the
|
||||
* child's link count since there will be no further opportunity to fix
|
||||
* errors.
|
||||
*/
|
||||
error = xrep_adoption_trans_alloc(sc, &dl->adoption);
|
||||
if (error)
|
||||
goto out_iolock;
|
||||
dl->adoption.bump_child_nlink = true;
|
||||
|
||||
/* Figure out what name we're going to use here. */
|
||||
error = xrep_adoption_compute_name(&dl->adoption, &dl->xname);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
|
||||
/*
|
||||
* Now that we have a proposed name for the orphanage entry, create
|
||||
* a faux path so that the live update hook will see it.
|
||||
*/
|
||||
mutex_lock(&dl->lock);
|
||||
if (dl->stale) {
|
||||
mutex_unlock(&dl->lock);
|
||||
error = -ESTALE;
|
||||
goto out_trans;
|
||||
}
|
||||
error = xrep_dirtree_create_adoption_path(dl);
|
||||
mutex_unlock(&dl->lock);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
|
||||
/* Reparent the directory. */
|
||||
error = xrep_adoption_move(&dl->adoption);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
|
||||
/*
|
||||
* Commit the name and release all inode locks except for the scrub
|
||||
* target's IOLOCK.
|
||||
*/
|
||||
error = xrep_trans_commit(sc);
|
||||
goto out_ilock;
|
||||
|
||||
out_trans:
|
||||
xchk_trans_cancel(sc);
|
||||
out_ilock:
|
||||
xchk_iunlock(sc, XFS_ILOCK_EXCL);
|
||||
xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
|
||||
out_iolock:
|
||||
xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* This newly orphaned directory needs to be adopted by the orphanage.
|
||||
* Make this happen.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_dirtree_move_to_orphanage(
|
||||
struct xchk_dirtree *dl)
|
||||
{
|
||||
struct xfs_scrub *sc = dl->sc;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Start by dropping all the resources that we hold so that we can grab
|
||||
* all the resources that we need for the adoption.
|
||||
*/
|
||||
mutex_unlock(&dl->lock);
|
||||
xchk_trans_cancel(sc);
|
||||
xchk_iunlock(sc, XFS_ILOCK_EXCL);
|
||||
|
||||
/* Perform the adoption. */
|
||||
error = xrep_dirtree_adopt(dl);
|
||||
|
||||
/*
|
||||
* Retake all the resources we had at the beginning even if the repair
|
||||
* failed or the scan data are now stale. This keeps things simple for
|
||||
* the caller.
|
||||
*/
|
||||
xchk_trans_alloc_empty(sc);
|
||||
xchk_ilock(sc, XFS_ILOCK_EXCL);
|
||||
mutex_lock(&dl->lock);
|
||||
|
||||
if (!error && dl->stale)
|
||||
error = -ESTALE;
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to fix all the problems. Returns -ESTALE if the scan data have become
|
||||
* out of date.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_dirtree_fix_problems(
|
||||
struct xchk_dirtree *dl,
|
||||
struct xchk_dirtree_outcomes *oc)
|
||||
{
|
||||
struct xchk_dirpath *path;
|
||||
int error;
|
||||
|
||||
/* Delete all the paths we don't want. */
|
||||
xchk_dirtree_for_each_path(dl, path) {
|
||||
if (path->outcome != XCHK_DIRPATH_DELETE)
|
||||
continue;
|
||||
|
||||
error = xrep_dirtree_delete_path(dl, path);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Reparent this directory to the orphanage. */
|
||||
if (oc->needs_adoption) {
|
||||
if (xrep_orphanage_can_adopt(dl->sc))
|
||||
return xrep_dirtree_move_to_orphanage(dl);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fix directory loops involving this directory. */
|
||||
int
|
||||
xrep_dirtree(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
struct xchk_dirtree *dl = sc->buf;
|
||||
struct xchk_dirtree_outcomes oc;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Prepare to fix the directory tree by retaking the scan lock. The
|
||||
* order of resource acquisition is still IOLOCK -> transaction ->
|
||||
* ILOCK -> scan lock.
|
||||
*/
|
||||
mutex_lock(&dl->lock);
|
||||
do {
|
||||
/*
|
||||
* Decide what we're going to do, then do it. An -ESTALE
|
||||
* return here means the scan results are invalid and we have
|
||||
* to walk again.
|
||||
*/
|
||||
if (!dl->stale) {
|
||||
xrep_dirtree_decide_fate(dl, &oc);
|
||||
|
||||
trace_xrep_dirtree_decided_fate(dl, &oc);
|
||||
|
||||
error = xrep_dirtree_fix_problems(dl, &oc);
|
||||
if (!error || error != -ESTALE)
|
||||
break;
|
||||
}
|
||||
error = xchk_dirtree_find_paths_to_root(dl);
|
||||
if (error == -ELNRNG || error == -ENOSR)
|
||||
error = -EFSCORRUPTED;
|
||||
} while (!error);
|
||||
mutex_unlock(&dl->lock);
|
||||
|
||||
return error;
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user