xref: /linux/fs/xfs/scrub/parent_repair.c (revision 1e58a8ccf2597c9259a8e71a2bffac5e11e12ea0)
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 "scrub/xfs_scrub.h"
28 #include "scrub/scrub.h"
29 #include "scrub/common.h"
30 #include "scrub/trace.h"
31 #include "scrub/repair.h"
32 #include "scrub/iscan.h"
33 #include "scrub/findparent.h"
34 #include "scrub/readdir.h"
35 #include "scrub/tempfile.h"
36 #include "scrub/orphanage.h"
37 
38 /*
39  * Repairing The Directory Parent Pointer
40  * ======================================
41  *
42  * Currently, only directories support parent pointers (in the form of '..'
43  * entries), so we simply scan the filesystem and update the '..' entry.
44  *
45  * Note that because the only parent pointer is the dotdot entry, we won't
46  * touch an unhealthy directory, since the directory repair code is perfectly
47  * capable of rebuilding a directory with the proper parent inode.
48  *
49  * See the section on locking issues in dir_repair.c for more information about
50  * conflicts with the VFS.  The findparent code wll keep our incore parent
51  * inode up to date.
52  */
53 
54 struct xrep_parent {
55 	struct xfs_scrub	*sc;
56 
57 	/*
58 	 * Information used to scan the filesystem to find the inumber of the
59 	 * dotdot entry for this directory.
60 	 */
61 	struct xrep_parent_scan_info pscan;
62 
63 	/* Orphanage reparenting request. */
64 	struct xrep_adoption	adoption;
65 
66 	/* Directory entry name, plus the trailing null. */
67 	struct xfs_name		xname;
68 	unsigned char		namebuf[MAXNAMELEN];
69 };
70 
71 /* Tear down all the incore stuff we created. */
72 static void
73 xrep_parent_teardown(
74 	struct xrep_parent	*rp)
75 {
76 	xrep_findparent_scan_teardown(&rp->pscan);
77 }
78 
79 /* Set up for a parent repair. */
80 int
81 xrep_setup_parent(
82 	struct xfs_scrub	*sc)
83 {
84 	struct xrep_parent	*rp;
85 
86 	xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
87 
88 	rp = kvzalloc(sizeof(struct xrep_parent), XCHK_GFP_FLAGS);
89 	if (!rp)
90 		return -ENOMEM;
91 	rp->sc = sc;
92 	rp->xname.name = rp->namebuf;
93 	sc->buf = rp;
94 
95 	return xrep_orphanage_try_create(sc);
96 }
97 
98 /*
99  * Scan all files in the filesystem for a child dirent that we can turn into
100  * the dotdot entry for this directory.
101  */
102 STATIC int
103 xrep_parent_find_dotdot(
104 	struct xrep_parent	*rp)
105 {
106 	struct xfs_scrub	*sc = rp->sc;
107 	xfs_ino_t		ino;
108 	unsigned int		sick, checked;
109 	int			error;
110 
111 	/*
112 	 * Avoid sick directories.  There shouldn't be anyone else clearing the
113 	 * directory's sick status.
114 	 */
115 	xfs_inode_measure_sickness(sc->ip, &sick, &checked);
116 	if (sick & XFS_SICK_INO_DIR)
117 		return -EFSCORRUPTED;
118 
119 	ino = xrep_findparent_self_reference(sc);
120 	if (ino != NULLFSINO) {
121 		xrep_findparent_scan_finish_early(&rp->pscan, ino);
122 		return 0;
123 	}
124 
125 	/*
126 	 * Drop the ILOCK on this directory so that we can scan for the dotdot
127 	 * entry.  Figure out who is going to be the parent of this directory,
128 	 * then retake the ILOCK so that we can salvage directory entries.
129 	 */
130 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
131 
132 	/* Does the VFS dcache have an answer for us? */
133 	ino = xrep_findparent_from_dcache(sc);
134 	if (ino != NULLFSINO) {
135 		error = xrep_findparent_confirm(sc, &ino);
136 		if (!error && ino != NULLFSINO) {
137 			xrep_findparent_scan_finish_early(&rp->pscan, ino);
138 			goto out_relock;
139 		}
140 	}
141 
142 	/* Scan the entire filesystem for a parent. */
143 	error = xrep_findparent_scan(&rp->pscan);
144 out_relock:
145 	xchk_ilock(sc, XFS_ILOCK_EXCL);
146 
147 	return error;
148 }
149 
150 /* Reset a directory's dotdot entry, if needed. */
151 STATIC int
152 xrep_parent_reset_dotdot(
153 	struct xrep_parent	*rp)
154 {
155 	struct xfs_scrub	*sc = rp->sc;
156 	xfs_ino_t		ino;
157 	unsigned int		spaceres;
158 	int			error = 0;
159 
160 	ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
161 
162 	error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &ino);
163 	if (error || ino == rp->pscan.parent_ino)
164 		return error;
165 
166 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
167 
168 	trace_xrep_parent_reset_dotdot(sc->ip, rp->pscan.parent_ino);
169 
170 	/*
171 	 * Reserve more space just in case we have to expand the dir.  We're
172 	 * allowed to exceed quota to repair inconsistent metadata.
173 	 */
174 	spaceres = XFS_RENAME_SPACE_RES(sc->mp, xfs_name_dotdot.len);
175 	error = xfs_trans_reserve_more_inode(sc->tp, sc->ip, spaceres, 0,
176 			true);
177 	if (error)
178 		return error;
179 
180 	error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,
181 			rp->pscan.parent_ino, spaceres);
182 	if (error)
183 		return error;
184 
185 	/*
186 	 * Roll transaction to detach the inode from the transaction but retain
187 	 * ILOCK_EXCL.
188 	 */
189 	return xfs_trans_roll(&sc->tp);
190 }
191 
192 /*
193  * Move the current file to the orphanage.
194  *
195  * Caller must hold IOLOCK_EXCL on @sc->ip, and no other inode locks.  Upon
196  * successful return, the scrub transaction will have enough extra reservation
197  * to make the move; it will hold IOLOCK_EXCL and ILOCK_EXCL of @sc->ip and the
198  * orphanage; and both inodes will be ijoined.
199  */
200 STATIC int
201 xrep_parent_move_to_orphanage(
202 	struct xrep_parent	*rp)
203 {
204 	struct xfs_scrub	*sc = rp->sc;
205 	xfs_ino_t		orig_parent, new_parent;
206 	int			error;
207 
208 	/*
209 	 * We are about to drop the ILOCK on sc->ip to lock the orphanage and
210 	 * prepare for the adoption.  Therefore, look up the old dotdot entry
211 	 * for sc->ip so that we can compare it after we re-lock sc->ip.
212 	 */
213 	error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &orig_parent);
214 	if (error)
215 		return error;
216 
217 	/*
218 	 * Drop the ILOCK on the scrub target and commit the transaction.
219 	 * Adoption computes its own resource requirements and gathers the
220 	 * necessary components.
221 	 */
222 	error = xrep_trans_commit(sc);
223 	if (error)
224 		return error;
225 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
226 
227 	/* If we can take the orphanage's iolock then we're ready to move. */
228 	if (!xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) {
229 		xchk_iunlock(sc, sc->ilock_flags);
230 		error = xrep_orphanage_iolock_two(sc);
231 		if (error)
232 			return error;
233 	}
234 
235 	/* Grab transaction and ILOCK the two files. */
236 	error = xrep_adoption_trans_alloc(sc, &rp->adoption);
237 	if (error)
238 		return error;
239 
240 	error = xrep_adoption_compute_name(&rp->adoption, &rp->xname);
241 	if (error)
242 		return error;
243 
244 	/*
245 	 * Now that we've reacquired the ILOCK on sc->ip, look up the dotdot
246 	 * entry again.  If the parent changed or the child was unlinked while
247 	 * the child directory was unlocked, we don't need to move the child to
248 	 * the orphanage after all.
249 	 */
250 	error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &new_parent);
251 	if (error)
252 		return error;
253 
254 	/*
255 	 * Attach to the orphanage if we still have a linked directory and it
256 	 * hasn't been moved.
257 	 */
258 	if (orig_parent == new_parent && VFS_I(sc->ip)->i_nlink > 0) {
259 		error = xrep_adoption_move(&rp->adoption);
260 		if (error)
261 			return error;
262 	}
263 
264 	/*
265 	 * Launder the scrub transaction so we can drop the orphanage ILOCK
266 	 * and IOLOCK.  Return holding the scrub target's ILOCK and IOLOCK.
267 	 */
268 	error = xrep_adoption_trans_roll(&rp->adoption);
269 	if (error)
270 		return error;
271 
272 	xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
273 	xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
274 	return 0;
275 }
276 
277 /*
278  * Commit the new parent pointer structure (currently only the dotdot entry) to
279  * the file that we're repairing.
280  */
281 STATIC int
282 xrep_parent_rebuild_tree(
283 	struct xrep_parent	*rp)
284 {
285 	if (rp->pscan.parent_ino == NULLFSINO) {
286 		if (xrep_orphanage_can_adopt(rp->sc))
287 			return xrep_parent_move_to_orphanage(rp);
288 		return -EFSCORRUPTED;
289 	}
290 
291 	return xrep_parent_reset_dotdot(rp);
292 }
293 
294 /* Set up the filesystem scan so we can look for parents. */
295 STATIC int
296 xrep_parent_setup_scan(
297 	struct xrep_parent	*rp)
298 {
299 	struct xfs_scrub	*sc = rp->sc;
300 
301 	return xrep_findparent_scan_start(sc, &rp->pscan);
302 }
303 
304 int
305 xrep_parent(
306 	struct xfs_scrub	*sc)
307 {
308 	struct xrep_parent	*rp = sc->buf;
309 	int			error;
310 
311 	error = xrep_parent_setup_scan(rp);
312 	if (error)
313 		return error;
314 
315 	error = xrep_parent_find_dotdot(rp);
316 	if (error)
317 		goto out_teardown;
318 
319 	/* Last chance to abort before we start committing fixes. */
320 	if (xchk_should_terminate(sc, &error))
321 		goto out_teardown;
322 
323 	error = xrep_parent_rebuild_tree(rp);
324 	if (error)
325 		goto out_teardown;
326 
327 out_teardown:
328 	xrep_parent_teardown(rp);
329 	return error;
330 }
331