1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2023-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_log_format.h" 13 #include "xfs_trans.h" 14 #include "xfs_inode.h" 15 #include "xfs_metafile.h" 16 #include "xfs_quota.h" 17 #include "xfs_qm.h" 18 #include "xfs_dir2.h" 19 #include "xfs_parent.h" 20 #include "xfs_bmap_btree.h" 21 #include "xfs_trans_space.h" 22 #include "xfs_attr.h" 23 #include "scrub/scrub.h" 24 #include "scrub/common.h" 25 #include "scrub/trace.h" 26 #include "scrub/readdir.h" 27 #include "scrub/repair.h" 28 29 /* 30 * Metadata Directory Tree Paths 31 * ============================= 32 * 33 * A filesystem with metadir enabled expects to find metadata structures 34 * attached to files that are accessible by walking a path down the metadata 35 * directory tree. Given the metadir path and the incore inode storing the 36 * metadata, this scrubber ensures that the ondisk metadir path points to the 37 * ondisk inode represented by the incore inode. 38 */ 39 40 struct xchk_metapath { 41 struct xfs_scrub *sc; 42 43 /* Name for lookup */ 44 struct xfs_name xname; 45 46 /* Directory update for repairs */ 47 struct xfs_dir_update du; 48 49 /* Path down to this metadata file from the parent directory */ 50 const char *path; 51 52 /* Directory parent of the metadata file. */ 53 struct xfs_inode *dp; 54 55 /* Locks held on dp */ 56 unsigned int dp_ilock_flags; 57 58 /* Transaction block reservations */ 59 unsigned int link_resblks; 60 unsigned int unlink_resblks; 61 62 /* Parent pointer updates */ 63 struct xfs_parent_args link_ppargs; 64 struct xfs_parent_args unlink_ppargs; 65 66 /* Scratchpads for removing links */ 67 struct xfs_da_args pptr_args; 68 }; 69 70 /* Release resources tracked in the buffer. */ 71 static inline void 72 xchk_metapath_cleanup( 73 void *buf) 74 { 75 struct xchk_metapath *mpath = buf; 76 77 if (mpath->dp_ilock_flags) 78 xfs_iunlock(mpath->dp, mpath->dp_ilock_flags); 79 kfree(mpath->path); 80 } 81 82 int 83 xchk_setup_metapath( 84 struct xfs_scrub *sc) 85 { 86 if (!xfs_has_metadir(sc->mp)) 87 return -ENOENT; 88 if (sc->sm->sm_gen) 89 return -EINVAL; 90 91 switch (sc->sm->sm_ino) { 92 case XFS_SCRUB_METAPATH_PROBE: 93 /* Just probing, nothing else to do. */ 94 if (sc->sm->sm_agno) 95 return -EINVAL; 96 return 0; 97 default: 98 return -ENOENT; 99 } 100 } 101 102 /* 103 * Take the ILOCK on the metadata directory parent and child. We do not know 104 * that the metadata directory is not corrupt, so we lock the parent and try 105 * to lock the child. Returns 0 if successful, or -EINTR to abort the scrub. 106 */ 107 STATIC int 108 xchk_metapath_ilock_both( 109 struct xchk_metapath *mpath) 110 { 111 struct xfs_scrub *sc = mpath->sc; 112 int error = 0; 113 114 while (true) { 115 xfs_ilock(mpath->dp, XFS_ILOCK_EXCL); 116 if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) { 117 mpath->dp_ilock_flags |= XFS_ILOCK_EXCL; 118 return 0; 119 } 120 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 121 122 if (xchk_should_terminate(sc, &error)) 123 return error; 124 125 delay(1); 126 } 127 128 ASSERT(0); 129 return -EINTR; 130 } 131 132 /* Unlock parent and child inodes. */ 133 static inline void 134 xchk_metapath_iunlock( 135 struct xchk_metapath *mpath) 136 { 137 struct xfs_scrub *sc = mpath->sc; 138 139 xchk_iunlock(sc, XFS_ILOCK_EXCL); 140 141 mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL; 142 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 143 } 144 145 int 146 xchk_metapath( 147 struct xfs_scrub *sc) 148 { 149 struct xchk_metapath *mpath = sc->buf; 150 xfs_ino_t ino = NULLFSINO; 151 int error; 152 153 /* Just probing, nothing else to do. */ 154 if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE) 155 return 0; 156 157 /* Parent required to do anything else. */ 158 if (mpath->dp == NULL) { 159 xchk_ino_set_corrupt(sc, sc->ip->i_ino); 160 return 0; 161 } 162 163 error = xchk_trans_alloc_empty(sc); 164 if (error) 165 return error; 166 167 error = xchk_metapath_ilock_both(mpath); 168 if (error) 169 goto out_cancel; 170 171 /* Make sure the parent dir has a dirent pointing to this file. */ 172 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino); 173 trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino); 174 if (error == -ENOENT) { 175 /* No directory entry at all */ 176 xchk_ino_set_corrupt(sc, sc->ip->i_ino); 177 error = 0; 178 goto out_ilock; 179 } 180 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) 181 goto out_ilock; 182 if (ino != sc->ip->i_ino) { 183 /* Pointing to wrong inode */ 184 xchk_ino_set_corrupt(sc, sc->ip->i_ino); 185 } 186 187 out_ilock: 188 xchk_metapath_iunlock(mpath); 189 out_cancel: 190 xchk_trans_cancel(sc); 191 return error; 192 } 193 194 #ifdef CONFIG_XFS_ONLINE_REPAIR 195 /* Create the dirent represented by the final component of the path. */ 196 STATIC int 197 xrep_metapath_link( 198 struct xchk_metapath *mpath) 199 { 200 struct xfs_scrub *sc = mpath->sc; 201 202 mpath->du.dp = mpath->dp; 203 mpath->du.name = &mpath->xname; 204 mpath->du.ip = sc->ip; 205 206 if (xfs_has_parent(sc->mp)) 207 mpath->du.ppargs = &mpath->link_ppargs; 208 else 209 mpath->du.ppargs = NULL; 210 211 trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino); 212 213 return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du); 214 } 215 216 /* Remove the dirent at the final component of the path. */ 217 STATIC int 218 xrep_metapath_unlink( 219 struct xchk_metapath *mpath, 220 xfs_ino_t ino, 221 struct xfs_inode *ip) 222 { 223 struct xfs_parent_rec rec; 224 struct xfs_scrub *sc = mpath->sc; 225 struct xfs_mount *mp = sc->mp; 226 int error; 227 228 trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino); 229 230 if (!ip) { 231 /* The child inode isn't allocated. Junk the dirent. */ 232 xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE); 233 return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname, 234 ino, mpath->unlink_resblks); 235 } 236 237 mpath->du.dp = mpath->dp; 238 mpath->du.name = &mpath->xname; 239 mpath->du.ip = ip; 240 mpath->du.ppargs = NULL; 241 242 /* Figure out if we're removing a parent pointer too. */ 243 if (xfs_has_parent(mp)) { 244 xfs_inode_to_parent_rec(&rec, ip); 245 error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec, 246 &mpath->pptr_args); 247 switch (error) { 248 case -ENOATTR: 249 break; 250 case 0: 251 mpath->du.ppargs = &mpath->unlink_ppargs; 252 break; 253 default: 254 return error; 255 } 256 } 257 258 return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du); 259 } 260 261 /* 262 * Try to create a dirent in @mpath->dp with the name @mpath->xname that points 263 * to @sc->ip. Returns: 264 * 265 * -EEXIST and an @alleged_child if the dirent that points to the wrong inode; 266 * 0 if there is now a dirent pointing to @sc->ip; or 267 * A negative errno on error. 268 */ 269 STATIC int 270 xrep_metapath_try_link( 271 struct xchk_metapath *mpath, 272 xfs_ino_t *alleged_child) 273 { 274 struct xfs_scrub *sc = mpath->sc; 275 xfs_ino_t ino; 276 int error; 277 278 /* Allocate transaction, lock inodes, join to transaction. */ 279 error = xchk_trans_alloc(sc, mpath->link_resblks); 280 if (error) 281 return error; 282 283 error = xchk_metapath_ilock_both(mpath); 284 if (error) { 285 xchk_trans_cancel(sc); 286 return error; 287 } 288 xfs_trans_ijoin(sc->tp, mpath->dp, 0); 289 xfs_trans_ijoin(sc->tp, sc->ip, 0); 290 291 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino); 292 trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino); 293 if (error == -ENOENT) { 294 /* 295 * There is no dirent in the directory. Create an entry 296 * pointing to @sc->ip. 297 */ 298 error = xrep_metapath_link(mpath); 299 if (error) 300 goto out_cancel; 301 302 error = xrep_trans_commit(sc); 303 xchk_metapath_iunlock(mpath); 304 return error; 305 } 306 if (error) 307 goto out_cancel; 308 309 if (ino == sc->ip->i_ino) { 310 /* The dirent already points to @sc->ip; we're done. */ 311 error = 0; 312 goto out_cancel; 313 } 314 315 /* 316 * The dirent points elsewhere; pass that back so that the caller 317 * can try to remove the dirent. 318 */ 319 *alleged_child = ino; 320 error = -EEXIST; 321 322 out_cancel: 323 xchk_trans_cancel(sc); 324 xchk_metapath_iunlock(mpath); 325 return error; 326 } 327 328 /* 329 * Take the ILOCK on the metadata directory parent and a bad child, if one is 330 * supplied. We do not know that the metadata directory is not corrupt, so we 331 * lock the parent and try to lock the child. Returns 0 if successful, or 332 * -EINTR to abort the repair. The lock state of @dp is not recorded in @mpath. 333 */ 334 STATIC int 335 xchk_metapath_ilock_parent_and_child( 336 struct xchk_metapath *mpath, 337 struct xfs_inode *ip) 338 { 339 struct xfs_scrub *sc = mpath->sc; 340 int error = 0; 341 342 while (true) { 343 xfs_ilock(mpath->dp, XFS_ILOCK_EXCL); 344 if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) 345 return 0; 346 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 347 348 if (xchk_should_terminate(sc, &error)) 349 return error; 350 351 delay(1); 352 } 353 354 ASSERT(0); 355 return -EINTR; 356 } 357 358 /* 359 * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points 360 * to @alleged_child. Returns: 361 * 362 * 0 if there is no longer a dirent; 363 * -EEXIST if the dirent points to @sc->ip; 364 * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or 365 * A negative errno for any other error. 366 */ 367 STATIC int 368 xrep_metapath_try_unlink( 369 struct xchk_metapath *mpath, 370 xfs_ino_t *alleged_child) 371 { 372 struct xfs_scrub *sc = mpath->sc; 373 struct xfs_inode *ip = NULL; 374 xfs_ino_t ino; 375 int error; 376 377 ASSERT(*alleged_child != sc->ip->i_ino); 378 379 trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp, 380 *alleged_child); 381 382 /* 383 * Allocate transaction, grab the alleged child inode, lock inodes, 384 * join to transaction. 385 */ 386 error = xchk_trans_alloc(sc, mpath->unlink_resblks); 387 if (error) 388 return error; 389 390 error = xchk_iget(sc, *alleged_child, &ip); 391 if (error == -EINVAL || error == -ENOENT) { 392 /* inode number is bogus, junk the dirent */ 393 error = 0; 394 } 395 if (error) { 396 xchk_trans_cancel(sc); 397 return error; 398 } 399 400 error = xchk_metapath_ilock_parent_and_child(mpath, ip); 401 if (error) { 402 xchk_trans_cancel(sc); 403 return error; 404 } 405 xfs_trans_ijoin(sc->tp, mpath->dp, 0); 406 if (ip) 407 xfs_trans_ijoin(sc->tp, ip, 0); 408 409 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino); 410 trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino); 411 if (error == -ENOENT) { 412 /* 413 * There is no dirent in the directory anymore. We're ready to 414 * try the link operation again. 415 */ 416 error = 0; 417 goto out_cancel; 418 } 419 if (error) 420 goto out_cancel; 421 422 if (ino == sc->ip->i_ino) { 423 /* The dirent already points to @sc->ip; we're done. */ 424 error = -EEXIST; 425 goto out_cancel; 426 } 427 428 /* 429 * The dirent does not point to the alleged child. Update the caller 430 * and signal that we want to be called again. 431 */ 432 if (ino != *alleged_child) { 433 *alleged_child = ino; 434 error = -EAGAIN; 435 goto out_cancel; 436 } 437 438 /* Remove the link to the child. */ 439 error = xrep_metapath_unlink(mpath, ino, ip); 440 if (error) 441 goto out_cancel; 442 443 error = xrep_trans_commit(sc); 444 goto out_unlock; 445 446 out_cancel: 447 xchk_trans_cancel(sc); 448 out_unlock: 449 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 450 if (ip) { 451 xfs_iunlock(ip, XFS_ILOCK_EXCL); 452 xchk_irele(sc, ip); 453 } 454 return error; 455 } 456 457 /* 458 * Make sure the metadata directory path points to the child being examined. 459 * 460 * Repair needs to be able to create a directory structure, create its own 461 * transactions, and take ILOCKs. This function /must/ be called after all 462 * other repairs have completed. 463 */ 464 int 465 xrep_metapath( 466 struct xfs_scrub *sc) 467 { 468 struct xchk_metapath *mpath = sc->buf; 469 struct xfs_mount *mp = sc->mp; 470 int error = 0; 471 472 /* Just probing, nothing to repair. */ 473 if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE) 474 return 0; 475 476 /* Parent required to do anything else. */ 477 if (mpath->dp == NULL) 478 return -EFSCORRUPTED; 479 480 /* 481 * Make sure the child file actually has an attr fork to receive a new 482 * parent pointer if the fs has parent pointers. 483 */ 484 if (xfs_has_parent(mp)) { 485 error = xfs_attr_add_fork(sc->ip, 486 sizeof(struct xfs_attr_sf_hdr), 1); 487 if (error) 488 return error; 489 } 490 491 /* Compute block reservation required to unlink and link a file. */ 492 mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN); 493 mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN); 494 495 do { 496 xfs_ino_t alleged_child; 497 498 /* Re-establish the link, or tell us which inode to remove. */ 499 error = xrep_metapath_try_link(mpath, &alleged_child); 500 if (!error) 501 return 0; 502 if (error != -EEXIST) 503 return error; 504 505 /* 506 * Remove an incorrect link to an alleged child, or tell us 507 * which inode to remove. 508 */ 509 do { 510 error = xrep_metapath_try_unlink(mpath, &alleged_child); 511 } while (error == -EAGAIN); 512 if (error == -EEXIST) { 513 /* Link established; we're done. */ 514 error = 0; 515 break; 516 } 517 } while (!error); 518 519 return error; 520 } 521 #endif /* CONFIG_XFS_ONLINE_REPAIR */ 522