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 "xfs_rtgroup.h"
24 #include "scrub/scrub.h"
25 #include "scrub/common.h"
26 #include "scrub/trace.h"
27 #include "scrub/readdir.h"
28 #include "scrub/repair.h"
29
30 /*
31 * Metadata Directory Tree Paths
32 * =============================
33 *
34 * A filesystem with metadir enabled expects to find metadata structures
35 * attached to files that are accessible by walking a path down the metadata
36 * directory tree. Given the metadir path and the incore inode storing the
37 * metadata, this scrubber ensures that the ondisk metadir path points to the
38 * ondisk inode represented by the incore inode.
39 */
40
41 struct xchk_metapath {
42 struct xfs_scrub *sc;
43
44 /* Name for lookup */
45 struct xfs_name xname;
46
47 /* Directory update for repairs */
48 struct xfs_dir_update du;
49
50 /* Path down to this metadata file from the parent directory */
51 const char *path;
52
53 /* Directory parent of the metadata file. */
54 struct xfs_inode *dp;
55
56 /* Locks held on dp */
57 unsigned int dp_ilock_flags;
58
59 /* Transaction block reservations */
60 unsigned int link_resblks;
61 unsigned int unlink_resblks;
62
63 /* Parent pointer updates */
64 struct xfs_parent_args link_ppargs;
65 struct xfs_parent_args unlink_ppargs;
66
67 /* Scratchpads for removing links */
68 struct xfs_da_args pptr_args;
69 };
70
71 /* Release resources tracked in the buffer. */
72 static inline void
xchk_metapath_cleanup(void * buf)73 xchk_metapath_cleanup(
74 void *buf)
75 {
76 struct xchk_metapath *mpath = buf;
77
78 if (mpath->dp_ilock_flags)
79 xfs_iunlock(mpath->dp, mpath->dp_ilock_flags);
80 kfree(mpath->path);
81 }
82
83 /* Set up a metadir path scan. @path must be dynamically allocated. */
84 static inline int
xchk_setup_metapath_scan(struct xfs_scrub * sc,struct xfs_inode * dp,const char * path,struct xfs_inode * ip)85 xchk_setup_metapath_scan(
86 struct xfs_scrub *sc,
87 struct xfs_inode *dp,
88 const char *path,
89 struct xfs_inode *ip)
90 {
91 struct xchk_metapath *mpath;
92 int error;
93
94 if (!path)
95 return -ENOMEM;
96
97 error = xchk_install_live_inode(sc, ip);
98 if (error) {
99 kfree(path);
100 return error;
101 }
102
103 mpath = kzalloc(sizeof(struct xchk_metapath), XCHK_GFP_FLAGS);
104 if (!mpath) {
105 kfree(path);
106 return -ENOMEM;
107 }
108
109 mpath->sc = sc;
110 sc->buf = mpath;
111 sc->buf_cleanup = xchk_metapath_cleanup;
112
113 mpath->dp = dp;
114 mpath->path = path; /* path is now owned by mpath */
115
116 mpath->xname.name = mpath->path;
117 mpath->xname.len = strlen(mpath->path);
118 mpath->xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
119
120 return 0;
121 }
122
123 #ifdef CONFIG_XFS_RT
124 /* Scan the /rtgroups directory itself. */
125 static int
xchk_setup_metapath_rtdir(struct xfs_scrub * sc)126 xchk_setup_metapath_rtdir(
127 struct xfs_scrub *sc)
128 {
129 if (!sc->mp->m_rtdirip)
130 return -ENOENT;
131
132 return xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
133 kasprintf(GFP_KERNEL, "rtgroups"), sc->mp->m_rtdirip);
134 }
135
136 /* Scan a rtgroup inode under the /rtgroups directory. */
137 static int
xchk_setup_metapath_rtginode(struct xfs_scrub * sc,enum xfs_rtg_inodes type)138 xchk_setup_metapath_rtginode(
139 struct xfs_scrub *sc,
140 enum xfs_rtg_inodes type)
141 {
142 struct xfs_rtgroup *rtg;
143 struct xfs_inode *ip;
144 int error;
145
146 rtg = xfs_rtgroup_get(sc->mp, sc->sm->sm_agno);
147 if (!rtg)
148 return -ENOENT;
149
150 ip = rtg->rtg_inodes[type];
151 if (!ip) {
152 error = -ENOENT;
153 goto out_put_rtg;
154 }
155
156 error = xchk_setup_metapath_scan(sc, sc->mp->m_rtdirip,
157 xfs_rtginode_path(rtg_rgno(rtg), type), ip);
158
159 out_put_rtg:
160 xfs_rtgroup_put(rtg);
161 return error;
162 }
163 #else
164 # define xchk_setup_metapath_rtdir(...) (-ENOENT)
165 # define xchk_setup_metapath_rtginode(...) (-ENOENT)
166 #endif /* CONFIG_XFS_RT */
167
168 #ifdef CONFIG_XFS_QUOTA
169 /* Scan the /quota directory itself. */
170 static int
xchk_setup_metapath_quotadir(struct xfs_scrub * sc)171 xchk_setup_metapath_quotadir(
172 struct xfs_scrub *sc)
173 {
174 struct xfs_quotainfo *qi = sc->mp->m_quotainfo;
175
176 if (!qi || !qi->qi_dirip)
177 return -ENOENT;
178
179 return xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
180 kstrdup("quota", GFP_KERNEL), qi->qi_dirip);
181 }
182
183 /* Scan a quota inode under the /quota directory. */
184 static int
xchk_setup_metapath_dqinode(struct xfs_scrub * sc,xfs_dqtype_t type)185 xchk_setup_metapath_dqinode(
186 struct xfs_scrub *sc,
187 xfs_dqtype_t type)
188 {
189 struct xfs_quotainfo *qi = sc->mp->m_quotainfo;
190 struct xfs_inode *ip = NULL;
191
192 if (!qi)
193 return -ENOENT;
194
195 switch (type) {
196 case XFS_DQTYPE_USER:
197 ip = qi->qi_uquotaip;
198 break;
199 case XFS_DQTYPE_GROUP:
200 ip = qi->qi_gquotaip;
201 break;
202 case XFS_DQTYPE_PROJ:
203 ip = qi->qi_pquotaip;
204 break;
205 default:
206 ASSERT(0);
207 return -EINVAL;
208 }
209 if (!ip)
210 return -ENOENT;
211
212 return xchk_setup_metapath_scan(sc, qi->qi_dirip,
213 kstrdup(xfs_dqinode_path(type), GFP_KERNEL), ip);
214 }
215 #else
216 # define xchk_setup_metapath_quotadir(...) (-ENOENT)
217 # define xchk_setup_metapath_dqinode(...) (-ENOENT)
218 #endif /* CONFIG_XFS_QUOTA */
219
220 int
xchk_setup_metapath(struct xfs_scrub * sc)221 xchk_setup_metapath(
222 struct xfs_scrub *sc)
223 {
224 if (!xfs_has_metadir(sc->mp))
225 return -ENOENT;
226 if (sc->sm->sm_gen)
227 return -EINVAL;
228
229 switch (sc->sm->sm_ino) {
230 case XFS_SCRUB_METAPATH_PROBE:
231 /* Just probing, nothing else to do. */
232 if (sc->sm->sm_agno)
233 return -EINVAL;
234 return 0;
235 case XFS_SCRUB_METAPATH_RTDIR:
236 return xchk_setup_metapath_rtdir(sc);
237 case XFS_SCRUB_METAPATH_RTBITMAP:
238 return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
239 case XFS_SCRUB_METAPATH_RTSUMMARY:
240 return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
241 case XFS_SCRUB_METAPATH_QUOTADIR:
242 return xchk_setup_metapath_quotadir(sc);
243 case XFS_SCRUB_METAPATH_USRQUOTA:
244 return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER);
245 case XFS_SCRUB_METAPATH_GRPQUOTA:
246 return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP);
247 case XFS_SCRUB_METAPATH_PRJQUOTA:
248 return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ);
249 default:
250 return -ENOENT;
251 }
252 }
253
254 /*
255 * Take the ILOCK on the metadata directory parent and child. We do not know
256 * that the metadata directory is not corrupt, so we lock the parent and try
257 * to lock the child. Returns 0 if successful, or -EINTR to abort the scrub.
258 */
259 STATIC int
xchk_metapath_ilock_both(struct xchk_metapath * mpath)260 xchk_metapath_ilock_both(
261 struct xchk_metapath *mpath)
262 {
263 struct xfs_scrub *sc = mpath->sc;
264 int error = 0;
265
266 while (true) {
267 xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
268 if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
269 mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
270 return 0;
271 }
272 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
273
274 if (xchk_should_terminate(sc, &error))
275 return error;
276
277 delay(1);
278 }
279
280 ASSERT(0);
281 return -EINTR;
282 }
283
284 /* Unlock parent and child inodes. */
285 static inline void
xchk_metapath_iunlock(struct xchk_metapath * mpath)286 xchk_metapath_iunlock(
287 struct xchk_metapath *mpath)
288 {
289 struct xfs_scrub *sc = mpath->sc;
290
291 xchk_iunlock(sc, XFS_ILOCK_EXCL);
292
293 mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
294 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
295 }
296
297 int
xchk_metapath(struct xfs_scrub * sc)298 xchk_metapath(
299 struct xfs_scrub *sc)
300 {
301 struct xchk_metapath *mpath = sc->buf;
302 xfs_ino_t ino = NULLFSINO;
303 int error;
304
305 /* Just probing, nothing else to do. */
306 if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
307 return 0;
308
309 /* Parent required to do anything else. */
310 if (mpath->dp == NULL) {
311 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
312 return 0;
313 }
314
315 error = xchk_trans_alloc_empty(sc);
316 if (error)
317 return error;
318
319 error = xchk_metapath_ilock_both(mpath);
320 if (error)
321 goto out_cancel;
322
323 /* Make sure the parent dir has a dirent pointing to this file. */
324 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
325 trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
326 if (error == -ENOENT) {
327 /* No directory entry at all */
328 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
329 error = 0;
330 goto out_ilock;
331 }
332 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
333 goto out_ilock;
334 if (ino != sc->ip->i_ino) {
335 /* Pointing to wrong inode */
336 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
337 }
338
339 out_ilock:
340 xchk_metapath_iunlock(mpath);
341 out_cancel:
342 xchk_trans_cancel(sc);
343 return error;
344 }
345
346 #ifdef CONFIG_XFS_ONLINE_REPAIR
347 /* Create the dirent represented by the final component of the path. */
348 STATIC int
xrep_metapath_link(struct xchk_metapath * mpath)349 xrep_metapath_link(
350 struct xchk_metapath *mpath)
351 {
352 struct xfs_scrub *sc = mpath->sc;
353
354 mpath->du.dp = mpath->dp;
355 mpath->du.name = &mpath->xname;
356 mpath->du.ip = sc->ip;
357
358 if (xfs_has_parent(sc->mp))
359 mpath->du.ppargs = &mpath->link_ppargs;
360 else
361 mpath->du.ppargs = NULL;
362
363 trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino);
364
365 return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
366 }
367
368 /* Remove the dirent at the final component of the path. */
369 STATIC int
xrep_metapath_unlink(struct xchk_metapath * mpath,xfs_ino_t ino,struct xfs_inode * ip)370 xrep_metapath_unlink(
371 struct xchk_metapath *mpath,
372 xfs_ino_t ino,
373 struct xfs_inode *ip)
374 {
375 struct xfs_parent_rec rec;
376 struct xfs_scrub *sc = mpath->sc;
377 struct xfs_mount *mp = sc->mp;
378 int error;
379
380 trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino);
381
382 if (!ip) {
383 /* The child inode isn't allocated. Junk the dirent. */
384 xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE);
385 return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname,
386 ino, mpath->unlink_resblks);
387 }
388
389 mpath->du.dp = mpath->dp;
390 mpath->du.name = &mpath->xname;
391 mpath->du.ip = ip;
392 mpath->du.ppargs = NULL;
393
394 /* Figure out if we're removing a parent pointer too. */
395 if (xfs_has_parent(mp)) {
396 xfs_inode_to_parent_rec(&rec, ip);
397 error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
398 &mpath->pptr_args);
399 switch (error) {
400 case -ENOATTR:
401 break;
402 case 0:
403 mpath->du.ppargs = &mpath->unlink_ppargs;
404 break;
405 default:
406 return error;
407 }
408 }
409
410 return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du);
411 }
412
413 /*
414 * Try to create a dirent in @mpath->dp with the name @mpath->xname that points
415 * to @sc->ip. Returns:
416 *
417 * -EEXIST and an @alleged_child if the dirent that points to the wrong inode;
418 * 0 if there is now a dirent pointing to @sc->ip; or
419 * A negative errno on error.
420 */
421 STATIC int
xrep_metapath_try_link(struct xchk_metapath * mpath,xfs_ino_t * alleged_child)422 xrep_metapath_try_link(
423 struct xchk_metapath *mpath,
424 xfs_ino_t *alleged_child)
425 {
426 struct xfs_scrub *sc = mpath->sc;
427 xfs_ino_t ino;
428 int error;
429
430 /* Allocate transaction, lock inodes, join to transaction. */
431 error = xchk_trans_alloc(sc, mpath->link_resblks);
432 if (error)
433 return error;
434
435 error = xchk_metapath_ilock_both(mpath);
436 if (error) {
437 xchk_trans_cancel(sc);
438 return error;
439 }
440 xfs_trans_ijoin(sc->tp, mpath->dp, 0);
441 xfs_trans_ijoin(sc->tp, sc->ip, 0);
442
443 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
444 trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
445 if (error == -ENOENT) {
446 /*
447 * There is no dirent in the directory. Create an entry
448 * pointing to @sc->ip.
449 */
450 error = xrep_metapath_link(mpath);
451 if (error)
452 goto out_cancel;
453
454 error = xrep_trans_commit(sc);
455 xchk_metapath_iunlock(mpath);
456 return error;
457 }
458 if (error)
459 goto out_cancel;
460
461 if (ino == sc->ip->i_ino) {
462 /* The dirent already points to @sc->ip; we're done. */
463 error = 0;
464 goto out_cancel;
465 }
466
467 /*
468 * The dirent points elsewhere; pass that back so that the caller
469 * can try to remove the dirent.
470 */
471 *alleged_child = ino;
472 error = -EEXIST;
473
474 out_cancel:
475 xchk_trans_cancel(sc);
476 xchk_metapath_iunlock(mpath);
477 return error;
478 }
479
480 /*
481 * Take the ILOCK on the metadata directory parent and a bad child, if one is
482 * supplied. We do not know that the metadata directory is not corrupt, so we
483 * lock the parent and try to lock the child. Returns 0 if successful, or
484 * -EINTR to abort the repair. The lock state of @dp is not recorded in @mpath.
485 */
486 STATIC int
xchk_metapath_ilock_parent_and_child(struct xchk_metapath * mpath,struct xfs_inode * ip)487 xchk_metapath_ilock_parent_and_child(
488 struct xchk_metapath *mpath,
489 struct xfs_inode *ip)
490 {
491 struct xfs_scrub *sc = mpath->sc;
492 int error = 0;
493
494 while (true) {
495 xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
496 if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
497 return 0;
498 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
499
500 if (xchk_should_terminate(sc, &error))
501 return error;
502
503 delay(1);
504 }
505
506 ASSERT(0);
507 return -EINTR;
508 }
509
510 /*
511 * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points
512 * to @alleged_child. Returns:
513 *
514 * 0 if there is no longer a dirent;
515 * -EEXIST if the dirent points to @sc->ip;
516 * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or
517 * A negative errno for any other error.
518 */
519 STATIC int
xrep_metapath_try_unlink(struct xchk_metapath * mpath,xfs_ino_t * alleged_child)520 xrep_metapath_try_unlink(
521 struct xchk_metapath *mpath,
522 xfs_ino_t *alleged_child)
523 {
524 struct xfs_scrub *sc = mpath->sc;
525 struct xfs_inode *ip = NULL;
526 xfs_ino_t ino;
527 int error;
528
529 ASSERT(*alleged_child != sc->ip->i_ino);
530
531 trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp,
532 *alleged_child);
533
534 /*
535 * Allocate transaction, grab the alleged child inode, lock inodes,
536 * join to transaction.
537 */
538 error = xchk_trans_alloc(sc, mpath->unlink_resblks);
539 if (error)
540 return error;
541
542 error = xchk_iget(sc, *alleged_child, &ip);
543 if (error == -EINVAL || error == -ENOENT) {
544 /* inode number is bogus, junk the dirent */
545 error = 0;
546 }
547 if (error) {
548 xchk_trans_cancel(sc);
549 return error;
550 }
551
552 error = xchk_metapath_ilock_parent_and_child(mpath, ip);
553 if (error) {
554 xchk_trans_cancel(sc);
555 return error;
556 }
557 xfs_trans_ijoin(sc->tp, mpath->dp, 0);
558 if (ip)
559 xfs_trans_ijoin(sc->tp, ip, 0);
560
561 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
562 trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
563 if (error == -ENOENT) {
564 /*
565 * There is no dirent in the directory anymore. We're ready to
566 * try the link operation again.
567 */
568 error = 0;
569 goto out_cancel;
570 }
571 if (error)
572 goto out_cancel;
573
574 if (ino == sc->ip->i_ino) {
575 /* The dirent already points to @sc->ip; we're done. */
576 error = -EEXIST;
577 goto out_cancel;
578 }
579
580 /*
581 * The dirent does not point to the alleged child. Update the caller
582 * and signal that we want to be called again.
583 */
584 if (ino != *alleged_child) {
585 *alleged_child = ino;
586 error = -EAGAIN;
587 goto out_cancel;
588 }
589
590 /* Remove the link to the child. */
591 error = xrep_metapath_unlink(mpath, ino, ip);
592 if (error)
593 goto out_cancel;
594
595 error = xrep_trans_commit(sc);
596 goto out_unlock;
597
598 out_cancel:
599 xchk_trans_cancel(sc);
600 out_unlock:
601 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
602 if (ip) {
603 xfs_iunlock(ip, XFS_ILOCK_EXCL);
604 xchk_irele(sc, ip);
605 }
606 return error;
607 }
608
609 /*
610 * Make sure the metadata directory path points to the child being examined.
611 *
612 * Repair needs to be able to create a directory structure, create its own
613 * transactions, and take ILOCKs. This function /must/ be called after all
614 * other repairs have completed.
615 */
616 int
xrep_metapath(struct xfs_scrub * sc)617 xrep_metapath(
618 struct xfs_scrub *sc)
619 {
620 struct xchk_metapath *mpath = sc->buf;
621 struct xfs_mount *mp = sc->mp;
622 int error = 0;
623
624 /* Just probing, nothing to repair. */
625 if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
626 return 0;
627
628 /* Parent required to do anything else. */
629 if (mpath->dp == NULL)
630 return -EFSCORRUPTED;
631
632 /*
633 * Make sure the child file actually has an attr fork to receive a new
634 * parent pointer if the fs has parent pointers.
635 */
636 if (xfs_has_parent(mp)) {
637 error = xfs_attr_add_fork(sc->ip,
638 sizeof(struct xfs_attr_sf_hdr), 1);
639 if (error)
640 return error;
641 }
642
643 /* Compute block reservation required to unlink and link a file. */
644 mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN);
645 mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN);
646
647 do {
648 xfs_ino_t alleged_child;
649
650 /* Re-establish the link, or tell us which inode to remove. */
651 error = xrep_metapath_try_link(mpath, &alleged_child);
652 if (!error)
653 return 0;
654 if (error != -EEXIST)
655 return error;
656
657 /*
658 * Remove an incorrect link to an alleged child, or tell us
659 * which inode to remove.
660 */
661 do {
662 error = xrep_metapath_try_unlink(mpath, &alleged_child);
663 } while (error == -EAGAIN);
664 if (error == -EEXIST) {
665 /* Link established; we're done. */
666 error = 0;
667 break;
668 }
669 } while (!error);
670
671 return error;
672 }
673 #endif /* CONFIG_XFS_ONLINE_REPAIR */
674