/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2023-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong */ #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__ */