3f31406aef
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>
179 lines
4.9 KiB
C
179 lines
4.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (c) 2023-2024 Oracle. All Rights Reserved.
|
|
* Author: Darrick J. Wong <djwong@kernel.org>
|
|
*/
|
|
#ifndef __XFS_SCRUB_DIRTREE_H__
|
|
#define __XFS_SCRUB_DIRTREE_H__
|
|
|
|
/*
|
|
* Each of these represents one parent pointer path step in a chain going
|
|
* up towards the directory tree root. These are stored inside an xfarray.
|
|
*/
|
|
struct xchk_dirpath_step {
|
|
/* Directory entry name associated with this parent link. */
|
|
xfblob_cookie name_cookie;
|
|
unsigned int name_len;
|
|
|
|
/* Handle of the parent directory. */
|
|
struct xfs_parent_rec pptr_rec;
|
|
};
|
|
|
|
enum xchk_dirpath_outcome {
|
|
XCHK_DIRPATH_SCANNING = 0, /* still being put together */
|
|
XCHK_DIRPATH_DELETE, /* delete this path */
|
|
XCHK_DIRPATH_CORRUPT, /* corruption detected in path */
|
|
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 */
|
|
};
|
|
|
|
/*
|
|
* Each of these represents one parent pointer path out of the directory being
|
|
* scanned. These exist in-core, and hopefully there aren't more than a
|
|
* handful of them.
|
|
*/
|
|
struct xchk_dirpath {
|
|
struct list_head list;
|
|
|
|
/* Index of the first step in this path. */
|
|
xfarray_idx_t first_step;
|
|
|
|
/* Index of the second step in this path. */
|
|
xfarray_idx_t second_step;
|
|
|
|
/* Inodes seen while walking this path. */
|
|
struct xino_bitmap seen_inodes;
|
|
|
|
/* Number of steps in this path. */
|
|
unsigned int nr_steps;
|
|
|
|
/* Which path is this? */
|
|
unsigned int path_nr;
|
|
|
|
/* What did we conclude from following this path? */
|
|
enum xchk_dirpath_outcome outcome;
|
|
};
|
|
|
|
struct xchk_dirtree_outcomes {
|
|
/* Number of XCHK_DIRPATH_DELETE */
|
|
unsigned int bad;
|
|
|
|
/* Number of XCHK_DIRPATH_CORRUPT or XCHK_DIRPATH_LOOP */
|
|
unsigned int suspect;
|
|
|
|
/* Number of XCHK_DIRPATH_OK */
|
|
unsigned int good;
|
|
|
|
/* Directory needs to be added to lost+found */
|
|
bool needs_adoption;
|
|
};
|
|
|
|
struct xchk_dirtree {
|
|
struct xfs_scrub *sc;
|
|
|
|
/* Root inode that we're looking for. */
|
|
xfs_ino_t root_ino;
|
|
|
|
/*
|
|
* This is the inode that we're scanning. The live update hook can
|
|
* continue to be called after xchk_teardown drops sc->ip but before
|
|
* it calls buf_cleanup, so we keep a copy.
|
|
*/
|
|
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;
|
|
|
|
/* Name buffer */
|
|
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;
|
|
|
|
/* buffer for the live update functions to use for dirent names */
|
|
struct xfs_name hook_xname;
|
|
unsigned char hook_namebuf[MAXNAMELEN];
|
|
|
|
/*
|
|
* All path steps observed during this scan. Each of the path
|
|
* steps for a particular pathwalk are recorded in sequential
|
|
* order in the xfarray. A pathwalk ends either with a step
|
|
* pointing to the root directory (success) or pointing to NULLFSINO
|
|
* (loop detected, empty dir detected, etc).
|
|
*/
|
|
struct xfarray *path_steps;
|
|
|
|
/* All names observed during this scan. */
|
|
struct xfblob *path_names;
|
|
|
|
/* All paths being tracked by this scanner. */
|
|
struct list_head path_list;
|
|
|
|
/* Number of paths in path_list. */
|
|
unsigned int nr_paths;
|
|
|
|
/* Number of parents found by a pptr scan. */
|
|
unsigned int parents_found;
|
|
|
|
/* Have the path data been invalidated by a concurrent update? */
|
|
bool stale:1;
|
|
|
|
/* Has the scan been aborted? */
|
|
bool aborted:1;
|
|
};
|
|
|
|
#define xchk_dirtree_for_each_path_safe(dl, path, n) \
|
|
list_for_each_entry_safe((path), (n), &(dl)->path_list, list)
|
|
|
|
#define xchk_dirtree_for_each_path(dl, path) \
|
|
list_for_each_entry((path), &(dl)->path_list, list)
|
|
|
|
static inline bool
|
|
xchk_dirtree_parentless(const struct xchk_dirtree *dl)
|
|
{
|
|
struct xfs_scrub *sc = dl->sc;
|
|
|
|
if (sc->ip == sc->mp->m_rootip)
|
|
return true;
|
|
if (VFS_I(sc->ip)->i_nlink == 0)
|
|
return true;
|
|
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__ */
|