Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | /* 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__ */ |