| /* 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__ */ |