]> Git Repo - J-linux.git/blob - fs/xfs/scrub/metapath.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / fs / xfs / scrub / metapath.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2023-2024 Oracle.  All Rights Reserved.
4  * Author: Darrick J. Wong <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */
This page took 0.064132 seconds and 4 git commands to generate.