xref: /linux/fs/xfs/scrub/metapath.c (revision a74923333d9c3bc7cae3f8820d5e80535dca1457)
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
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 int
169 xchk_setup_metapath(
170 	struct xfs_scrub	*sc)
171 {
172 	if (!xfs_has_metadir(sc->mp))
173 		return -ENOENT;
174 	if (sc->sm->sm_gen)
175 		return -EINVAL;
176 
177 	switch (sc->sm->sm_ino) {
178 	case XFS_SCRUB_METAPATH_PROBE:
179 		/* Just probing, nothing else to do. */
180 		if (sc->sm->sm_agno)
181 			return -EINVAL;
182 		return 0;
183 	case XFS_SCRUB_METAPATH_RTDIR:
184 		return xchk_setup_metapath_rtdir(sc);
185 	case XFS_SCRUB_METAPATH_RTBITMAP:
186 		return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
187 	case XFS_SCRUB_METAPATH_RTSUMMARY:
188 		return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
189 	default:
190 		return -ENOENT;
191 	}
192 }
193 
194 /*
195  * Take the ILOCK on the metadata directory parent and child.  We do not know
196  * that the metadata directory is not corrupt, so we lock the parent and try
197  * to lock the child.  Returns 0 if successful, or -EINTR to abort the scrub.
198  */
199 STATIC int
200 xchk_metapath_ilock_both(
201 	struct xchk_metapath	*mpath)
202 {
203 	struct xfs_scrub	*sc = mpath->sc;
204 	int			error = 0;
205 
206 	while (true) {
207 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
208 		if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
209 			mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
210 			return 0;
211 		}
212 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
213 
214 		if (xchk_should_terminate(sc, &error))
215 			return error;
216 
217 		delay(1);
218 	}
219 
220 	ASSERT(0);
221 	return -EINTR;
222 }
223 
224 /* Unlock parent and child inodes. */
225 static inline void
226 xchk_metapath_iunlock(
227 	struct xchk_metapath	*mpath)
228 {
229 	struct xfs_scrub	*sc = mpath->sc;
230 
231 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
232 
233 	mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
234 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
235 }
236 
237 int
238 xchk_metapath(
239 	struct xfs_scrub	*sc)
240 {
241 	struct xchk_metapath	*mpath = sc->buf;
242 	xfs_ino_t		ino = NULLFSINO;
243 	int			error;
244 
245 	/* Just probing, nothing else to do. */
246 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
247 		return 0;
248 
249 	/* Parent required to do anything else. */
250 	if (mpath->dp == NULL) {
251 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
252 		return 0;
253 	}
254 
255 	error = xchk_trans_alloc_empty(sc);
256 	if (error)
257 		return error;
258 
259 	error = xchk_metapath_ilock_both(mpath);
260 	if (error)
261 		goto out_cancel;
262 
263 	/* Make sure the parent dir has a dirent pointing to this file. */
264 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
265 	trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
266 	if (error == -ENOENT) {
267 		/* No directory entry at all */
268 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
269 		error = 0;
270 		goto out_ilock;
271 	}
272 	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
273 		goto out_ilock;
274 	if (ino != sc->ip->i_ino) {
275 		/* Pointing to wrong inode */
276 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
277 	}
278 
279 out_ilock:
280 	xchk_metapath_iunlock(mpath);
281 out_cancel:
282 	xchk_trans_cancel(sc);
283 	return error;
284 }
285 
286 #ifdef CONFIG_XFS_ONLINE_REPAIR
287 /* Create the dirent represented by the final component of the path. */
288 STATIC int
289 xrep_metapath_link(
290 	struct xchk_metapath	*mpath)
291 {
292 	struct xfs_scrub	*sc = mpath->sc;
293 
294 	mpath->du.dp = mpath->dp;
295 	mpath->du.name = &mpath->xname;
296 	mpath->du.ip = sc->ip;
297 
298 	if (xfs_has_parent(sc->mp))
299 		mpath->du.ppargs = &mpath->link_ppargs;
300 	else
301 		mpath->du.ppargs = NULL;
302 
303 	trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino);
304 
305 	return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
306 }
307 
308 /* Remove the dirent at the final component of the path. */
309 STATIC int
310 xrep_metapath_unlink(
311 	struct xchk_metapath	*mpath,
312 	xfs_ino_t		ino,
313 	struct xfs_inode	*ip)
314 {
315 	struct xfs_parent_rec	rec;
316 	struct xfs_scrub	*sc = mpath->sc;
317 	struct xfs_mount	*mp = sc->mp;
318 	int			error;
319 
320 	trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino);
321 
322 	if (!ip) {
323 		/* The child inode isn't allocated.  Junk the dirent. */
324 		xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE);
325 		return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname,
326 				ino, mpath->unlink_resblks);
327 	}
328 
329 	mpath->du.dp = mpath->dp;
330 	mpath->du.name = &mpath->xname;
331 	mpath->du.ip = ip;
332 	mpath->du.ppargs = NULL;
333 
334 	/* Figure out if we're removing a parent pointer too. */
335 	if (xfs_has_parent(mp)) {
336 		xfs_inode_to_parent_rec(&rec, ip);
337 		error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
338 				&mpath->pptr_args);
339 		switch (error) {
340 		case -ENOATTR:
341 			break;
342 		case 0:
343 			mpath->du.ppargs = &mpath->unlink_ppargs;
344 			break;
345 		default:
346 			return error;
347 		}
348 	}
349 
350 	return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du);
351 }
352 
353 /*
354  * Try to create a dirent in @mpath->dp with the name @mpath->xname that points
355  * to @sc->ip.  Returns:
356  *
357  * -EEXIST and an @alleged_child if the dirent that points to the wrong inode;
358  * 0 if there is now a dirent pointing to @sc->ip; or
359  * A negative errno on error.
360  */
361 STATIC int
362 xrep_metapath_try_link(
363 	struct xchk_metapath	*mpath,
364 	xfs_ino_t		*alleged_child)
365 {
366 	struct xfs_scrub	*sc = mpath->sc;
367 	xfs_ino_t		ino;
368 	int			error;
369 
370 	/* Allocate transaction, lock inodes, join to transaction. */
371 	error = xchk_trans_alloc(sc, mpath->link_resblks);
372 	if (error)
373 		return error;
374 
375 	error = xchk_metapath_ilock_both(mpath);
376 	if (error) {
377 		xchk_trans_cancel(sc);
378 		return error;
379 	}
380 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
381 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
382 
383 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
384 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
385 	if (error == -ENOENT) {
386 		/*
387 		 * There is no dirent in the directory.  Create an entry
388 		 * pointing to @sc->ip.
389 		 */
390 		error = xrep_metapath_link(mpath);
391 		if (error)
392 			goto out_cancel;
393 
394 		error = xrep_trans_commit(sc);
395 		xchk_metapath_iunlock(mpath);
396 		return error;
397 	}
398 	if (error)
399 		goto out_cancel;
400 
401 	if (ino == sc->ip->i_ino) {
402 		/* The dirent already points to @sc->ip; we're done. */
403 		error = 0;
404 		goto out_cancel;
405 	}
406 
407 	/*
408 	 * The dirent points elsewhere; pass that back so that the caller
409 	 * can try to remove the dirent.
410 	 */
411 	*alleged_child = ino;
412 	error = -EEXIST;
413 
414 out_cancel:
415 	xchk_trans_cancel(sc);
416 	xchk_metapath_iunlock(mpath);
417 	return error;
418 }
419 
420 /*
421  * Take the ILOCK on the metadata directory parent and a bad child, if one is
422  * supplied.  We do not know that the metadata directory is not corrupt, so we
423  * lock the parent and try to lock the child.  Returns 0 if successful, or
424  * -EINTR to abort the repair.  The lock state of @dp is not recorded in @mpath.
425  */
426 STATIC int
427 xchk_metapath_ilock_parent_and_child(
428 	struct xchk_metapath	*mpath,
429 	struct xfs_inode	*ip)
430 {
431 	struct xfs_scrub	*sc = mpath->sc;
432 	int			error = 0;
433 
434 	while (true) {
435 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
436 		if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
437 			return 0;
438 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
439 
440 		if (xchk_should_terminate(sc, &error))
441 			return error;
442 
443 		delay(1);
444 	}
445 
446 	ASSERT(0);
447 	return -EINTR;
448 }
449 
450 /*
451  * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points
452  * to @alleged_child.  Returns:
453  *
454  * 0 if there is no longer a dirent;
455  * -EEXIST if the dirent points to @sc->ip;
456  * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or
457  * A negative errno for any other error.
458  */
459 STATIC int
460 xrep_metapath_try_unlink(
461 	struct xchk_metapath	*mpath,
462 	xfs_ino_t		*alleged_child)
463 {
464 	struct xfs_scrub	*sc = mpath->sc;
465 	struct xfs_inode	*ip = NULL;
466 	xfs_ino_t		ino;
467 	int			error;
468 
469 	ASSERT(*alleged_child != sc->ip->i_ino);
470 
471 	trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp,
472 			*alleged_child);
473 
474 	/*
475 	 * Allocate transaction, grab the alleged child inode, lock inodes,
476 	 * join to transaction.
477 	 */
478 	error = xchk_trans_alloc(sc, mpath->unlink_resblks);
479 	if (error)
480 		return error;
481 
482 	error = xchk_iget(sc, *alleged_child, &ip);
483 	if (error == -EINVAL || error == -ENOENT) {
484 		/* inode number is bogus, junk the dirent */
485 		error = 0;
486 	}
487 	if (error) {
488 		xchk_trans_cancel(sc);
489 		return error;
490 	}
491 
492 	error = xchk_metapath_ilock_parent_and_child(mpath, ip);
493 	if (error) {
494 		xchk_trans_cancel(sc);
495 		return error;
496 	}
497 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
498 	if (ip)
499 		xfs_trans_ijoin(sc->tp, ip, 0);
500 
501 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
502 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
503 	if (error == -ENOENT) {
504 		/*
505 		 * There is no dirent in the directory anymore.  We're ready to
506 		 * try the link operation again.
507 		 */
508 		error = 0;
509 		goto out_cancel;
510 	}
511 	if (error)
512 		goto out_cancel;
513 
514 	if (ino == sc->ip->i_ino) {
515 		/* The dirent already points to @sc->ip; we're done. */
516 		error = -EEXIST;
517 		goto out_cancel;
518 	}
519 
520 	/*
521 	 * The dirent does not point to the alleged child.  Update the caller
522 	 * and signal that we want to be called again.
523 	 */
524 	if (ino != *alleged_child) {
525 		*alleged_child = ino;
526 		error = -EAGAIN;
527 		goto out_cancel;
528 	}
529 
530 	/* Remove the link to the child. */
531 	error = xrep_metapath_unlink(mpath, ino, ip);
532 	if (error)
533 		goto out_cancel;
534 
535 	error = xrep_trans_commit(sc);
536 	goto out_unlock;
537 
538 out_cancel:
539 	xchk_trans_cancel(sc);
540 out_unlock:
541 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
542 	if (ip) {
543 		xfs_iunlock(ip, XFS_ILOCK_EXCL);
544 		xchk_irele(sc, ip);
545 	}
546 	return error;
547 }
548 
549 /*
550  * Make sure the metadata directory path points to the child being examined.
551  *
552  * Repair needs to be able to create a directory structure, create its own
553  * transactions, and take ILOCKs.  This function /must/ be called after all
554  * other repairs have completed.
555  */
556 int
557 xrep_metapath(
558 	struct xfs_scrub	*sc)
559 {
560 	struct xchk_metapath	*mpath = sc->buf;
561 	struct xfs_mount	*mp = sc->mp;
562 	int			error = 0;
563 
564 	/* Just probing, nothing to repair. */
565 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
566 		return 0;
567 
568 	/* Parent required to do anything else. */
569 	if (mpath->dp == NULL)
570 		return -EFSCORRUPTED;
571 
572 	/*
573 	 * Make sure the child file actually has an attr fork to receive a new
574 	 * parent pointer if the fs has parent pointers.
575 	 */
576 	if (xfs_has_parent(mp)) {
577 		error = xfs_attr_add_fork(sc->ip,
578 				sizeof(struct xfs_attr_sf_hdr), 1);
579 		if (error)
580 			return error;
581 	}
582 
583 	/* Compute block reservation required to unlink and link a file. */
584 	mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN);
585 	mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN);
586 
587 	do {
588 		xfs_ino_t	alleged_child;
589 
590 		/* Re-establish the link, or tell us which inode to remove. */
591 		error = xrep_metapath_try_link(mpath, &alleged_child);
592 		if (!error)
593 			return 0;
594 		if (error != -EEXIST)
595 			return error;
596 
597 		/*
598 		 * Remove an incorrect link to an alleged child, or tell us
599 		 * which inode to remove.
600 		 */
601 		do {
602 			error = xrep_metapath_try_unlink(mpath, &alleged_child);
603 		} while (error == -EAGAIN);
604 		if (error == -EEXIST) {
605 			/* Link established; we're done. */
606 			error = 0;
607 			break;
608 		}
609 	} while (!error);
610 
611 	return error;
612 }
613 #endif /* CONFIG_XFS_ONLINE_REPAIR */
614