1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2020-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_defer.h"
13#include "xfs_bit.h"
14#include "xfs_log_format.h"
15#include "xfs_trans.h"
16#include "xfs_sb.h"
17#include "xfs_inode.h"
18#include "xfs_icache.h"
19#include "xfs_da_format.h"
20#include "xfs_da_btree.h"
21#include "xfs_dir2.h"
22#include "xfs_bmap_btree.h"
23#include "xfs_dir2_priv.h"
24#include "xfs_trans_space.h"
25#include "xfs_health.h"
26#include "xfs_exchmaps.h"
27#include "xfs_parent.h"
28#include "scrub/xfs_scrub.h"
29#include "scrub/scrub.h"
30#include "scrub/common.h"
31#include "scrub/trace.h"
32#include "scrub/repair.h"
33#include "scrub/iscan.h"
34#include "scrub/findparent.h"
35#include "scrub/readdir.h"
36#include "scrub/tempfile.h"
37#include "scrub/listxattr.h"
38
39/*
40 * Finding the Parent of a Directory
41 * =================================
42 *
43 * Directories have parent pointers, in the sense that each directory contains
44 * a dotdot entry that points to the single allowed parent.  The brute force
45 * way to find the parent of a given directory is to scan every directory in
46 * the filesystem looking for a child dirent that references this directory.
47 *
48 * This module wraps the process of scanning the directory tree.  It requires
49 * that @sc->ip is the directory whose parent we want to find, and that the
50 * caller hold only the IOLOCK on that directory.  The scan itself needs to
51 * take the ILOCK of each directory visited.
52 *
53 * Because we cannot hold @sc->ip's ILOCK during a scan of the whole fs, it is
54 * necessary to use dirent hook to update the parent scan results.  Callers
55 * must not read the scan results without re-taking @sc->ip's ILOCK.
56 *
57 * There are a few shortcuts that we can take to avoid scanning the entire
58 * filesystem, such as noticing directory tree roots and querying the dentry
59 * cache for parent information.
60 */
61
62struct xrep_findparent_info {
63	/* The directory currently being scanned. */
64	struct xfs_inode	*dp;
65
66	/*
67	 * Scrub context.  We're looking for a @dp containing a directory
68	 * entry pointing to sc->ip->i_ino.
69	 */
70	struct xfs_scrub	*sc;
71
72	/* Optional scan information for a xrep_findparent_scan call. */
73	struct xrep_parent_scan_info *parent_scan;
74
75	/*
76	 * Parent that we've found for sc->ip.  If we're scanning the entire
77	 * directory tree, we need this to ensure that we only find /one/
78	 * parent directory.
79	 */
80	xfs_ino_t		found_parent;
81
82	/*
83	 * This is set to true if @found_parent was not observed directly from
84	 * the directory scan but by noticing a change in dotdot entries after
85	 * cycling the sc->ip IOLOCK.
86	 */
87	bool			parent_tentative;
88};
89
90/*
91 * If this directory entry points to the scrub target inode, then the directory
92 * we're scanning is the parent of the scrub target inode.
93 */
94STATIC int
95xrep_findparent_dirent(
96	struct xfs_scrub		*sc,
97	struct xfs_inode		*dp,
98	xfs_dir2_dataptr_t		dapos,
99	const struct xfs_name		*name,
100	xfs_ino_t			ino,
101	void				*priv)
102{
103	struct xrep_findparent_info	*fpi = priv;
104	int				error = 0;
105
106	if (xchk_should_terminate(fpi->sc, &error))
107		return error;
108
109	if (ino != fpi->sc->ip->i_ino)
110		return 0;
111
112	/* Ignore garbage directory entry names. */
113	if (name->len == 0 || !xfs_dir2_namecheck(name->name, name->len))
114		return -EFSCORRUPTED;
115
116	/*
117	 * Ignore dotdot and dot entries -- we're looking for parent -> child
118	 * links only.
119	 */
120	if (name->name[0] == '.' && (name->len == 1 ||
121				     (name->len == 2 && name->name[1] == '.')))
122		return 0;
123
124	/* Uhoh, more than one parent for a dir? */
125	if (fpi->found_parent != NULLFSINO &&
126	    !(fpi->parent_tentative && fpi->found_parent == fpi->dp->i_ino)) {
127		trace_xrep_findparent_dirent(fpi->sc->ip, 0);
128		return -EFSCORRUPTED;
129	}
130
131	/* We found a potential parent; remember this. */
132	trace_xrep_findparent_dirent(fpi->sc->ip, fpi->dp->i_ino);
133	fpi->found_parent = fpi->dp->i_ino;
134	fpi->parent_tentative = false;
135
136	if (fpi->parent_scan)
137		xrep_findparent_scan_found(fpi->parent_scan, fpi->dp->i_ino);
138
139	return 0;
140}
141
142/*
143 * If this is a directory, walk the dirents looking for any that point to the
144 * scrub target inode.
145 */
146STATIC int
147xrep_findparent_walk_directory(
148	struct xrep_findparent_info	*fpi)
149{
150	struct xfs_scrub		*sc = fpi->sc;
151	struct xfs_inode		*dp = fpi->dp;
152	unsigned int			lock_mode;
153	int				error = 0;
154
155	/*
156	 * The inode being scanned cannot be its own parent, nor can any
157	 * temporary directory we created to stage this repair.
158	 */
159	if (dp == sc->ip || dp == sc->tempip)
160		return 0;
161
162	/*
163	 * Similarly, temporary files created to stage a repair cannot be the
164	 * parent of this inode.
165	 */
166	if (xrep_is_tempfile(dp))
167		return 0;
168
169	/*
170	 * Scan the directory to see if there it contains an entry pointing to
171	 * the directory that we are repairing.
172	 */
173	lock_mode = xfs_ilock_data_map_shared(dp);
174
175	/*
176	 * If this directory is known to be sick, we cannot scan it reliably
177	 * and must abort.
178	 */
179	if (xfs_inode_has_sickness(dp, XFS_SICK_INO_CORE |
180				       XFS_SICK_INO_BMBTD |
181				       XFS_SICK_INO_DIR)) {
182		error = -EFSCORRUPTED;
183		goto out_unlock;
184	}
185
186	/*
187	 * We cannot complete our parent pointer scan if a directory looks as
188	 * though it has been zapped by the inode record repair code.
189	 */
190	if (xchk_dir_looks_zapped(dp)) {
191		error = -EBUSY;
192		goto out_unlock;
193	}
194
195	error = xchk_dir_walk(sc, dp, xrep_findparent_dirent, fpi);
196	if (error)
197		goto out_unlock;
198
199out_unlock:
200	xfs_iunlock(dp, lock_mode);
201	return error;
202}
203
204/*
205 * Update this directory's dotdot pointer based on ongoing dirent updates.
206 */
207STATIC int
208xrep_findparent_live_update(
209	struct notifier_block		*nb,
210	unsigned long			action,
211	void				*data)
212{
213	struct xfs_dir_update_params	*p = data;
214	struct xrep_parent_scan_info	*pscan;
215	struct xfs_scrub		*sc;
216
217	pscan = container_of(nb, struct xrep_parent_scan_info,
218			dhook.dirent_hook.nb);
219	sc = pscan->sc;
220
221	/*
222	 * If @p->ip is the subdirectory that we're interested in and we've
223	 * already scanned @p->dp, update the dotdot target inumber to the
224	 * parent inode.
225	 */
226	if (p->ip->i_ino == sc->ip->i_ino &&
227	    xchk_iscan_want_live_update(&pscan->iscan, p->dp->i_ino)) {
228		if (p->delta > 0) {
229			xrep_findparent_scan_found(pscan, p->dp->i_ino);
230		} else {
231			xrep_findparent_scan_found(pscan, NULLFSINO);
232		}
233	}
234
235	return NOTIFY_DONE;
236}
237
238/*
239 * Set up a scan to find the parent of a directory.  The provided dirent hook
240 * will be called when there is a dotdot update for the inode being repaired.
241 */
242int
243__xrep_findparent_scan_start(
244	struct xfs_scrub		*sc,
245	struct xrep_parent_scan_info	*pscan,
246	notifier_fn_t			custom_fn)
247{
248	int				error;
249
250	if (!(sc->flags & XCHK_FSGATES_DIRENTS)) {
251		ASSERT(sc->flags & XCHK_FSGATES_DIRENTS);
252		return -EINVAL;
253	}
254
255	pscan->sc = sc;
256	pscan->parent_ino = NULLFSINO;
257
258	mutex_init(&pscan->lock);
259
260	xchk_iscan_start(sc, 30000, 100, &pscan->iscan);
261
262	/*
263	 * Hook into the dirent update code.  The hook only operates on inodes
264	 * that were already scanned, and the scanner thread takes each inode's
265	 * ILOCK, which means that any in-progress inode updates will finish
266	 * before we can scan the inode.
267	 */
268	if (custom_fn)
269		xfs_dir_hook_setup(&pscan->dhook, custom_fn);
270	else
271		xfs_dir_hook_setup(&pscan->dhook, xrep_findparent_live_update);
272	error = xfs_dir_hook_add(sc->mp, &pscan->dhook);
273	if (error)
274		goto out_iscan;
275
276	return 0;
277out_iscan:
278	xchk_iscan_teardown(&pscan->iscan);
279	mutex_destroy(&pscan->lock);
280	return error;
281}
282
283/*
284 * Scan the entire filesystem looking for a parent inode for the inode being
285 * scrubbed.  @sc->ip must not be the root of a directory tree.  Callers must
286 * not hold a dirty transaction or any lock that would interfere with taking
287 * an ILOCK.
288 *
289 * Returns 0 with @pscan->parent_ino set to the parent that we found.
290 * Returns 0 with @pscan->parent_ino set to NULLFSINO if we found no parents.
291 * Returns the usual negative errno if something else happened.
292 */
293int
294xrep_findparent_scan(
295	struct xrep_parent_scan_info	*pscan)
296{
297	struct xrep_findparent_info	fpi = {
298		.sc			= pscan->sc,
299		.found_parent		= NULLFSINO,
300		.parent_scan		= pscan,
301	};
302	struct xfs_scrub		*sc = pscan->sc;
303	int				ret;
304
305	ASSERT(S_ISDIR(VFS_IC(sc->ip)->i_mode));
306
307	while ((ret = xchk_iscan_iter(&pscan->iscan, &fpi.dp)) == 1) {
308		if (S_ISDIR(VFS_I(fpi.dp)->i_mode))
309			ret = xrep_findparent_walk_directory(&fpi);
310		else
311			ret = 0;
312		xchk_iscan_mark_visited(&pscan->iscan, fpi.dp);
313		xchk_irele(sc, fpi.dp);
314		if (ret)
315			break;
316
317		if (xchk_should_terminate(sc, &ret))
318			break;
319	}
320	xchk_iscan_iter_finish(&pscan->iscan);
321
322	return ret;
323}
324
325/* Tear down a parent scan. */
326void
327xrep_findparent_scan_teardown(
328	struct xrep_parent_scan_info	*pscan)
329{
330	xfs_dir_hook_del(pscan->sc->mp, &pscan->dhook);
331	xchk_iscan_teardown(&pscan->iscan);
332	mutex_destroy(&pscan->lock);
333}
334
335/* Finish a parent scan early. */
336void
337xrep_findparent_scan_finish_early(
338	struct xrep_parent_scan_info	*pscan,
339	xfs_ino_t			ino)
340{
341	xrep_findparent_scan_found(pscan, ino);
342	xchk_iscan_finish_early(&pscan->iscan);
343}
344
345/*
346 * Confirm that the directory @parent_ino actually contains a directory entry
347 * pointing to the child @sc->ip->ino.  This function returns one of several
348 * ways:
349 *
350 * Returns 0 with @parent_ino unchanged if the parent was confirmed.
351 * Returns 0 with @parent_ino set to NULLFSINO if the parent was not valid.
352 * Returns the usual negative errno if something else happened.
353 */
354int
355xrep_findparent_confirm(
356	struct xfs_scrub	*sc,
357	xfs_ino_t		*parent_ino)
358{
359	struct xrep_findparent_info fpi = {
360		.sc		= sc,
361		.found_parent	= NULLFSINO,
362	};
363	int			error;
364
365	/*
366	 * The root directory always points to itself.  Unlinked dirs can point
367	 * anywhere, so we point them at the root dir too.
368	 */
369	if (sc->ip == sc->mp->m_rootip || VFS_I(sc->ip)->i_nlink == 0) {
370		*parent_ino = sc->mp->m_sb.sb_rootino;
371		return 0;
372	}
373
374	/* Reject garbage parent inode numbers and self-referential parents. */
375	if (*parent_ino == NULLFSINO)
376	       return 0;
377	if (!xfs_verify_dir_ino(sc->mp, *parent_ino) ||
378	    *parent_ino == sc->ip->i_ino) {
379		*parent_ino = NULLFSINO;
380		return 0;
381	}
382
383	error = xchk_iget(sc, *parent_ino, &fpi.dp);
384	if (error)
385		return error;
386
387	if (!S_ISDIR(VFS_I(fpi.dp)->i_mode)) {
388		*parent_ino = NULLFSINO;
389		goto out_rele;
390	}
391
392	error = xrep_findparent_walk_directory(&fpi);
393	if (error)
394		goto out_rele;
395
396	*parent_ino = fpi.found_parent;
397out_rele:
398	xchk_irele(sc, fpi.dp);
399	return error;
400}
401
402/*
403 * If we're the root of a directory tree, we are our own parent.  If we're an
404 * unlinked directory, the parent /won't/ have a link to us.  Set the parent
405 * directory to the root for both cases.  Returns NULLFSINO if we don't know
406 * what to do.
407 */
408xfs_ino_t
409xrep_findparent_self_reference(
410	struct xfs_scrub	*sc)
411{
412	if (sc->ip->i_ino == sc->mp->m_sb.sb_rootino)
413		return sc->mp->m_sb.sb_rootino;
414
415	if (VFS_I(sc->ip)->i_nlink == 0)
416		return sc->mp->m_sb.sb_rootino;
417
418	return NULLFSINO;
419}
420
421/* Check the dentry cache to see if knows of a parent for the scrub target. */
422xfs_ino_t
423xrep_findparent_from_dcache(
424	struct xfs_scrub	*sc)
425{
426	struct inode		*pip = NULL;
427	struct dentry		*dentry, *parent;
428	xfs_ino_t		ret = NULLFSINO;
429
430	dentry = d_find_alias(VFS_I(sc->ip));
431	if (!dentry)
432		goto out;
433
434	parent = dget_parent(dentry);
435	if (!parent)
436		goto out_dput;
437
438	ASSERT(parent->d_sb == sc->ip->i_mount->m_super);
439
440	pip = igrab(d_inode(parent));
441	dput(parent);
442
443	if (S_ISDIR(pip->i_mode)) {
444		trace_xrep_findparent_from_dcache(sc->ip, XFS_I(pip)->i_ino);
445		ret = XFS_I(pip)->i_ino;
446	}
447
448	xchk_irele(sc, XFS_I(pip));
449
450out_dput:
451	dput(dentry);
452out:
453	return ret;
454}
455