accel/qaic: Add AIC200 support
[drm/drm-misc.git] / fs / xfs / scrub / metapath.c
blobb78db651346518ebafc6b7bf1db9c776f7ad16c3
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"
31 * Metadata Directory Tree Paths
32 * =============================
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.
41 struct xchk_metapath {
42 struct xfs_scrub *sc;
44 /* Name for lookup */
45 struct xfs_name xname;
47 /* Directory update for repairs */
48 struct xfs_dir_update du;
50 /* Path down to this metadata file from the parent directory */
51 const char *path;
53 /* Directory parent of the metadata file. */
54 struct xfs_inode *dp;
56 /* Locks held on dp */
57 unsigned int dp_ilock_flags;
59 /* Transaction block reservations */
60 unsigned int link_resblks;
61 unsigned int unlink_resblks;
63 /* Parent pointer updates */
64 struct xfs_parent_args link_ppargs;
65 struct xfs_parent_args unlink_ppargs;
67 /* Scratchpads for removing links */
68 struct xfs_da_args pptr_args;
71 /* Release resources tracked in the buffer. */
72 static inline void
73 xchk_metapath_cleanup(
74 void *buf)
76 struct xchk_metapath *mpath = buf;
78 if (mpath->dp_ilock_flags)
79 xfs_iunlock(mpath->dp, mpath->dp_ilock_flags);
80 kfree(mpath->path);
83 /* Set up a metadir path scan. @path must be dynamically allocated. */
84 static inline int
85 xchk_setup_metapath_scan(
86 struct xfs_scrub *sc,
87 struct xfs_inode *dp,
88 const char *path,
89 struct xfs_inode *ip)
91 struct xchk_metapath *mpath;
92 int error;
94 if (!path)
95 return -ENOMEM;
97 error = xchk_install_live_inode(sc, ip);
98 if (error) {
99 kfree(path);
100 return error;
103 mpath = kzalloc(sizeof(struct xchk_metapath), XCHK_GFP_FLAGS);
104 if (!mpath) {
105 kfree(path);
106 return -ENOMEM;
109 mpath->sc = sc;
110 sc->buf = mpath;
111 sc->buf_cleanup = xchk_metapath_cleanup;
113 mpath->dp = dp;
114 mpath->path = path; /* path is now owned by mpath */
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);
120 return 0;
123 #ifdef CONFIG_XFS_RT
124 /* Scan the /rtgroups directory itself. */
125 static int
126 xchk_setup_metapath_rtdir(
127 struct xfs_scrub *sc)
129 if (!sc->mp->m_rtdirip)
130 return -ENOENT;
132 return xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
133 kasprintf(GFP_KERNEL, "rtgroups"), sc->mp->m_rtdirip);
136 /* Scan a rtgroup inode under the /rtgroups directory. */
137 static int
138 xchk_setup_metapath_rtginode(
139 struct xfs_scrub *sc,
140 enum xfs_rtg_inodes type)
142 struct xfs_rtgroup *rtg;
143 struct xfs_inode *ip;
144 int error;
146 rtg = xfs_rtgroup_get(sc->mp, sc->sm->sm_agno);
147 if (!rtg)
148 return -ENOENT;
150 ip = rtg->rtg_inodes[type];
151 if (!ip) {
152 error = -ENOENT;
153 goto out_put_rtg;
156 error = xchk_setup_metapath_scan(sc, sc->mp->m_rtdirip,
157 xfs_rtginode_path(rtg_rgno(rtg), type), ip);
159 out_put_rtg:
160 xfs_rtgroup_put(rtg);
161 return error;
163 #else
164 # define xchk_setup_metapath_rtdir(...) (-ENOENT)
165 # define xchk_setup_metapath_rtginode(...) (-ENOENT)
166 #endif /* CONFIG_XFS_RT */
168 #ifdef CONFIG_XFS_QUOTA
169 /* Scan the /quota directory itself. */
170 static int
171 xchk_setup_metapath_quotadir(
172 struct xfs_scrub *sc)
174 struct xfs_trans *tp;
175 struct xfs_inode *dp = NULL;
176 int error;
178 error = xfs_trans_alloc_empty(sc->mp, &tp);
179 if (error)
180 return error;
182 error = xfs_dqinode_load_parent(tp, &dp);
183 xfs_trans_cancel(tp);
184 if (error)
185 return error;
187 error = xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
188 kasprintf(GFP_KERNEL, "quota"), dp);
189 xfs_irele(dp);
190 return error;
193 /* Scan a quota inode under the /quota directory. */
194 static int
195 xchk_setup_metapath_dqinode(
196 struct xfs_scrub *sc,
197 xfs_dqtype_t type)
199 struct xfs_trans *tp = NULL;
200 struct xfs_inode *dp = NULL;
201 struct xfs_inode *ip = NULL;
202 const char *path;
203 int error;
205 error = xfs_trans_alloc_empty(sc->mp, &tp);
206 if (error)
207 return error;
209 error = xfs_dqinode_load_parent(tp, &dp);
210 if (error)
211 goto out_cancel;
213 error = xfs_dqinode_load(tp, dp, type, &ip);
214 if (error)
215 goto out_dp;
217 xfs_trans_cancel(tp);
218 tp = NULL;
220 path = kasprintf(GFP_KERNEL, "%s", xfs_dqinode_path(type));
221 error = xchk_setup_metapath_scan(sc, dp, path, ip);
223 xfs_irele(ip);
224 out_dp:
225 xfs_irele(dp);
226 out_cancel:
227 if (tp)
228 xfs_trans_cancel(tp);
229 return error;
231 #else
232 # define xchk_setup_metapath_quotadir(...) (-ENOENT)
233 # define xchk_setup_metapath_dqinode(...) (-ENOENT)
234 #endif /* CONFIG_XFS_QUOTA */
237 xchk_setup_metapath(
238 struct xfs_scrub *sc)
240 if (!xfs_has_metadir(sc->mp))
241 return -ENOENT;
242 if (sc->sm->sm_gen)
243 return -EINVAL;
245 switch (sc->sm->sm_ino) {
246 case XFS_SCRUB_METAPATH_PROBE:
247 /* Just probing, nothing else to do. */
248 if (sc->sm->sm_agno)
249 return -EINVAL;
250 return 0;
251 case XFS_SCRUB_METAPATH_RTDIR:
252 return xchk_setup_metapath_rtdir(sc);
253 case XFS_SCRUB_METAPATH_RTBITMAP:
254 return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
255 case XFS_SCRUB_METAPATH_RTSUMMARY:
256 return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
257 case XFS_SCRUB_METAPATH_QUOTADIR:
258 return xchk_setup_metapath_quotadir(sc);
259 case XFS_SCRUB_METAPATH_USRQUOTA:
260 return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER);
261 case XFS_SCRUB_METAPATH_GRPQUOTA:
262 return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP);
263 case XFS_SCRUB_METAPATH_PRJQUOTA:
264 return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ);
265 default:
266 return -ENOENT;
271 * Take the ILOCK on the metadata directory parent and child. We do not know
272 * that the metadata directory is not corrupt, so we lock the parent and try
273 * to lock the child. Returns 0 if successful, or -EINTR to abort the scrub.
275 STATIC int
276 xchk_metapath_ilock_both(
277 struct xchk_metapath *mpath)
279 struct xfs_scrub *sc = mpath->sc;
280 int error = 0;
282 while (true) {
283 xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
284 if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
285 mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
286 return 0;
288 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
290 if (xchk_should_terminate(sc, &error))
291 return error;
293 delay(1);
296 ASSERT(0);
297 return -EINTR;
300 /* Unlock parent and child inodes. */
301 static inline void
302 xchk_metapath_iunlock(
303 struct xchk_metapath *mpath)
305 struct xfs_scrub *sc = mpath->sc;
307 xchk_iunlock(sc, XFS_ILOCK_EXCL);
309 mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
310 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
314 xchk_metapath(
315 struct xfs_scrub *sc)
317 struct xchk_metapath *mpath = sc->buf;
318 xfs_ino_t ino = NULLFSINO;
319 int error;
321 /* Just probing, nothing else to do. */
322 if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
323 return 0;
325 /* Parent required to do anything else. */
326 if (mpath->dp == NULL) {
327 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
328 return 0;
331 error = xchk_trans_alloc_empty(sc);
332 if (error)
333 return error;
335 error = xchk_metapath_ilock_both(mpath);
336 if (error)
337 goto out_cancel;
339 /* Make sure the parent dir has a dirent pointing to this file. */
340 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
341 trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
342 if (error == -ENOENT) {
343 /* No directory entry at all */
344 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
345 error = 0;
346 goto out_ilock;
348 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
349 goto out_ilock;
350 if (ino != sc->ip->i_ino) {
351 /* Pointing to wrong inode */
352 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
355 out_ilock:
356 xchk_metapath_iunlock(mpath);
357 out_cancel:
358 xchk_trans_cancel(sc);
359 return error;
362 #ifdef CONFIG_XFS_ONLINE_REPAIR
363 /* Create the dirent represented by the final component of the path. */
364 STATIC int
365 xrep_metapath_link(
366 struct xchk_metapath *mpath)
368 struct xfs_scrub *sc = mpath->sc;
370 mpath->du.dp = mpath->dp;
371 mpath->du.name = &mpath->xname;
372 mpath->du.ip = sc->ip;
374 if (xfs_has_parent(sc->mp))
375 mpath->du.ppargs = &mpath->link_ppargs;
376 else
377 mpath->du.ppargs = NULL;
379 trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino);
381 return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
384 /* Remove the dirent at the final component of the path. */
385 STATIC int
386 xrep_metapath_unlink(
387 struct xchk_metapath *mpath,
388 xfs_ino_t ino,
389 struct xfs_inode *ip)
391 struct xfs_parent_rec rec;
392 struct xfs_scrub *sc = mpath->sc;
393 struct xfs_mount *mp = sc->mp;
394 int error;
396 trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino);
398 if (!ip) {
399 /* The child inode isn't allocated. Junk the dirent. */
400 xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE);
401 return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname,
402 ino, mpath->unlink_resblks);
405 mpath->du.dp = mpath->dp;
406 mpath->du.name = &mpath->xname;
407 mpath->du.ip = ip;
408 mpath->du.ppargs = NULL;
410 /* Figure out if we're removing a parent pointer too. */
411 if (xfs_has_parent(mp)) {
412 xfs_inode_to_parent_rec(&rec, ip);
413 error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
414 &mpath->pptr_args);
415 switch (error) {
416 case -ENOATTR:
417 break;
418 case 0:
419 mpath->du.ppargs = &mpath->unlink_ppargs;
420 break;
421 default:
422 return error;
426 return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du);
430 * Try to create a dirent in @mpath->dp with the name @mpath->xname that points
431 * to @sc->ip. Returns:
433 * -EEXIST and an @alleged_child if the dirent that points to the wrong inode;
434 * 0 if there is now a dirent pointing to @sc->ip; or
435 * A negative errno on error.
437 STATIC int
438 xrep_metapath_try_link(
439 struct xchk_metapath *mpath,
440 xfs_ino_t *alleged_child)
442 struct xfs_scrub *sc = mpath->sc;
443 xfs_ino_t ino;
444 int error;
446 /* Allocate transaction, lock inodes, join to transaction. */
447 error = xchk_trans_alloc(sc, mpath->link_resblks);
448 if (error)
449 return error;
451 error = xchk_metapath_ilock_both(mpath);
452 if (error) {
453 xchk_trans_cancel(sc);
454 return error;
456 xfs_trans_ijoin(sc->tp, mpath->dp, 0);
457 xfs_trans_ijoin(sc->tp, sc->ip, 0);
459 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
460 trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
461 if (error == -ENOENT) {
463 * There is no dirent in the directory. Create an entry
464 * pointing to @sc->ip.
466 error = xrep_metapath_link(mpath);
467 if (error)
468 goto out_cancel;
470 error = xrep_trans_commit(sc);
471 xchk_metapath_iunlock(mpath);
472 return error;
474 if (error)
475 goto out_cancel;
477 if (ino == sc->ip->i_ino) {
478 /* The dirent already points to @sc->ip; we're done. */
479 error = 0;
480 goto out_cancel;
484 * The dirent points elsewhere; pass that back so that the caller
485 * can try to remove the dirent.
487 *alleged_child = ino;
488 error = -EEXIST;
490 out_cancel:
491 xchk_trans_cancel(sc);
492 xchk_metapath_iunlock(mpath);
493 return error;
497 * Take the ILOCK on the metadata directory parent and a bad child, if one is
498 * supplied. We do not know that the metadata directory is not corrupt, so we
499 * lock the parent and try to lock the child. Returns 0 if successful, or
500 * -EINTR to abort the repair. The lock state of @dp is not recorded in @mpath.
502 STATIC int
503 xchk_metapath_ilock_parent_and_child(
504 struct xchk_metapath *mpath,
505 struct xfs_inode *ip)
507 struct xfs_scrub *sc = mpath->sc;
508 int error = 0;
510 while (true) {
511 xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
512 if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
513 return 0;
514 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
516 if (xchk_should_terminate(sc, &error))
517 return error;
519 delay(1);
522 ASSERT(0);
523 return -EINTR;
527 * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points
528 * to @alleged_child. Returns:
530 * 0 if there is no longer a dirent;
531 * -EEXIST if the dirent points to @sc->ip;
532 * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or
533 * A negative errno for any other error.
535 STATIC int
536 xrep_metapath_try_unlink(
537 struct xchk_metapath *mpath,
538 xfs_ino_t *alleged_child)
540 struct xfs_scrub *sc = mpath->sc;
541 struct xfs_inode *ip = NULL;
542 xfs_ino_t ino;
543 int error;
545 ASSERT(*alleged_child != sc->ip->i_ino);
547 trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp,
548 *alleged_child);
551 * Allocate transaction, grab the alleged child inode, lock inodes,
552 * join to transaction.
554 error = xchk_trans_alloc(sc, mpath->unlink_resblks);
555 if (error)
556 return error;
558 error = xchk_iget(sc, *alleged_child, &ip);
559 if (error == -EINVAL || error == -ENOENT) {
560 /* inode number is bogus, junk the dirent */
561 error = 0;
563 if (error) {
564 xchk_trans_cancel(sc);
565 return error;
568 error = xchk_metapath_ilock_parent_and_child(mpath, ip);
569 if (error) {
570 xchk_trans_cancel(sc);
571 return error;
573 xfs_trans_ijoin(sc->tp, mpath->dp, 0);
574 if (ip)
575 xfs_trans_ijoin(sc->tp, ip, 0);
577 error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
578 trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
579 if (error == -ENOENT) {
581 * There is no dirent in the directory anymore. We're ready to
582 * try the link operation again.
584 error = 0;
585 goto out_cancel;
587 if (error)
588 goto out_cancel;
590 if (ino == sc->ip->i_ino) {
591 /* The dirent already points to @sc->ip; we're done. */
592 error = -EEXIST;
593 goto out_cancel;
597 * The dirent does not point to the alleged child. Update the caller
598 * and signal that we want to be called again.
600 if (ino != *alleged_child) {
601 *alleged_child = ino;
602 error = -EAGAIN;
603 goto out_cancel;
606 /* Remove the link to the child. */
607 error = xrep_metapath_unlink(mpath, ino, ip);
608 if (error)
609 goto out_cancel;
611 error = xrep_trans_commit(sc);
612 goto out_unlock;
614 out_cancel:
615 xchk_trans_cancel(sc);
616 out_unlock:
617 xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
618 if (ip) {
619 xfs_iunlock(ip, XFS_ILOCK_EXCL);
620 xchk_irele(sc, ip);
622 return error;
626 * Make sure the metadata directory path points to the child being examined.
628 * Repair needs to be able to create a directory structure, create its own
629 * transactions, and take ILOCKs. This function /must/ be called after all
630 * other repairs have completed.
633 xrep_metapath(
634 struct xfs_scrub *sc)
636 struct xchk_metapath *mpath = sc->buf;
637 struct xfs_mount *mp = sc->mp;
638 int error = 0;
640 /* Just probing, nothing to repair. */
641 if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
642 return 0;
644 /* Parent required to do anything else. */
645 if (mpath->dp == NULL)
646 return -EFSCORRUPTED;
649 * Make sure the child file actually has an attr fork to receive a new
650 * parent pointer if the fs has parent pointers.
652 if (xfs_has_parent(mp)) {
653 error = xfs_attr_add_fork(sc->ip,
654 sizeof(struct xfs_attr_sf_hdr), 1);
655 if (error)
656 return error;
659 /* Compute block reservation required to unlink and link a file. */
660 mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN);
661 mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN);
663 do {
664 xfs_ino_t alleged_child;
666 /* Re-establish the link, or tell us which inode to remove. */
667 error = xrep_metapath_try_link(mpath, &alleged_child);
668 if (!error)
669 return 0;
670 if (error != -EEXIST)
671 return error;
674 * Remove an incorrect link to an alleged child, or tell us
675 * which inode to remove.
677 do {
678 error = xrep_metapath_try_unlink(mpath, &alleged_child);
679 } while (error == -EAGAIN);
680 if (error == -EEXIST) {
681 /* Link established; we're done. */
682 error = 0;
683 break;
685 } while (!error);
687 return error;
689 #endif /* CONFIG_XFS_ONLINE_REPAIR */