1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2020-2024 Oracle. All Rights Reserved. 4 * Author: Darrick J. Wong <djwong@kernel.org> 5 */ 6 #include "xfs.h" 7 #include "xfs_fs.h" 8 #include "xfs_shared.h" 9 #include "xfs_format.h" 10 #include "xfs_trans_resv.h" 11 #include "xfs_mount.h" 12 #include "xfs_defer.h" 13 #include "xfs_bit.h" 14 #include "xfs_log_format.h" 15 #include "xfs_trans.h" 16 #include "xfs_sb.h" 17 #include "xfs_inode.h" 18 #include "xfs_icache.h" 19 #include "xfs_da_format.h" 20 #include "xfs_da_btree.h" 21 #include "xfs_dir2.h" 22 #include "xfs_bmap_btree.h" 23 #include "xfs_dir2_priv.h" 24 #include "xfs_trans_space.h" 25 #include "xfs_health.h" 26 #include "xfs_exchmaps.h" 27 #include "xfs_parent.h" 28 #include "scrub/xfs_scrub.h" 29 #include "scrub/scrub.h" 30 #include "scrub/common.h" 31 #include "scrub/trace.h" 32 #include "scrub/repair.h" 33 #include "scrub/iscan.h" 34 #include "scrub/findparent.h" 35 #include "scrub/readdir.h" 36 #include "scrub/tempfile.h" 37 #include "scrub/listxattr.h" 38 39 /* 40 * Finding the Parent of a Directory 41 * ================================= 42 * 43 * Directories have parent pointers, in the sense that each directory contains 44 * a dotdot entry that points to the single allowed parent. The brute force 45 * way to find the parent of a given directory is to scan every directory in 46 * the filesystem looking for a child dirent that references this directory. 47 * 48 * This module wraps the process of scanning the directory tree. It requires 49 * that @sc->ip is the directory whose parent we want to find, and that the 50 * caller hold only the IOLOCK on that directory. The scan itself needs to 51 * take the ILOCK of each directory visited. 52 * 53 * Because we cannot hold @sc->ip's ILOCK during a scan of the whole fs, it is 54 * necessary to use dirent hook to update the parent scan results. Callers 55 * must not read the scan results without re-taking @sc->ip's ILOCK. 56 * 57 * There are a few shortcuts that we can take to avoid scanning the entire 58 * filesystem, such as noticing directory tree roots and querying the dentry 59 * cache for parent information. 60 */ 61 62 struct xrep_findparent_info { 63 /* The directory currently being scanned. */ 64 struct xfs_inode *dp; 65 66 /* 67 * Scrub context. We're looking for a @dp containing a directory 68 * entry pointing to sc->ip->i_ino. 69 */ 70 struct xfs_scrub *sc; 71 72 /* Optional scan information for a xrep_findparent_scan call. */ 73 struct xrep_parent_scan_info *parent_scan; 74 75 /* 76 * Parent that we've found for sc->ip. If we're scanning the entire 77 * directory tree, we need this to ensure that we only find /one/ 78 * parent directory. 79 */ 80 xfs_ino_t found_parent; 81 82 /* 83 * This is set to true if @found_parent was not observed directly from 84 * the directory scan but by noticing a change in dotdot entries after 85 * cycling the sc->ip IOLOCK. 86 */ 87 bool parent_tentative; 88 }; 89 90 /* 91 * If this directory entry points to the scrub target inode, then the directory 92 * we're scanning is the parent of the scrub target inode. 93 */ 94 STATIC int 95 xrep_findparent_dirent( 96 struct xfs_scrub *sc, 97 struct xfs_inode *dp, 98 xfs_dir2_dataptr_t dapos, 99 const struct xfs_name *name, 100 xfs_ino_t ino, 101 void *priv) 102 { 103 struct xrep_findparent_info *fpi = priv; 104 int error = 0; 105 106 if (xchk_should_terminate(fpi->sc, &error)) 107 return error; 108 109 if (ino != fpi->sc->ip->i_ino) 110 return 0; 111 112 /* Ignore garbage directory entry names. */ 113 if (name->len == 0 || !xfs_dir2_namecheck(name->name, name->len)) 114 return -EFSCORRUPTED; 115 116 /* 117 * Ignore dotdot and dot entries -- we're looking for parent -> child 118 * links only. 119 */ 120 if (name->name[0] == '.' && (name->len == 1 || 121 (name->len == 2 && name->name[1] == '.'))) 122 return 0; 123 124 /* Uhoh, more than one parent for a dir? */ 125 if (fpi->found_parent != NULLFSINO && 126 !(fpi->parent_tentative && fpi->found_parent == fpi->dp->i_ino)) { 127 trace_xrep_findparent_dirent(fpi->sc->ip, 0); 128 return -EFSCORRUPTED; 129 } 130 131 /* We found a potential parent; remember this. */ 132 trace_xrep_findparent_dirent(fpi->sc->ip, fpi->dp->i_ino); 133 fpi->found_parent = fpi->dp->i_ino; 134 fpi->parent_tentative = false; 135 136 if (fpi->parent_scan) 137 xrep_findparent_scan_found(fpi->parent_scan, fpi->dp->i_ino); 138 139 return 0; 140 } 141 142 /* 143 * If this is a directory, walk the dirents looking for any that point to the 144 * scrub target inode. 145 */ 146 STATIC int 147 xrep_findparent_walk_directory( 148 struct xrep_findparent_info *fpi) 149 { 150 struct xfs_scrub *sc = fpi->sc; 151 struct xfs_inode *dp = fpi->dp; 152 unsigned int lock_mode; 153 int error = 0; 154 155 /* 156 * The inode being scanned cannot be its own parent, nor can any 157 * temporary directory we created to stage this repair. 158 */ 159 if (dp == sc->ip || dp == sc->tempip) 160 return 0; 161 162 /* 163 * Similarly, temporary files created to stage a repair cannot be the 164 * parent of this inode. 165 */ 166 if (xrep_is_tempfile(dp)) 167 return 0; 168 169 /* 170 * Scan the directory to see if there it contains an entry pointing to 171 * the directory that we are repairing. 172 */ 173 lock_mode = xfs_ilock_data_map_shared(dp); 174 175 /* 176 * If this directory is known to be sick, we cannot scan it reliably 177 * and must abort. 178 */ 179 if (xfs_inode_has_sickness(dp, XFS_SICK_INO_CORE | 180 XFS_SICK_INO_BMBTD | 181 XFS_SICK_INO_DIR)) { 182 error = -EFSCORRUPTED; 183 goto out_unlock; 184 } 185 186 /* 187 * We cannot complete our parent pointer scan if a directory looks as 188 * though it has been zapped by the inode record repair code. 189 */ 190 if (xchk_dir_looks_zapped(dp)) { 191 error = -EBUSY; 192 goto out_unlock; 193 } 194 195 error = xchk_dir_walk(sc, dp, xrep_findparent_dirent, fpi); 196 if (error) 197 goto out_unlock; 198 199 out_unlock: 200 xfs_iunlock(dp, lock_mode); 201 return error; 202 } 203 204 /* 205 * Update this directory's dotdot pointer based on ongoing dirent updates. 206 */ 207 STATIC int 208 xrep_findparent_live_update( 209 struct notifier_block *nb, 210 unsigned long action, 211 void *data) 212 { 213 struct xfs_dir_update_params *p = data; 214 struct xrep_parent_scan_info *pscan; 215 struct xfs_scrub *sc; 216 217 pscan = container_of(nb, struct xrep_parent_scan_info, 218 dhook.dirent_hook.nb); 219 sc = pscan->sc; 220 221 /* 222 * If @p->ip is the subdirectory that we're interested in and we've 223 * already scanned @p->dp, update the dotdot target inumber to the 224 * parent inode. 225 */ 226 if (p->ip->i_ino == sc->ip->i_ino && 227 xchk_iscan_want_live_update(&pscan->iscan, p->dp->i_ino)) { 228 if (p->delta > 0) { 229 xrep_findparent_scan_found(pscan, p->dp->i_ino); 230 } else { 231 xrep_findparent_scan_found(pscan, NULLFSINO); 232 } 233 } 234 235 return NOTIFY_DONE; 236 } 237 238 /* 239 * Set up a scan to find the parent of a directory. The provided dirent hook 240 * will be called when there is a dotdot update for the inode being repaired. 241 */ 242 int 243 __xrep_findparent_scan_start( 244 struct xfs_scrub *sc, 245 struct xrep_parent_scan_info *pscan, 246 notifier_fn_t custom_fn) 247 { 248 int error; 249 250 if (!(sc->flags & XCHK_FSGATES_DIRENTS)) { 251 ASSERT(sc->flags & XCHK_FSGATES_DIRENTS); 252 return -EINVAL; 253 } 254 255 pscan->sc = sc; 256 pscan->parent_ino = NULLFSINO; 257 258 mutex_init(&pscan->lock); 259 260 xchk_iscan_start(sc, 30000, 100, &pscan->iscan); 261 262 /* 263 * Hook into the dirent update code. The hook only operates on inodes 264 * that were already scanned, and the scanner thread takes each inode's 265 * ILOCK, which means that any in-progress inode updates will finish 266 * before we can scan the inode. 267 */ 268 if (custom_fn) 269 xfs_dir_hook_setup(&pscan->dhook, custom_fn); 270 else 271 xfs_dir_hook_setup(&pscan->dhook, xrep_findparent_live_update); 272 error = xfs_dir_hook_add(sc->mp, &pscan->dhook); 273 if (error) 274 goto out_iscan; 275 276 return 0; 277 out_iscan: 278 xchk_iscan_teardown(&pscan->iscan); 279 mutex_destroy(&pscan->lock); 280 return error; 281 } 282 283 /* 284 * Scan the entire filesystem looking for a parent inode for the inode being 285 * scrubbed. @sc->ip must not be the root of a directory tree. Callers must 286 * not hold a dirty transaction or any lock that would interfere with taking 287 * an ILOCK. 288 * 289 * Returns 0 with @pscan->parent_ino set to the parent that we found. 290 * Returns 0 with @pscan->parent_ino set to NULLFSINO if we found no parents. 291 * Returns the usual negative errno if something else happened. 292 */ 293 int 294 xrep_findparent_scan( 295 struct xrep_parent_scan_info *pscan) 296 { 297 struct xrep_findparent_info fpi = { 298 .sc = pscan->sc, 299 .found_parent = NULLFSINO, 300 .parent_scan = pscan, 301 }; 302 struct xfs_scrub *sc = pscan->sc; 303 int ret; 304 305 ASSERT(S_ISDIR(VFS_IC(sc->ip)->i_mode)); 306 307 while ((ret = xchk_iscan_iter(&pscan->iscan, &fpi.dp)) == 1) { 308 if (S_ISDIR(VFS_I(fpi.dp)->i_mode)) 309 ret = xrep_findparent_walk_directory(&fpi); 310 else 311 ret = 0; 312 xchk_iscan_mark_visited(&pscan->iscan, fpi.dp); 313 xchk_irele(sc, fpi.dp); 314 if (ret) 315 break; 316 317 if (xchk_should_terminate(sc, &ret)) 318 break; 319 } 320 xchk_iscan_iter_finish(&pscan->iscan); 321 322 return ret; 323 } 324 325 /* Tear down a parent scan. */ 326 void 327 xrep_findparent_scan_teardown( 328 struct xrep_parent_scan_info *pscan) 329 { 330 xfs_dir_hook_del(pscan->sc->mp, &pscan->dhook); 331 xchk_iscan_teardown(&pscan->iscan); 332 mutex_destroy(&pscan->lock); 333 } 334 335 /* Finish a parent scan early. */ 336 void 337 xrep_findparent_scan_finish_early( 338 struct xrep_parent_scan_info *pscan, 339 xfs_ino_t ino) 340 { 341 xrep_findparent_scan_found(pscan, ino); 342 xchk_iscan_finish_early(&pscan->iscan); 343 } 344 345 /* 346 * Confirm that the directory @parent_ino actually contains a directory entry 347 * pointing to the child @sc->ip->ino. This function returns one of several 348 * ways: 349 * 350 * Returns 0 with @parent_ino unchanged if the parent was confirmed. 351 * Returns 0 with @parent_ino set to NULLFSINO if the parent was not valid. 352 * Returns the usual negative errno if something else happened. 353 */ 354 int 355 xrep_findparent_confirm( 356 struct xfs_scrub *sc, 357 xfs_ino_t *parent_ino) 358 { 359 struct xrep_findparent_info fpi = { 360 .sc = sc, 361 .found_parent = NULLFSINO, 362 }; 363 int error; 364 365 /* 366 * The root directory always points to itself. Unlinked dirs can point 367 * anywhere, so we point them at the root dir too. 368 */ 369 if (sc->ip == sc->mp->m_rootip || VFS_I(sc->ip)->i_nlink == 0) { 370 *parent_ino = sc->mp->m_sb.sb_rootino; 371 return 0; 372 } 373 374 /* Reject garbage parent inode numbers and self-referential parents. */ 375 if (*parent_ino == NULLFSINO) 376 return 0; 377 if (!xfs_verify_dir_ino(sc->mp, *parent_ino) || 378 *parent_ino == sc->ip->i_ino) { 379 *parent_ino = NULLFSINO; 380 return 0; 381 } 382 383 error = xchk_iget(sc, *parent_ino, &fpi.dp); 384 if (error) 385 return error; 386 387 if (!S_ISDIR(VFS_I(fpi.dp)->i_mode)) { 388 *parent_ino = NULLFSINO; 389 goto out_rele; 390 } 391 392 error = xrep_findparent_walk_directory(&fpi); 393 if (error) 394 goto out_rele; 395 396 *parent_ino = fpi.found_parent; 397 out_rele: 398 xchk_irele(sc, fpi.dp); 399 return error; 400 } 401 402 /* 403 * If we're the root of a directory tree, we are our own parent. If we're an 404 * unlinked directory, the parent /won't/ have a link to us. Set the parent 405 * directory to the root for both cases. Returns NULLFSINO if we don't know 406 * what to do. 407 */ 408 xfs_ino_t 409 xrep_findparent_self_reference( 410 struct xfs_scrub *sc) 411 { 412 if (sc->ip->i_ino == sc->mp->m_sb.sb_rootino) 413 return sc->mp->m_sb.sb_rootino; 414 415 if (VFS_I(sc->ip)->i_nlink == 0) 416 return sc->mp->m_sb.sb_rootino; 417 418 return NULLFSINO; 419 } 420 421 /* Check the dentry cache to see if knows of a parent for the scrub target. */ 422 xfs_ino_t 423 xrep_findparent_from_dcache( 424 struct xfs_scrub *sc) 425 { 426 struct inode *pip = NULL; 427 struct dentry *dentry, *parent; 428 xfs_ino_t ret = NULLFSINO; 429 430 dentry = d_find_alias(VFS_I(sc->ip)); 431 if (!dentry) 432 goto out; 433 434 parent = dget_parent(dentry); 435 if (!parent) 436 goto out_dput; 437 438 ASSERT(parent->d_sb == sc->ip->i_mount->m_super); 439 440 pip = igrab(d_inode(parent)); 441 dput(parent); 442 443 if (S_ISDIR(pip->i_mode)) { 444 trace_xrep_findparent_from_dcache(sc->ip, XFS_I(pip)->i_ino); 445 ret = XFS_I(pip)->i_ino; 446 } 447 448 xchk_irele(sc, XFS_I(pip)); 449 450 out_dput: 451 dput(dentry); 452 out: 453 return ret; 454 } 455
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.