xref: /linux/fs/xfs/scrub/metapath.c (revision 0d2c636e489c115add86bd66952880f92b5edab7)
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 "scrub/scrub.h"
24 #include "scrub/common.h"
25 #include "scrub/trace.h"
26 #include "scrub/readdir.h"
27 #include "scrub/repair.h"
28 
29 /*
30  * Metadata Directory Tree Paths
31  * =============================
32  *
33  * A filesystem with metadir enabled expects to find metadata structures
34  * attached to files that are accessible by walking a path down the metadata
35  * directory tree.  Given the metadir path and the incore inode storing the
36  * metadata, this scrubber ensures that the ondisk metadir path points to the
37  * ondisk inode represented by the incore inode.
38  */
39 
40 struct xchk_metapath {
41 	struct xfs_scrub		*sc;
42 
43 	/* Name for lookup */
44 	struct xfs_name			xname;
45 
46 	/* Directory update for repairs */
47 	struct xfs_dir_update		du;
48 
49 	/* Path down to this metadata file from the parent directory */
50 	const char			*path;
51 
52 	/* Directory parent of the metadata file. */
53 	struct xfs_inode		*dp;
54 
55 	/* Locks held on dp */
56 	unsigned int			dp_ilock_flags;
57 
58 	/* Transaction block reservations */
59 	unsigned int			link_resblks;
60 	unsigned int			unlink_resblks;
61 
62 	/* Parent pointer updates */
63 	struct xfs_parent_args		link_ppargs;
64 	struct xfs_parent_args		unlink_ppargs;
65 
66 	/* Scratchpads for removing links */
67 	struct xfs_da_args		pptr_args;
68 };
69 
70 /* Release resources tracked in the buffer. */
71 static inline void
72 xchk_metapath_cleanup(
73 	void			*buf)
74 {
75 	struct xchk_metapath	*mpath = buf;
76 
77 	if (mpath->dp_ilock_flags)
78 		xfs_iunlock(mpath->dp, mpath->dp_ilock_flags);
79 	kfree(mpath->path);
80 }
81 
82 int
83 xchk_setup_metapath(
84 	struct xfs_scrub	*sc)
85 {
86 	if (!xfs_has_metadir(sc->mp))
87 		return -ENOENT;
88 	if (sc->sm->sm_gen)
89 		return -EINVAL;
90 
91 	switch (sc->sm->sm_ino) {
92 	case XFS_SCRUB_METAPATH_PROBE:
93 		/* Just probing, nothing else to do. */
94 		if (sc->sm->sm_agno)
95 			return -EINVAL;
96 		return 0;
97 	default:
98 		return -ENOENT;
99 	}
100 }
101 
102 /*
103  * Take the ILOCK on the metadata directory parent and child.  We do not know
104  * that the metadata directory is not corrupt, so we lock the parent and try
105  * to lock the child.  Returns 0 if successful, or -EINTR to abort the scrub.
106  */
107 STATIC int
108 xchk_metapath_ilock_both(
109 	struct xchk_metapath	*mpath)
110 {
111 	struct xfs_scrub	*sc = mpath->sc;
112 	int			error = 0;
113 
114 	while (true) {
115 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
116 		if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
117 			mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
118 			return 0;
119 		}
120 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
121 
122 		if (xchk_should_terminate(sc, &error))
123 			return error;
124 
125 		delay(1);
126 	}
127 
128 	ASSERT(0);
129 	return -EINTR;
130 }
131 
132 /* Unlock parent and child inodes. */
133 static inline void
134 xchk_metapath_iunlock(
135 	struct xchk_metapath	*mpath)
136 {
137 	struct xfs_scrub	*sc = mpath->sc;
138 
139 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
140 
141 	mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
142 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
143 }
144 
145 int
146 xchk_metapath(
147 	struct xfs_scrub	*sc)
148 {
149 	struct xchk_metapath	*mpath = sc->buf;
150 	xfs_ino_t		ino = NULLFSINO;
151 	int			error;
152 
153 	/* Just probing, nothing else to do. */
154 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
155 		return 0;
156 
157 	/* Parent required to do anything else. */
158 	if (mpath->dp == NULL) {
159 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
160 		return 0;
161 	}
162 
163 	error = xchk_trans_alloc_empty(sc);
164 	if (error)
165 		return error;
166 
167 	error = xchk_metapath_ilock_both(mpath);
168 	if (error)
169 		goto out_cancel;
170 
171 	/* Make sure the parent dir has a dirent pointing to this file. */
172 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
173 	trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
174 	if (error == -ENOENT) {
175 		/* No directory entry at all */
176 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
177 		error = 0;
178 		goto out_ilock;
179 	}
180 	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
181 		goto out_ilock;
182 	if (ino != sc->ip->i_ino) {
183 		/* Pointing to wrong inode */
184 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
185 	}
186 
187 out_ilock:
188 	xchk_metapath_iunlock(mpath);
189 out_cancel:
190 	xchk_trans_cancel(sc);
191 	return error;
192 }
193 
194 #ifdef CONFIG_XFS_ONLINE_REPAIR
195 /* Create the dirent represented by the final component of the path. */
196 STATIC int
197 xrep_metapath_link(
198 	struct xchk_metapath	*mpath)
199 {
200 	struct xfs_scrub	*sc = mpath->sc;
201 
202 	mpath->du.dp = mpath->dp;
203 	mpath->du.name = &mpath->xname;
204 	mpath->du.ip = sc->ip;
205 
206 	if (xfs_has_parent(sc->mp))
207 		mpath->du.ppargs = &mpath->link_ppargs;
208 	else
209 		mpath->du.ppargs = NULL;
210 
211 	trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino);
212 
213 	return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du);
214 }
215 
216 /* Remove the dirent at the final component of the path. */
217 STATIC int
218 xrep_metapath_unlink(
219 	struct xchk_metapath	*mpath,
220 	xfs_ino_t		ino,
221 	struct xfs_inode	*ip)
222 {
223 	struct xfs_parent_rec	rec;
224 	struct xfs_scrub	*sc = mpath->sc;
225 	struct xfs_mount	*mp = sc->mp;
226 	int			error;
227 
228 	trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino);
229 
230 	if (!ip) {
231 		/* The child inode isn't allocated.  Junk the dirent. */
232 		xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE);
233 		return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname,
234 				ino, mpath->unlink_resblks);
235 	}
236 
237 	mpath->du.dp = mpath->dp;
238 	mpath->du.name = &mpath->xname;
239 	mpath->du.ip = ip;
240 	mpath->du.ppargs = NULL;
241 
242 	/* Figure out if we're removing a parent pointer too. */
243 	if (xfs_has_parent(mp)) {
244 		xfs_inode_to_parent_rec(&rec, ip);
245 		error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
246 				&mpath->pptr_args);
247 		switch (error) {
248 		case -ENOATTR:
249 			break;
250 		case 0:
251 			mpath->du.ppargs = &mpath->unlink_ppargs;
252 			break;
253 		default:
254 			return error;
255 		}
256 	}
257 
258 	return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du);
259 }
260 
261 /*
262  * Try to create a dirent in @mpath->dp with the name @mpath->xname that points
263  * to @sc->ip.  Returns:
264  *
265  * -EEXIST and an @alleged_child if the dirent that points to the wrong inode;
266  * 0 if there is now a dirent pointing to @sc->ip; or
267  * A negative errno on error.
268  */
269 STATIC int
270 xrep_metapath_try_link(
271 	struct xchk_metapath	*mpath,
272 	xfs_ino_t		*alleged_child)
273 {
274 	struct xfs_scrub	*sc = mpath->sc;
275 	xfs_ino_t		ino;
276 	int			error;
277 
278 	/* Allocate transaction, lock inodes, join to transaction. */
279 	error = xchk_trans_alloc(sc, mpath->link_resblks);
280 	if (error)
281 		return error;
282 
283 	error = xchk_metapath_ilock_both(mpath);
284 	if (error) {
285 		xchk_trans_cancel(sc);
286 		return error;
287 	}
288 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
289 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
290 
291 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
292 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
293 	if (error == -ENOENT) {
294 		/*
295 		 * There is no dirent in the directory.  Create an entry
296 		 * pointing to @sc->ip.
297 		 */
298 		error = xrep_metapath_link(mpath);
299 		if (error)
300 			goto out_cancel;
301 
302 		error = xrep_trans_commit(sc);
303 		xchk_metapath_iunlock(mpath);
304 		return error;
305 	}
306 	if (error)
307 		goto out_cancel;
308 
309 	if (ino == sc->ip->i_ino) {
310 		/* The dirent already points to @sc->ip; we're done. */
311 		error = 0;
312 		goto out_cancel;
313 	}
314 
315 	/*
316 	 * The dirent points elsewhere; pass that back so that the caller
317 	 * can try to remove the dirent.
318 	 */
319 	*alleged_child = ino;
320 	error = -EEXIST;
321 
322 out_cancel:
323 	xchk_trans_cancel(sc);
324 	xchk_metapath_iunlock(mpath);
325 	return error;
326 }
327 
328 /*
329  * Take the ILOCK on the metadata directory parent and a bad child, if one is
330  * supplied.  We do not know that the metadata directory is not corrupt, so we
331  * lock the parent and try to lock the child.  Returns 0 if successful, or
332  * -EINTR to abort the repair.  The lock state of @dp is not recorded in @mpath.
333  */
334 STATIC int
335 xchk_metapath_ilock_parent_and_child(
336 	struct xchk_metapath	*mpath,
337 	struct xfs_inode	*ip)
338 {
339 	struct xfs_scrub	*sc = mpath->sc;
340 	int			error = 0;
341 
342 	while (true) {
343 		xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
344 		if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
345 			return 0;
346 		xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
347 
348 		if (xchk_should_terminate(sc, &error))
349 			return error;
350 
351 		delay(1);
352 	}
353 
354 	ASSERT(0);
355 	return -EINTR;
356 }
357 
358 /*
359  * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points
360  * to @alleged_child.  Returns:
361  *
362  * 0 if there is no longer a dirent;
363  * -EEXIST if the dirent points to @sc->ip;
364  * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or
365  * A negative errno for any other error.
366  */
367 STATIC int
368 xrep_metapath_try_unlink(
369 	struct xchk_metapath	*mpath,
370 	xfs_ino_t		*alleged_child)
371 {
372 	struct xfs_scrub	*sc = mpath->sc;
373 	struct xfs_inode	*ip = NULL;
374 	xfs_ino_t		ino;
375 	int			error;
376 
377 	ASSERT(*alleged_child != sc->ip->i_ino);
378 
379 	trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp,
380 			*alleged_child);
381 
382 	/*
383 	 * Allocate transaction, grab the alleged child inode, lock inodes,
384 	 * join to transaction.
385 	 */
386 	error = xchk_trans_alloc(sc, mpath->unlink_resblks);
387 	if (error)
388 		return error;
389 
390 	error = xchk_iget(sc, *alleged_child, &ip);
391 	if (error == -EINVAL || error == -ENOENT) {
392 		/* inode number is bogus, junk the dirent */
393 		error = 0;
394 	}
395 	if (error) {
396 		xchk_trans_cancel(sc);
397 		return error;
398 	}
399 
400 	error = xchk_metapath_ilock_parent_and_child(mpath, ip);
401 	if (error) {
402 		xchk_trans_cancel(sc);
403 		return error;
404 	}
405 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
406 	if (ip)
407 		xfs_trans_ijoin(sc->tp, ip, 0);
408 
409 	error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
410 	trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino);
411 	if (error == -ENOENT) {
412 		/*
413 		 * There is no dirent in the directory anymore.  We're ready to
414 		 * try the link operation again.
415 		 */
416 		error = 0;
417 		goto out_cancel;
418 	}
419 	if (error)
420 		goto out_cancel;
421 
422 	if (ino == sc->ip->i_ino) {
423 		/* The dirent already points to @sc->ip; we're done. */
424 		error = -EEXIST;
425 		goto out_cancel;
426 	}
427 
428 	/*
429 	 * The dirent does not point to the alleged child.  Update the caller
430 	 * and signal that we want to be called again.
431 	 */
432 	if (ino != *alleged_child) {
433 		*alleged_child = ino;
434 		error = -EAGAIN;
435 		goto out_cancel;
436 	}
437 
438 	/* Remove the link to the child. */
439 	error = xrep_metapath_unlink(mpath, ino, ip);
440 	if (error)
441 		goto out_cancel;
442 
443 	error = xrep_trans_commit(sc);
444 	goto out_unlock;
445 
446 out_cancel:
447 	xchk_trans_cancel(sc);
448 out_unlock:
449 	xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
450 	if (ip) {
451 		xfs_iunlock(ip, XFS_ILOCK_EXCL);
452 		xchk_irele(sc, ip);
453 	}
454 	return error;
455 }
456 
457 /*
458  * Make sure the metadata directory path points to the child being examined.
459  *
460  * Repair needs to be able to create a directory structure, create its own
461  * transactions, and take ILOCKs.  This function /must/ be called after all
462  * other repairs have completed.
463  */
464 int
465 xrep_metapath(
466 	struct xfs_scrub	*sc)
467 {
468 	struct xchk_metapath	*mpath = sc->buf;
469 	struct xfs_mount	*mp = sc->mp;
470 	int			error = 0;
471 
472 	/* Just probing, nothing to repair. */
473 	if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
474 		return 0;
475 
476 	/* Parent required to do anything else. */
477 	if (mpath->dp == NULL)
478 		return -EFSCORRUPTED;
479 
480 	/*
481 	 * Make sure the child file actually has an attr fork to receive a new
482 	 * parent pointer if the fs has parent pointers.
483 	 */
484 	if (xfs_has_parent(mp)) {
485 		error = xfs_attr_add_fork(sc->ip,
486 				sizeof(struct xfs_attr_sf_hdr), 1);
487 		if (error)
488 			return error;
489 	}
490 
491 	/* Compute block reservation required to unlink and link a file. */
492 	mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN);
493 	mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN);
494 
495 	do {
496 		xfs_ino_t	alleged_child;
497 
498 		/* Re-establish the link, or tell us which inode to remove. */
499 		error = xrep_metapath_try_link(mpath, &alleged_child);
500 		if (!error)
501 			return 0;
502 		if (error != -EEXIST)
503 			return error;
504 
505 		/*
506 		 * Remove an incorrect link to an alleged child, or tell us
507 		 * which inode to remove.
508 		 */
509 		do {
510 			error = xrep_metapath_try_unlink(mpath, &alleged_child);
511 		} while (error == -EAGAIN);
512 		if (error == -EEXIST) {
513 			/* Link established; we're done. */
514 			error = 0;
515 			break;
516 		}
517 	} while (!error);
518 
519 	return error;
520 }
521 #endif /* CONFIG_XFS_ONLINE_REPAIR */
522