File Index
Files and directories
Sign in
FILE

metapath.c

File preview with syntax highlighting.

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