xref: /linux/fs/xfs/scrub/parent_repair.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1cc22edabSDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
2cc22edabSDarrick J. Wong /*
3cc22edabSDarrick J. Wong  * Copyright (c) 2020-2024 Oracle.  All Rights Reserved.
4cc22edabSDarrick J. Wong  * Author: Darrick J. Wong <djwong@kernel.org>
5cc22edabSDarrick J. Wong  */
6cc22edabSDarrick J. Wong #include "xfs.h"
7cc22edabSDarrick J. Wong #include "xfs_fs.h"
8cc22edabSDarrick J. Wong #include "xfs_shared.h"
9cc22edabSDarrick J. Wong #include "xfs_format.h"
10cc22edabSDarrick J. Wong #include "xfs_trans_resv.h"
11cc22edabSDarrick J. Wong #include "xfs_mount.h"
12cc22edabSDarrick J. Wong #include "xfs_defer.h"
13cc22edabSDarrick J. Wong #include "xfs_bit.h"
14cc22edabSDarrick J. Wong #include "xfs_log_format.h"
15cc22edabSDarrick J. Wong #include "xfs_trans.h"
16cc22edabSDarrick J. Wong #include "xfs_sb.h"
17cc22edabSDarrick J. Wong #include "xfs_inode.h"
18cc22edabSDarrick J. Wong #include "xfs_icache.h"
19cc22edabSDarrick J. Wong #include "xfs_da_format.h"
20cc22edabSDarrick J. Wong #include "xfs_da_btree.h"
21cc22edabSDarrick J. Wong #include "xfs_dir2.h"
22cc22edabSDarrick J. Wong #include "xfs_bmap_btree.h"
23cc22edabSDarrick J. Wong #include "xfs_dir2_priv.h"
24cc22edabSDarrick J. Wong #include "xfs_trans_space.h"
25cc22edabSDarrick J. Wong #include "xfs_health.h"
26cc22edabSDarrick J. Wong #include "xfs_exchmaps.h"
27b334f7faSDarrick J. Wong #include "xfs_parent.h"
28a26dc213SDarrick J. Wong #include "xfs_attr.h"
29a26dc213SDarrick J. Wong #include "xfs_bmap.h"
303f50ddbfSDarrick J. Wong #include "xfs_ag.h"
31cc22edabSDarrick J. Wong #include "scrub/xfs_scrub.h"
32cc22edabSDarrick J. Wong #include "scrub/scrub.h"
33cc22edabSDarrick J. Wong #include "scrub/common.h"
34cc22edabSDarrick J. Wong #include "scrub/trace.h"
35cc22edabSDarrick J. Wong #include "scrub/repair.h"
36cc22edabSDarrick J. Wong #include "scrub/iscan.h"
37cc22edabSDarrick J. Wong #include "scrub/findparent.h"
38cc22edabSDarrick J. Wong #include "scrub/readdir.h"
391e58a8ccSDarrick J. Wong #include "scrub/tempfile.h"
40a26dc213SDarrick J. Wong #include "scrub/tempexch.h"
411e58a8ccSDarrick J. Wong #include "scrub/orphanage.h"
42b334f7faSDarrick J. Wong #include "scrub/xfile.h"
43b334f7faSDarrick J. Wong #include "scrub/xfarray.h"
44b334f7faSDarrick J. Wong #include "scrub/xfblob.h"
45a26dc213SDarrick J. Wong #include "scrub/attr_repair.h"
46a26dc213SDarrick J. Wong #include "scrub/listxattr.h"
47cc22edabSDarrick J. Wong 
48cc22edabSDarrick J. Wong /*
49cc22edabSDarrick J. Wong  * Repairing The Directory Parent Pointer
50cc22edabSDarrick J. Wong  * ======================================
51cc22edabSDarrick J. Wong  *
52cc22edabSDarrick J. Wong  * Currently, only directories support parent pointers (in the form of '..'
53cc22edabSDarrick J. Wong  * entries), so we simply scan the filesystem and update the '..' entry.
54cc22edabSDarrick J. Wong  *
55cc22edabSDarrick J. Wong  * Note that because the only parent pointer is the dotdot entry, we won't
56cc22edabSDarrick J. Wong  * touch an unhealthy directory, since the directory repair code is perfectly
57cc22edabSDarrick J. Wong  * capable of rebuilding a directory with the proper parent inode.
58cc22edabSDarrick J. Wong  *
59cc22edabSDarrick J. Wong  * See the section on locking issues in dir_repair.c for more information about
60cc22edabSDarrick J. Wong  * conflicts with the VFS.  The findparent code wll keep our incore parent
61cc22edabSDarrick J. Wong  * inode up to date.
62b334f7faSDarrick J. Wong  *
63b334f7faSDarrick J. Wong  * If parent pointers are enabled, we instead reconstruct the parent pointer
64b334f7faSDarrick J. Wong  * information by visiting every directory entry of every directory in the
65b334f7faSDarrick J. Wong  * system and translating the relevant dirents into parent pointers.  In this
66b334f7faSDarrick J. Wong  * case, it is advantageous to stash all parent pointers created from dirents
67b334f7faSDarrick J. Wong  * from a single parent file before replaying them into the temporary file.  To
68b334f7faSDarrick J. Wong  * save memory, the live filesystem scan reuses the findparent object.  Parent
69b334f7faSDarrick J. Wong  * pointer repair chooses either directory scanning or findparent, but not
70b334f7faSDarrick J. Wong  * both.
71b334f7faSDarrick J. Wong  *
72b334f7faSDarrick J. Wong  * When salvaging completes, the remaining stashed entries are replayed to the
73b334f7faSDarrick J. Wong  * temporary file.  All non-parent pointer extended attributes are copied to
74a26dc213SDarrick J. Wong  * the temporary file's extended attributes.  An atomic file mapping exchange
75a26dc213SDarrick J. Wong  * is used to commit the new xattr blocks to the file being repaired.  This
76a26dc213SDarrick J. Wong  * will disrupt attrmulti cursors.
77cc22edabSDarrick J. Wong  */
78cc22edabSDarrick J. Wong 
7965a1fb7aSDarrick J. Wong /* Create a parent pointer in the tempfile. */
8065a1fb7aSDarrick J. Wong #define XREP_PPTR_ADD		(1)
8165a1fb7aSDarrick J. Wong 
8265a1fb7aSDarrick J. Wong /* Remove a parent pointer from the tempfile. */
8365a1fb7aSDarrick J. Wong #define XREP_PPTR_REMOVE	(2)
8465a1fb7aSDarrick J. Wong 
85b334f7faSDarrick J. Wong /* A stashed parent pointer update. */
86b334f7faSDarrick J. Wong struct xrep_pptr {
87b334f7faSDarrick J. Wong 	/* Cookie for retrieval of the pptr name. */
88b334f7faSDarrick J. Wong 	xfblob_cookie		name_cookie;
89b334f7faSDarrick J. Wong 
90b334f7faSDarrick J. Wong 	/* Parent pointer record. */
91b334f7faSDarrick J. Wong 	struct xfs_parent_rec	pptr_rec;
92b334f7faSDarrick J. Wong 
93b334f7faSDarrick J. Wong 	/* Length of the pptr name. */
94b334f7faSDarrick J. Wong 	uint8_t			namelen;
9565a1fb7aSDarrick J. Wong 
9665a1fb7aSDarrick J. Wong 	/* XREP_PPTR_{ADD,REMOVE} */
9765a1fb7aSDarrick J. Wong 	uint8_t			action;
98b334f7faSDarrick J. Wong };
99b334f7faSDarrick J. Wong 
100b334f7faSDarrick J. Wong /*
101b334f7faSDarrick J. Wong  * Stash up to 8 pages of recovered parent pointers in pptr_recs and
102b334f7faSDarrick J. Wong  * pptr_names before we write them to the temp file.
103b334f7faSDarrick J. Wong  */
104b334f7faSDarrick J. Wong #define XREP_PARENT_MAX_STASH_BYTES	(PAGE_SIZE * 8)
105b334f7faSDarrick J. Wong 
106cc22edabSDarrick J. Wong struct xrep_parent {
107cc22edabSDarrick J. Wong 	struct xfs_scrub	*sc;
108cc22edabSDarrick J. Wong 
109b334f7faSDarrick J. Wong 	/* Fixed-size array of xrep_pptr structures. */
110b334f7faSDarrick J. Wong 	struct xfarray		*pptr_recs;
111b334f7faSDarrick J. Wong 
112b334f7faSDarrick J. Wong 	/* Blobs containing parent pointer names. */
113b334f7faSDarrick J. Wong 	struct xfblob		*pptr_names;
114b334f7faSDarrick J. Wong 
115a26dc213SDarrick J. Wong 	/* xattr keys */
116a26dc213SDarrick J. Wong 	struct xfarray		*xattr_records;
117a26dc213SDarrick J. Wong 
118a26dc213SDarrick J. Wong 	/* xattr values */
119a26dc213SDarrick J. Wong 	struct xfblob		*xattr_blobs;
120a26dc213SDarrick J. Wong 
121a26dc213SDarrick J. Wong 	/* Scratch buffers for saving extended attributes */
122a26dc213SDarrick J. Wong 	unsigned char		*xattr_name;
123a26dc213SDarrick J. Wong 	void			*xattr_value;
124a26dc213SDarrick J. Wong 	unsigned int		xattr_value_sz;
125a26dc213SDarrick J. Wong 
126a26dc213SDarrick J. Wong 	/*
127a26dc213SDarrick J. Wong 	 * Information used to exchange the attr fork mappings, if the fs
128a26dc213SDarrick J. Wong 	 * supports parent pointers.
129a26dc213SDarrick J. Wong 	 */
130a26dc213SDarrick J. Wong 	struct xrep_tempexch	tx;
131a26dc213SDarrick J. Wong 
132cc22edabSDarrick J. Wong 	/*
133cc22edabSDarrick J. Wong 	 * Information used to scan the filesystem to find the inumber of the
134b334f7faSDarrick J. Wong 	 * dotdot entry for this directory.  On filesystems without parent
135b334f7faSDarrick J. Wong 	 * pointers, we use the findparent_* functions on this object and
136b334f7faSDarrick J. Wong 	 * access only the parent_ino field directly.
137b334f7faSDarrick J. Wong 	 *
138b334f7faSDarrick J. Wong 	 * When parent pointers are enabled, the directory entry scanner uses
139b334f7faSDarrick J. Wong 	 * the iscan, hooks, and lock fields of this object directly.
140b334f7faSDarrick J. Wong 	 * @pscan.lock coordinates access to pptr_recs, pptr_names, pptr, and
141b334f7faSDarrick J. Wong 	 * pptr_scratch.  This reduces the memory requirements of this
142b334f7faSDarrick J. Wong 	 * structure.
143a26dc213SDarrick J. Wong 	 *
144a26dc213SDarrick J. Wong 	 * The lock also controls access to xattr_records and xattr_blobs(?)
145cc22edabSDarrick J. Wong 	 */
146cc22edabSDarrick J. Wong 	struct xrep_parent_scan_info pscan;
1471e58a8ccSDarrick J. Wong 
1481e58a8ccSDarrick J. Wong 	/* Orphanage reparenting request. */
1491e58a8ccSDarrick J. Wong 	struct xrep_adoption	adoption;
1501e58a8ccSDarrick J. Wong 
1511e58a8ccSDarrick J. Wong 	/* Directory entry name, plus the trailing null. */
1521e58a8ccSDarrick J. Wong 	struct xfs_name		xname;
1531e58a8ccSDarrick J. Wong 	unsigned char		namebuf[MAXNAMELEN];
154b334f7faSDarrick J. Wong 
155b334f7faSDarrick J. Wong 	/* Scratch buffer for scanning pptr xattrs */
156b334f7faSDarrick J. Wong 	struct xfs_da_args	pptr_args;
157a26dc213SDarrick J. Wong 
158a26dc213SDarrick J. Wong 	/* Have we seen any live updates of parent pointers recently? */
159a26dc213SDarrick J. Wong 	bool			saw_pptr_updates;
1603f50ddbfSDarrick J. Wong 
1613f50ddbfSDarrick J. Wong 	/* Number of parents we found after all other repairs */
1623f50ddbfSDarrick J. Wong 	unsigned long long	parents;
163cc22edabSDarrick J. Wong };
164cc22edabSDarrick J. Wong 
165a26dc213SDarrick J. Wong struct xrep_parent_xattr {
166a26dc213SDarrick J. Wong 	/* Cookie for retrieval of the xattr name. */
167a26dc213SDarrick J. Wong 	xfblob_cookie		name_cookie;
168a26dc213SDarrick J. Wong 
169a26dc213SDarrick J. Wong 	/* Cookie for retrieval of the xattr value. */
170a26dc213SDarrick J. Wong 	xfblob_cookie		value_cookie;
171a26dc213SDarrick J. Wong 
172a26dc213SDarrick J. Wong 	/* XFS_ATTR_* flags */
173a26dc213SDarrick J. Wong 	int			flags;
174a26dc213SDarrick J. Wong 
175a26dc213SDarrick J. Wong 	/* Length of the value and name. */
176a26dc213SDarrick J. Wong 	uint32_t		valuelen;
177a26dc213SDarrick J. Wong 	uint16_t		namelen;
178a26dc213SDarrick J. Wong };
179a26dc213SDarrick J. Wong 
180a26dc213SDarrick J. Wong /*
181a26dc213SDarrick J. Wong  * Stash up to 8 pages of attrs in xattr_records/xattr_blobs before we write
182a26dc213SDarrick J. Wong  * them to the temp file.
183a26dc213SDarrick J. Wong  */
184a26dc213SDarrick J. Wong #define XREP_PARENT_XATTR_MAX_STASH_BYTES	(PAGE_SIZE * 8)
185a26dc213SDarrick J. Wong 
186cc22edabSDarrick J. Wong /* Tear down all the incore stuff we created. */
187cc22edabSDarrick J. Wong static void
188cc22edabSDarrick J. Wong xrep_parent_teardown(
189cc22edabSDarrick J. Wong 	struct xrep_parent	*rp)
190cc22edabSDarrick J. Wong {
191cc22edabSDarrick J. Wong 	xrep_findparent_scan_teardown(&rp->pscan);
192a26dc213SDarrick J. Wong 	kvfree(rp->xattr_name);
193a26dc213SDarrick J. Wong 	rp->xattr_name = NULL;
194a26dc213SDarrick J. Wong 	kvfree(rp->xattr_value);
195a26dc213SDarrick J. Wong 	rp->xattr_value = NULL;
196a26dc213SDarrick J. Wong 	if (rp->xattr_blobs)
197a26dc213SDarrick J. Wong 		xfblob_destroy(rp->xattr_blobs);
198a26dc213SDarrick J. Wong 	rp->xattr_blobs = NULL;
199a26dc213SDarrick J. Wong 	if (rp->xattr_records)
200a26dc213SDarrick J. Wong 		xfarray_destroy(rp->xattr_records);
201a26dc213SDarrick J. Wong 	rp->xattr_records = NULL;
202b334f7faSDarrick J. Wong 	if (rp->pptr_names)
203b334f7faSDarrick J. Wong 		xfblob_destroy(rp->pptr_names);
204b334f7faSDarrick J. Wong 	rp->pptr_names = NULL;
205b334f7faSDarrick J. Wong 	if (rp->pptr_recs)
206b334f7faSDarrick J. Wong 		xfarray_destroy(rp->pptr_recs);
207b334f7faSDarrick J. Wong 	rp->pptr_recs = NULL;
208cc22edabSDarrick J. Wong }
209cc22edabSDarrick J. Wong 
210cc22edabSDarrick J. Wong /* Set up for a parent repair. */
211cc22edabSDarrick J. Wong int
212cc22edabSDarrick J. Wong xrep_setup_parent(
213cc22edabSDarrick J. Wong 	struct xfs_scrub	*sc)
214cc22edabSDarrick J. Wong {
215cc22edabSDarrick J. Wong 	struct xrep_parent	*rp;
216b334f7faSDarrick J. Wong 	int			error;
217cc22edabSDarrick J. Wong 
218cc22edabSDarrick J. Wong 	xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
219cc22edabSDarrick J. Wong 
220cc22edabSDarrick J. Wong 	rp = kvzalloc(sizeof(struct xrep_parent), XCHK_GFP_FLAGS);
221cc22edabSDarrick J. Wong 	if (!rp)
222cc22edabSDarrick J. Wong 		return -ENOMEM;
223cc22edabSDarrick J. Wong 	rp->sc = sc;
2241e58a8ccSDarrick J. Wong 	rp->xname.name = rp->namebuf;
225cc22edabSDarrick J. Wong 	sc->buf = rp;
226cc22edabSDarrick J. Wong 
227b334f7faSDarrick J. Wong 	error = xrep_tempfile_create(sc, S_IFREG);
228b334f7faSDarrick J. Wong 	if (error)
229b334f7faSDarrick J. Wong 		return error;
230b334f7faSDarrick J. Wong 
2311e58a8ccSDarrick J. Wong 	return xrep_orphanage_try_create(sc);
232cc22edabSDarrick J. Wong }
233cc22edabSDarrick J. Wong 
234cc22edabSDarrick J. Wong /*
235cc22edabSDarrick J. Wong  * Scan all files in the filesystem for a child dirent that we can turn into
236cc22edabSDarrick J. Wong  * the dotdot entry for this directory.
237cc22edabSDarrick J. Wong  */
238cc22edabSDarrick J. Wong STATIC int
239cc22edabSDarrick J. Wong xrep_parent_find_dotdot(
240cc22edabSDarrick J. Wong 	struct xrep_parent	*rp)
241cc22edabSDarrick J. Wong {
242cc22edabSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
243cc22edabSDarrick J. Wong 	xfs_ino_t		ino;
244cc22edabSDarrick J. Wong 	unsigned int		sick, checked;
245cc22edabSDarrick J. Wong 	int			error;
246cc22edabSDarrick J. Wong 
247cc22edabSDarrick J. Wong 	/*
248cc22edabSDarrick J. Wong 	 * Avoid sick directories.  There shouldn't be anyone else clearing the
249cc22edabSDarrick J. Wong 	 * directory's sick status.
250cc22edabSDarrick J. Wong 	 */
251cc22edabSDarrick J. Wong 	xfs_inode_measure_sickness(sc->ip, &sick, &checked);
252cc22edabSDarrick J. Wong 	if (sick & XFS_SICK_INO_DIR)
253cc22edabSDarrick J. Wong 		return -EFSCORRUPTED;
254cc22edabSDarrick J. Wong 
255cc22edabSDarrick J. Wong 	ino = xrep_findparent_self_reference(sc);
256cc22edabSDarrick J. Wong 	if (ino != NULLFSINO) {
257cc22edabSDarrick J. Wong 		xrep_findparent_scan_finish_early(&rp->pscan, ino);
258cc22edabSDarrick J. Wong 		return 0;
259cc22edabSDarrick J. Wong 	}
260cc22edabSDarrick J. Wong 
261cc22edabSDarrick J. Wong 	/*
262cc22edabSDarrick J. Wong 	 * Drop the ILOCK on this directory so that we can scan for the dotdot
263cc22edabSDarrick J. Wong 	 * entry.  Figure out who is going to be the parent of this directory,
264cc22edabSDarrick J. Wong 	 * then retake the ILOCK so that we can salvage directory entries.
265cc22edabSDarrick J. Wong 	 */
266cc22edabSDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
26734c9382cSDarrick J. Wong 
26834c9382cSDarrick J. Wong 	/* Does the VFS dcache have an answer for us? */
26934c9382cSDarrick J. Wong 	ino = xrep_findparent_from_dcache(sc);
27034c9382cSDarrick J. Wong 	if (ino != NULLFSINO) {
27134c9382cSDarrick J. Wong 		error = xrep_findparent_confirm(sc, &ino);
27234c9382cSDarrick J. Wong 		if (!error && ino != NULLFSINO) {
27334c9382cSDarrick J. Wong 			xrep_findparent_scan_finish_early(&rp->pscan, ino);
27434c9382cSDarrick J. Wong 			goto out_relock;
27534c9382cSDarrick J. Wong 		}
27634c9382cSDarrick J. Wong 	}
27734c9382cSDarrick J. Wong 
27834c9382cSDarrick J. Wong 	/* Scan the entire filesystem for a parent. */
279cc22edabSDarrick J. Wong 	error = xrep_findparent_scan(&rp->pscan);
28034c9382cSDarrick J. Wong out_relock:
281cc22edabSDarrick J. Wong 	xchk_ilock(sc, XFS_ILOCK_EXCL);
282cc22edabSDarrick J. Wong 
283cc22edabSDarrick J. Wong 	return error;
284cc22edabSDarrick J. Wong }
285cc22edabSDarrick J. Wong 
286b334f7faSDarrick J. Wong /*
287b334f7faSDarrick J. Wong  * Add this stashed incore parent pointer to the temporary file.
288b334f7faSDarrick J. Wong  * The caller must hold the tempdir's IOLOCK, must not hold any ILOCKs, and
289b334f7faSDarrick J. Wong  * must not be in transaction context.
290b334f7faSDarrick J. Wong  */
291b334f7faSDarrick J. Wong STATIC int
292b334f7faSDarrick J. Wong xrep_parent_replay_update(
293b334f7faSDarrick J. Wong 	struct xrep_parent	*rp,
294b334f7faSDarrick J. Wong 	const struct xfs_name	*xname,
295b334f7faSDarrick J. Wong 	struct xrep_pptr	*pptr)
296b334f7faSDarrick J. Wong {
297b334f7faSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
298b334f7faSDarrick J. Wong 
29965a1fb7aSDarrick J. Wong 	switch (pptr->action) {
30065a1fb7aSDarrick J. Wong 	case XREP_PPTR_ADD:
301b334f7faSDarrick J. Wong 		/* Create parent pointer. */
30265a1fb7aSDarrick J. Wong 		trace_xrep_parent_replay_parentadd(sc->tempip, xname,
30365a1fb7aSDarrick J. Wong 				&pptr->pptr_rec);
304b334f7faSDarrick J. Wong 
305b334f7faSDarrick J. Wong 		return xfs_parent_set(sc->tempip, sc->ip->i_ino, xname,
306b334f7faSDarrick J. Wong 				&pptr->pptr_rec, &rp->pptr_args);
30765a1fb7aSDarrick J. Wong 	case XREP_PPTR_REMOVE:
30865a1fb7aSDarrick J. Wong 		/* Remove parent pointer. */
30965a1fb7aSDarrick J. Wong 		trace_xrep_parent_replay_parentremove(sc->tempip, xname,
31065a1fb7aSDarrick J. Wong 				&pptr->pptr_rec);
31165a1fb7aSDarrick J. Wong 
31265a1fb7aSDarrick J. Wong 		return xfs_parent_unset(sc->tempip, sc->ip->i_ino, xname,
31365a1fb7aSDarrick J. Wong 				&pptr->pptr_rec, &rp->pptr_args);
31465a1fb7aSDarrick J. Wong 	}
31565a1fb7aSDarrick J. Wong 
31665a1fb7aSDarrick J. Wong 	ASSERT(0);
31765a1fb7aSDarrick J. Wong 	return -EIO;
318b334f7faSDarrick J. Wong }
319b334f7faSDarrick J. Wong 
320b334f7faSDarrick J. Wong /*
321b334f7faSDarrick J. Wong  * Flush stashed parent pointer updates that have been recorded by the scanner.
322b334f7faSDarrick J. Wong  * This is done to reduce the memory requirements of the parent pointer
323b334f7faSDarrick J. Wong  * rebuild, since files can have a lot of hardlinks and the fs can be busy.
324b334f7faSDarrick J. Wong  *
325b334f7faSDarrick J. Wong  * Caller must not hold transactions or ILOCKs.  Caller must hold the tempfile
326b334f7faSDarrick J. Wong  * IOLOCK.
327b334f7faSDarrick J. Wong  */
328b334f7faSDarrick J. Wong STATIC int
329b334f7faSDarrick J. Wong xrep_parent_replay_updates(
330b334f7faSDarrick J. Wong 	struct xrep_parent	*rp)
331b334f7faSDarrick J. Wong {
332b334f7faSDarrick J. Wong 	xfarray_idx_t		array_cur;
333b334f7faSDarrick J. Wong 	int			error;
334b334f7faSDarrick J. Wong 
335b334f7faSDarrick J. Wong 	mutex_lock(&rp->pscan.lock);
336b334f7faSDarrick J. Wong 	foreach_xfarray_idx(rp->pptr_recs, array_cur) {
337b334f7faSDarrick J. Wong 		struct xrep_pptr	pptr;
338b334f7faSDarrick J. Wong 
339b334f7faSDarrick J. Wong 		error = xfarray_load(rp->pptr_recs, array_cur, &pptr);
340b334f7faSDarrick J. Wong 		if (error)
341b334f7faSDarrick J. Wong 			goto out_unlock;
342b334f7faSDarrick J. Wong 
343b334f7faSDarrick J. Wong 		error = xfblob_loadname(rp->pptr_names, pptr.name_cookie,
344b334f7faSDarrick J. Wong 				&rp->xname, pptr.namelen);
345b334f7faSDarrick J. Wong 		if (error)
346b334f7faSDarrick J. Wong 			goto out_unlock;
347b334f7faSDarrick J. Wong 		rp->xname.len = pptr.namelen;
348b334f7faSDarrick J. Wong 		mutex_unlock(&rp->pscan.lock);
349b334f7faSDarrick J. Wong 
350b334f7faSDarrick J. Wong 		error = xrep_parent_replay_update(rp, &rp->xname, &pptr);
351b334f7faSDarrick J. Wong 		if (error)
352b334f7faSDarrick J. Wong 			return error;
353b334f7faSDarrick J. Wong 
354b334f7faSDarrick J. Wong 		mutex_lock(&rp->pscan.lock);
355b334f7faSDarrick J. Wong 	}
356b334f7faSDarrick J. Wong 
357b334f7faSDarrick J. Wong 	/* Empty out both arrays now that we've added the entries. */
358b334f7faSDarrick J. Wong 	xfarray_truncate(rp->pptr_recs);
359b334f7faSDarrick J. Wong 	xfblob_truncate(rp->pptr_names);
360b334f7faSDarrick J. Wong 	mutex_unlock(&rp->pscan.lock);
361b334f7faSDarrick J. Wong 	return 0;
362b334f7faSDarrick J. Wong out_unlock:
363b334f7faSDarrick J. Wong 	mutex_unlock(&rp->pscan.lock);
364b334f7faSDarrick J. Wong 	return error;
365b334f7faSDarrick J. Wong }
366b334f7faSDarrick J. Wong 
367b334f7faSDarrick J. Wong /*
368b334f7faSDarrick J. Wong  * Remember that we want to create a parent pointer in the tempfile.  These
369b334f7faSDarrick J. Wong  * stashed actions will be replayed later.
370b334f7faSDarrick J. Wong  */
371b334f7faSDarrick J. Wong STATIC int
372b334f7faSDarrick J. Wong xrep_parent_stash_parentadd(
373b334f7faSDarrick J. Wong 	struct xrep_parent	*rp,
374b334f7faSDarrick J. Wong 	const struct xfs_name	*name,
375b334f7faSDarrick J. Wong 	const struct xfs_inode	*dp)
376b334f7faSDarrick J. Wong {
377b334f7faSDarrick J. Wong 	struct xrep_pptr	pptr = {
37865a1fb7aSDarrick J. Wong 		.action		= XREP_PPTR_ADD,
379b334f7faSDarrick J. Wong 		.namelen	= name->len,
380b334f7faSDarrick J. Wong 	};
381b334f7faSDarrick J. Wong 	int			error;
382b334f7faSDarrick J. Wong 
383b334f7faSDarrick J. Wong 	trace_xrep_parent_stash_parentadd(rp->sc->tempip, dp, name);
384b334f7faSDarrick J. Wong 
385b334f7faSDarrick J. Wong 	xfs_inode_to_parent_rec(&pptr.pptr_rec, dp);
386b334f7faSDarrick J. Wong 	error = xfblob_storename(rp->pptr_names, &pptr.name_cookie, name);
387b334f7faSDarrick J. Wong 	if (error)
388b334f7faSDarrick J. Wong 		return error;
389b334f7faSDarrick J. Wong 
390b334f7faSDarrick J. Wong 	return xfarray_append(rp->pptr_recs, &pptr);
391b334f7faSDarrick J. Wong }
392b334f7faSDarrick J. Wong 
393b334f7faSDarrick J. Wong /*
39465a1fb7aSDarrick J. Wong  * Remember that we want to remove a parent pointer from the tempfile.  These
39565a1fb7aSDarrick J. Wong  * stashed actions will be replayed later.
39665a1fb7aSDarrick J. Wong  */
39765a1fb7aSDarrick J. Wong STATIC int
39865a1fb7aSDarrick J. Wong xrep_parent_stash_parentremove(
39965a1fb7aSDarrick J. Wong 	struct xrep_parent	*rp,
40065a1fb7aSDarrick J. Wong 	const struct xfs_name	*name,
40165a1fb7aSDarrick J. Wong 	const struct xfs_inode	*dp)
40265a1fb7aSDarrick J. Wong {
40365a1fb7aSDarrick J. Wong 	struct xrep_pptr	pptr = {
40465a1fb7aSDarrick J. Wong 		.action		= XREP_PPTR_REMOVE,
40565a1fb7aSDarrick J. Wong 		.namelen	= name->len,
40665a1fb7aSDarrick J. Wong 	};
40765a1fb7aSDarrick J. Wong 	int			error;
40865a1fb7aSDarrick J. Wong 
40965a1fb7aSDarrick J. Wong 	trace_xrep_parent_stash_parentremove(rp->sc->tempip, dp, name);
41065a1fb7aSDarrick J. Wong 
41165a1fb7aSDarrick J. Wong 	xfs_inode_to_parent_rec(&pptr.pptr_rec, dp);
41265a1fb7aSDarrick J. Wong 	error = xfblob_storename(rp->pptr_names, &pptr.name_cookie, name);
41365a1fb7aSDarrick J. Wong 	if (error)
41465a1fb7aSDarrick J. Wong 		return error;
41565a1fb7aSDarrick J. Wong 
41665a1fb7aSDarrick J. Wong 	return xfarray_append(rp->pptr_recs, &pptr);
41765a1fb7aSDarrick J. Wong }
41865a1fb7aSDarrick J. Wong 
41965a1fb7aSDarrick J. Wong /*
420b334f7faSDarrick J. Wong  * Examine an entry of a directory.  If this dirent leads us back to the file
421b334f7faSDarrick J. Wong  * whose parent pointers we're rebuilding, add a pptr to the temporary
422b334f7faSDarrick J. Wong  * directory.
423b334f7faSDarrick J. Wong  */
424b334f7faSDarrick J. Wong STATIC int
425b334f7faSDarrick J. Wong xrep_parent_scan_dirent(
426b334f7faSDarrick J. Wong 	struct xfs_scrub	*sc,
427b334f7faSDarrick J. Wong 	struct xfs_inode	*dp,
428b334f7faSDarrick J. Wong 	xfs_dir2_dataptr_t	dapos,
429b334f7faSDarrick J. Wong 	const struct xfs_name	*name,
430b334f7faSDarrick J. Wong 	xfs_ino_t		ino,
431b334f7faSDarrick J. Wong 	void			*priv)
432b334f7faSDarrick J. Wong {
433b334f7faSDarrick J. Wong 	struct xrep_parent	*rp = priv;
434b334f7faSDarrick J. Wong 	int			error;
435b334f7faSDarrick J. Wong 
436b334f7faSDarrick J. Wong 	/* Dirent doesn't point to this directory. */
437b334f7faSDarrick J. Wong 	if (ino != rp->sc->ip->i_ino)
438b334f7faSDarrick J. Wong 		return 0;
439b334f7faSDarrick J. Wong 
440b334f7faSDarrick J. Wong 	/* No weird looking names. */
441b334f7faSDarrick J. Wong 	if (name->len == 0 || !xfs_dir2_namecheck(name->name, name->len))
442b334f7faSDarrick J. Wong 		return -EFSCORRUPTED;
443b334f7faSDarrick J. Wong 
444b334f7faSDarrick J. Wong 	/* No mismatching ftypes. */
445b334f7faSDarrick J. Wong 	if (name->type != xfs_mode_to_ftype(VFS_I(sc->ip)->i_mode))
446b334f7faSDarrick J. Wong 		return -EFSCORRUPTED;
447b334f7faSDarrick J. Wong 
448b334f7faSDarrick J. Wong 	/* Don't pick up dot or dotdot entries; we only want child dirents. */
449b334f7faSDarrick J. Wong 	if (xfs_dir2_samename(name, &xfs_name_dotdot) ||
450b334f7faSDarrick J. Wong 	    xfs_dir2_samename(name, &xfs_name_dot))
451b334f7faSDarrick J. Wong 		return 0;
452b334f7faSDarrick J. Wong 
453b334f7faSDarrick J. Wong 	/*
454b334f7faSDarrick J. Wong 	 * Transform this dirent into a parent pointer and queue it for later
455b334f7faSDarrick J. Wong 	 * addition to the temporary file.
456b334f7faSDarrick J. Wong 	 */
457b334f7faSDarrick J. Wong 	mutex_lock(&rp->pscan.lock);
458b334f7faSDarrick J. Wong 	error = xrep_parent_stash_parentadd(rp, name, dp);
459b334f7faSDarrick J. Wong 	mutex_unlock(&rp->pscan.lock);
460b334f7faSDarrick J. Wong 	return error;
461b334f7faSDarrick J. Wong }
462b334f7faSDarrick J. Wong 
463b334f7faSDarrick J. Wong /*
464b334f7faSDarrick J. Wong  * Decide if we want to look for dirents in this directory.  Skip the file
465b334f7faSDarrick J. Wong  * being repaired and any files being used to stage repairs.
466b334f7faSDarrick J. Wong  */
467b334f7faSDarrick J. Wong static inline bool
468b334f7faSDarrick J. Wong xrep_parent_want_scan(
469b334f7faSDarrick J. Wong 	struct xrep_parent	*rp,
470b334f7faSDarrick J. Wong 	const struct xfs_inode	*ip)
471b334f7faSDarrick J. Wong {
472b334f7faSDarrick J. Wong 	return ip != rp->sc->ip && !xrep_is_tempfile(ip);
473b334f7faSDarrick J. Wong }
474b334f7faSDarrick J. Wong 
475b334f7faSDarrick J. Wong /*
476b334f7faSDarrick J. Wong  * Take ILOCK on a file that we want to scan.
477b334f7faSDarrick J. Wong  *
478b334f7faSDarrick J. Wong  * Select ILOCK_EXCL if the file is a directory with an unloaded data bmbt.
479b334f7faSDarrick J. Wong  * Otherwise, take ILOCK_SHARED.
480b334f7faSDarrick J. Wong  */
481b334f7faSDarrick J. Wong static inline unsigned int
482b334f7faSDarrick J. Wong xrep_parent_scan_ilock(
483b334f7faSDarrick J. Wong 	struct xrep_parent	*rp,
484b334f7faSDarrick J. Wong 	struct xfs_inode	*ip)
485b334f7faSDarrick J. Wong {
486b334f7faSDarrick J. Wong 	uint			lock_mode = XFS_ILOCK_SHARED;
487b334f7faSDarrick J. Wong 
488b334f7faSDarrick J. Wong 	/* Still need to take the shared ILOCK to advance the iscan cursor. */
489b334f7faSDarrick J. Wong 	if (!xrep_parent_want_scan(rp, ip))
490b334f7faSDarrick J. Wong 		goto lock;
491b334f7faSDarrick J. Wong 
492b334f7faSDarrick J. Wong 	if (S_ISDIR(VFS_I(ip)->i_mode) && xfs_need_iread_extents(&ip->i_df)) {
493b334f7faSDarrick J. Wong 		lock_mode = XFS_ILOCK_EXCL;
494b334f7faSDarrick J. Wong 		goto lock;
495b334f7faSDarrick J. Wong 	}
496b334f7faSDarrick J. Wong 
497b334f7faSDarrick J. Wong lock:
498b334f7faSDarrick J. Wong 	xfs_ilock(ip, lock_mode);
499b334f7faSDarrick J. Wong 	return lock_mode;
500b334f7faSDarrick J. Wong }
501b334f7faSDarrick J. Wong 
502b334f7faSDarrick J. Wong /*
503b334f7faSDarrick J. Wong  * Scan this file for relevant child dirents that point to the file whose
504b334f7faSDarrick J. Wong  * parent pointers we're rebuilding.
505b334f7faSDarrick J. Wong  */
506b334f7faSDarrick J. Wong STATIC int
507b334f7faSDarrick J. Wong xrep_parent_scan_file(
508b334f7faSDarrick J. Wong 	struct xrep_parent	*rp,
509b334f7faSDarrick J. Wong 	struct xfs_inode	*ip)
510b334f7faSDarrick J. Wong {
511b334f7faSDarrick J. Wong 	unsigned int		lock_mode;
512b334f7faSDarrick J. Wong 	int			error = 0;
513b334f7faSDarrick J. Wong 
514b334f7faSDarrick J. Wong 	lock_mode = xrep_parent_scan_ilock(rp, ip);
515b334f7faSDarrick J. Wong 
516b334f7faSDarrick J. Wong 	if (!xrep_parent_want_scan(rp, ip))
517b334f7faSDarrick J. Wong 		goto scan_done;
518b334f7faSDarrick J. Wong 
519b334f7faSDarrick J. Wong 	if (S_ISDIR(VFS_I(ip)->i_mode)) {
520b334f7faSDarrick J. Wong 		/*
521b334f7faSDarrick J. Wong 		 * If the directory looks as though it has been zapped by the
522b334f7faSDarrick J. Wong 		 * inode record repair code, we cannot scan for child dirents.
523b334f7faSDarrick J. Wong 		 */
524b334f7faSDarrick J. Wong 		if (xchk_dir_looks_zapped(ip)) {
525b334f7faSDarrick J. Wong 			error = -EBUSY;
526b334f7faSDarrick J. Wong 			goto scan_done;
527b334f7faSDarrick J. Wong 		}
528b334f7faSDarrick J. Wong 
529b334f7faSDarrick J. Wong 		error = xchk_dir_walk(rp->sc, ip, xrep_parent_scan_dirent, rp);
530b334f7faSDarrick J. Wong 		if (error)
531b334f7faSDarrick J. Wong 			goto scan_done;
532b334f7faSDarrick J. Wong 	}
533b334f7faSDarrick J. Wong 
534b334f7faSDarrick J. Wong scan_done:
535b334f7faSDarrick J. Wong 	xchk_iscan_mark_visited(&rp->pscan.iscan, ip);
536b334f7faSDarrick J. Wong 	xfs_iunlock(ip, lock_mode);
537b334f7faSDarrick J. Wong 	return error;
538b334f7faSDarrick J. Wong }
539b334f7faSDarrick J. Wong 
540b334f7faSDarrick J. Wong /* Decide if we've stashed too much pptr data in memory. */
541b334f7faSDarrick J. Wong static inline bool
542b334f7faSDarrick J. Wong xrep_parent_want_flush_stashed(
543b334f7faSDarrick J. Wong 	struct xrep_parent	*rp)
544b334f7faSDarrick J. Wong {
545b334f7faSDarrick J. Wong 	unsigned long long	bytes;
546b334f7faSDarrick J. Wong 
547b334f7faSDarrick J. Wong 	bytes = xfarray_bytes(rp->pptr_recs) + xfblob_bytes(rp->pptr_names);
548b334f7faSDarrick J. Wong 	return bytes > XREP_PARENT_MAX_STASH_BYTES;
549b334f7faSDarrick J. Wong }
550b334f7faSDarrick J. Wong 
551b334f7faSDarrick J. Wong /*
552b334f7faSDarrick J. Wong  * Scan all directories in the filesystem to look for dirents that we can turn
553b334f7faSDarrick J. Wong  * into parent pointers.
554b334f7faSDarrick J. Wong  */
555b334f7faSDarrick J. Wong STATIC int
556b334f7faSDarrick J. Wong xrep_parent_scan_dirtree(
557b334f7faSDarrick J. Wong 	struct xrep_parent	*rp)
558b334f7faSDarrick J. Wong {
559b334f7faSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
560b334f7faSDarrick J. Wong 	struct xfs_inode	*ip;
561b334f7faSDarrick J. Wong 	int			error;
562b334f7faSDarrick J. Wong 
563b334f7faSDarrick J. Wong 	/*
564b334f7faSDarrick J. Wong 	 * Filesystem scans are time consuming.  Drop the file ILOCK and all
565b334f7faSDarrick J. Wong 	 * other resources for the duration of the scan and hope for the best.
566b334f7faSDarrick J. Wong 	 * The live update hooks will keep our scan information up to date.
567b334f7faSDarrick J. Wong 	 */
568b334f7faSDarrick J. Wong 	xchk_trans_cancel(sc);
569b334f7faSDarrick J. Wong 	if (sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL))
570b334f7faSDarrick J. Wong 		xchk_iunlock(sc, sc->ilock_flags & (XFS_ILOCK_SHARED |
571b334f7faSDarrick J. Wong 						    XFS_ILOCK_EXCL));
572b334f7faSDarrick J. Wong 	error = xchk_trans_alloc_empty(sc);
573b334f7faSDarrick J. Wong 	if (error)
574b334f7faSDarrick J. Wong 		return error;
575b334f7faSDarrick J. Wong 
576b334f7faSDarrick J. Wong 	while ((error = xchk_iscan_iter(&rp->pscan.iscan, &ip)) == 1) {
577b334f7faSDarrick J. Wong 		bool		flush;
578b334f7faSDarrick J. Wong 
579b334f7faSDarrick J. Wong 		error = xrep_parent_scan_file(rp, ip);
580b334f7faSDarrick J. Wong 		xchk_irele(sc, ip);
581b334f7faSDarrick J. Wong 		if (error)
582b334f7faSDarrick J. Wong 			break;
583b334f7faSDarrick J. Wong 
584b334f7faSDarrick J. Wong 		/* Flush stashed pptr updates to constrain memory usage. */
585b334f7faSDarrick J. Wong 		mutex_lock(&rp->pscan.lock);
586b334f7faSDarrick J. Wong 		flush = xrep_parent_want_flush_stashed(rp);
587b334f7faSDarrick J. Wong 		mutex_unlock(&rp->pscan.lock);
588b334f7faSDarrick J. Wong 		if (flush) {
589b334f7faSDarrick J. Wong 			xchk_trans_cancel(sc);
590b334f7faSDarrick J. Wong 
591b334f7faSDarrick J. Wong 			error = xrep_tempfile_iolock_polled(sc);
592b334f7faSDarrick J. Wong 			if (error)
593b334f7faSDarrick J. Wong 				break;
594b334f7faSDarrick J. Wong 
595b334f7faSDarrick J. Wong 			error = xrep_parent_replay_updates(rp);
596b334f7faSDarrick J. Wong 			xrep_tempfile_iounlock(sc);
597b334f7faSDarrick J. Wong 			if (error)
598b334f7faSDarrick J. Wong 				break;
599b334f7faSDarrick J. Wong 
600b334f7faSDarrick J. Wong 			error = xchk_trans_alloc_empty(sc);
601b334f7faSDarrick J. Wong 			if (error)
602b334f7faSDarrick J. Wong 				break;
603b334f7faSDarrick J. Wong 		}
604b334f7faSDarrick J. Wong 
605b334f7faSDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
606b334f7faSDarrick J. Wong 			break;
607b334f7faSDarrick J. Wong 	}
608b334f7faSDarrick J. Wong 	xchk_iscan_iter_finish(&rp->pscan.iscan);
609b334f7faSDarrick J. Wong 	if (error) {
610b334f7faSDarrick J. Wong 		/*
611b334f7faSDarrick J. Wong 		 * If we couldn't grab an inode that was busy with a state
612b334f7faSDarrick J. Wong 		 * change, change the error code so that we exit to userspace
613b334f7faSDarrick J. Wong 		 * as quickly as possible.
614b334f7faSDarrick J. Wong 		 */
615b334f7faSDarrick J. Wong 		if (error == -EBUSY)
616b334f7faSDarrick J. Wong 			return -ECANCELED;
617b334f7faSDarrick J. Wong 		return error;
618b334f7faSDarrick J. Wong 	}
619b334f7faSDarrick J. Wong 
620b334f7faSDarrick J. Wong 	/*
621a26dc213SDarrick J. Wong 	 * Retake sc->ip's ILOCK now that we're done flushing stashed parent
622a26dc213SDarrick J. Wong 	 * pointers.  We end this function with an empty transaction and the
623a26dc213SDarrick J. Wong 	 * ILOCK.
624b334f7faSDarrick J. Wong 	 */
625a26dc213SDarrick J. Wong 	xchk_ilock(rp->sc, XFS_ILOCK_EXCL);
626b334f7faSDarrick J. Wong 	return 0;
627b334f7faSDarrick J. Wong }
628b334f7faSDarrick J. Wong 
62965a1fb7aSDarrick J. Wong /*
63065a1fb7aSDarrick J. Wong  * Capture dirent updates being made by other threads which are relevant to the
63165a1fb7aSDarrick J. Wong  * file being repaired.
63265a1fb7aSDarrick J. Wong  */
63365a1fb7aSDarrick J. Wong STATIC int
63465a1fb7aSDarrick J. Wong xrep_parent_live_update(
63565a1fb7aSDarrick J. Wong 	struct notifier_block		*nb,
63665a1fb7aSDarrick J. Wong 	unsigned long			action,
63765a1fb7aSDarrick J. Wong 	void				*data)
63865a1fb7aSDarrick J. Wong {
63965a1fb7aSDarrick J. Wong 	struct xfs_dir_update_params	*p = data;
64065a1fb7aSDarrick J. Wong 	struct xrep_parent		*rp;
64165a1fb7aSDarrick J. Wong 	struct xfs_scrub		*sc;
64265a1fb7aSDarrick J. Wong 	int				error;
64365a1fb7aSDarrick J. Wong 
64465a1fb7aSDarrick J. Wong 	rp = container_of(nb, struct xrep_parent, pscan.dhook.dirent_hook.nb);
64565a1fb7aSDarrick J. Wong 	sc = rp->sc;
64665a1fb7aSDarrick J. Wong 
64765a1fb7aSDarrick J. Wong 	/*
64865a1fb7aSDarrick J. Wong 	 * This thread updated a dirent that points to the file that we're
64965a1fb7aSDarrick J. Wong 	 * repairing, so stash the update for replay against the temporary
65065a1fb7aSDarrick J. Wong 	 * file.
65165a1fb7aSDarrick J. Wong 	 */
65265a1fb7aSDarrick J. Wong 	if (p->ip->i_ino == sc->ip->i_ino &&
65365a1fb7aSDarrick J. Wong 	    xchk_iscan_want_live_update(&rp->pscan.iscan, p->dp->i_ino)) {
65465a1fb7aSDarrick J. Wong 		mutex_lock(&rp->pscan.lock);
65565a1fb7aSDarrick J. Wong 		if (p->delta > 0)
65665a1fb7aSDarrick J. Wong 			error = xrep_parent_stash_parentadd(rp, p->name, p->dp);
65765a1fb7aSDarrick J. Wong 		else
65865a1fb7aSDarrick J. Wong 			error = xrep_parent_stash_parentremove(rp, p->name,
65965a1fb7aSDarrick J. Wong 					p->dp);
660a26dc213SDarrick J. Wong 		if (!error)
661a26dc213SDarrick J. Wong 			rp->saw_pptr_updates = true;
66265a1fb7aSDarrick J. Wong 		mutex_unlock(&rp->pscan.lock);
66365a1fb7aSDarrick J. Wong 		if (error)
66465a1fb7aSDarrick J. Wong 			goto out_abort;
66565a1fb7aSDarrick J. Wong 	}
66665a1fb7aSDarrick J. Wong 
66765a1fb7aSDarrick J. Wong 	return NOTIFY_DONE;
66865a1fb7aSDarrick J. Wong out_abort:
66965a1fb7aSDarrick J. Wong 	xchk_iscan_abort(&rp->pscan.iscan);
67065a1fb7aSDarrick J. Wong 	return NOTIFY_DONE;
67165a1fb7aSDarrick J. Wong }
67265a1fb7aSDarrick J. Wong 
673cc22edabSDarrick J. Wong /* Reset a directory's dotdot entry, if needed. */
674cc22edabSDarrick J. Wong STATIC int
675cc22edabSDarrick J. Wong xrep_parent_reset_dotdot(
676cc22edabSDarrick J. Wong 	struct xrep_parent	*rp)
677cc22edabSDarrick J. Wong {
678cc22edabSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
679cc22edabSDarrick J. Wong 	xfs_ino_t		ino;
680cc22edabSDarrick J. Wong 	unsigned int		spaceres;
681cc22edabSDarrick J. Wong 	int			error = 0;
682cc22edabSDarrick J. Wong 
683cc22edabSDarrick J. Wong 	ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
684cc22edabSDarrick J. Wong 
685cc22edabSDarrick J. Wong 	error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &ino);
686cc22edabSDarrick J. Wong 	if (error || ino == rp->pscan.parent_ino)
687cc22edabSDarrick J. Wong 		return error;
688cc22edabSDarrick J. Wong 
689cc22edabSDarrick J. Wong 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
690cc22edabSDarrick J. Wong 
691cc22edabSDarrick J. Wong 	trace_xrep_parent_reset_dotdot(sc->ip, rp->pscan.parent_ino);
692cc22edabSDarrick J. Wong 
693cc22edabSDarrick J. Wong 	/*
694cc22edabSDarrick J. Wong 	 * Reserve more space just in case we have to expand the dir.  We're
695cc22edabSDarrick J. Wong 	 * allowed to exceed quota to repair inconsistent metadata.
696cc22edabSDarrick J. Wong 	 */
6975a8338c8SAllison Henderson 	spaceres = xfs_rename_space_res(sc->mp, 0, false, xfs_name_dotdot.len,
6985a8338c8SAllison Henderson 			false);
699cc22edabSDarrick J. Wong 	error = xfs_trans_reserve_more_inode(sc->tp, sc->ip, spaceres, 0,
700cc22edabSDarrick J. Wong 			true);
701cc22edabSDarrick J. Wong 	if (error)
702cc22edabSDarrick J. Wong 		return error;
703cc22edabSDarrick J. Wong 
704cc22edabSDarrick J. Wong 	error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,
705cc22edabSDarrick J. Wong 			rp->pscan.parent_ino, spaceres);
706cc22edabSDarrick J. Wong 	if (error)
707cc22edabSDarrick J. Wong 		return error;
708cc22edabSDarrick J. Wong 
709cc22edabSDarrick J. Wong 	/*
710cc22edabSDarrick J. Wong 	 * Roll transaction to detach the inode from the transaction but retain
711cc22edabSDarrick J. Wong 	 * ILOCK_EXCL.
712cc22edabSDarrick J. Wong 	 */
713cc22edabSDarrick J. Wong 	return xfs_trans_roll(&sc->tp);
714cc22edabSDarrick J. Wong }
715cc22edabSDarrick J. Wong 
716a26dc213SDarrick J. Wong /* Pass back the parent inumber if this a parent pointer */
717a26dc213SDarrick J. Wong STATIC int
718a26dc213SDarrick J. Wong xrep_parent_lookup_pptr(
719a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc,
720a26dc213SDarrick J. Wong 	struct xfs_inode	*ip,
721a26dc213SDarrick J. Wong 	unsigned int		attr_flags,
722a26dc213SDarrick J. Wong 	const unsigned char	*name,
723a26dc213SDarrick J. Wong 	unsigned int		namelen,
724a26dc213SDarrick J. Wong 	const void		*value,
725a26dc213SDarrick J. Wong 	unsigned int		valuelen,
726a26dc213SDarrick J. Wong 	void			*priv)
727a26dc213SDarrick J. Wong {
728a26dc213SDarrick J. Wong 	xfs_ino_t		*inop = priv;
729a26dc213SDarrick J. Wong 	xfs_ino_t		parent_ino;
730a26dc213SDarrick J. Wong 	int			error;
731a26dc213SDarrick J. Wong 
732a26dc213SDarrick J. Wong 	if (!(attr_flags & XFS_ATTR_PARENT))
733a26dc213SDarrick J. Wong 		return 0;
734a26dc213SDarrick J. Wong 
735a26dc213SDarrick J. Wong 	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,
736a26dc213SDarrick J. Wong 			valuelen, &parent_ino, NULL);
737a26dc213SDarrick J. Wong 	if (error)
738a26dc213SDarrick J. Wong 		return error;
739a26dc213SDarrick J. Wong 
740a26dc213SDarrick J. Wong 	*inop = parent_ino;
741a26dc213SDarrick J. Wong 	return -ECANCELED;
742a26dc213SDarrick J. Wong }
743a26dc213SDarrick J. Wong 
744a26dc213SDarrick J. Wong /*
745a26dc213SDarrick J. Wong  * Find the first parent of the scrub target by walking parent pointers for
746a26dc213SDarrick J. Wong  * the purpose of deciding if we're going to move it to the orphanage.
747a26dc213SDarrick J. Wong  * We don't care if the attr fork is zapped.
748a26dc213SDarrick J. Wong  */
749a26dc213SDarrick J. Wong STATIC int
750a26dc213SDarrick J. Wong xrep_parent_lookup_pptrs(
751a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc,
752a26dc213SDarrick J. Wong 	xfs_ino_t		*inop)
753a26dc213SDarrick J. Wong {
754a26dc213SDarrick J. Wong 	int			error;
755a26dc213SDarrick J. Wong 
756a26dc213SDarrick J. Wong 	*inop = NULLFSINO;
757a26dc213SDarrick J. Wong 
758a26dc213SDarrick J. Wong 	error = xchk_xattr_walk(sc, sc->ip, xrep_parent_lookup_pptr, NULL,
759a26dc213SDarrick J. Wong 			inop);
760a26dc213SDarrick J. Wong 	if (error && error != -ECANCELED)
761a26dc213SDarrick J. Wong 		return error;
762a26dc213SDarrick J. Wong 	return 0;
763a26dc213SDarrick J. Wong }
764a26dc213SDarrick J. Wong 
765cc22edabSDarrick J. Wong /*
7661e58a8ccSDarrick J. Wong  * Move the current file to the orphanage.
7671e58a8ccSDarrick J. Wong  *
7681e58a8ccSDarrick J. Wong  * Caller must hold IOLOCK_EXCL on @sc->ip, and no other inode locks.  Upon
7691e58a8ccSDarrick J. Wong  * successful return, the scrub transaction will have enough extra reservation
7701e58a8ccSDarrick J. Wong  * to make the move; it will hold IOLOCK_EXCL and ILOCK_EXCL of @sc->ip and the
7711e58a8ccSDarrick J. Wong  * orphanage; and both inodes will be ijoined.
7721e58a8ccSDarrick J. Wong  */
7731e58a8ccSDarrick J. Wong STATIC int
7741e58a8ccSDarrick J. Wong xrep_parent_move_to_orphanage(
7751e58a8ccSDarrick J. Wong 	struct xrep_parent	*rp)
7761e58a8ccSDarrick J. Wong {
7771e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
7781e58a8ccSDarrick J. Wong 	xfs_ino_t		orig_parent, new_parent;
7791e58a8ccSDarrick J. Wong 	int			error;
7801e58a8ccSDarrick J. Wong 
781a26dc213SDarrick J. Wong 	if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {
7821e58a8ccSDarrick J. Wong 		/*
783a26dc213SDarrick J. Wong 		 * We are about to drop the ILOCK on sc->ip to lock the
784a26dc213SDarrick J. Wong 		 * orphanage and prepare for the adoption.  Therefore, look up
785a26dc213SDarrick J. Wong 		 * the old dotdot entry for sc->ip so that we can compare it
786a26dc213SDarrick J. Wong 		 * after we re-lock sc->ip.
7871e58a8ccSDarrick J. Wong 		 */
788a26dc213SDarrick J. Wong 		error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,
789a26dc213SDarrick J. Wong 				&orig_parent);
7901e58a8ccSDarrick J. Wong 		if (error)
7911e58a8ccSDarrick J. Wong 			return error;
792a26dc213SDarrick J. Wong 	} else {
793a26dc213SDarrick J. Wong 		/*
794a26dc213SDarrick J. Wong 		 * We haven't dropped the ILOCK since we committed the new
795a26dc213SDarrick J. Wong 		 * xattr structure (and hence the new parent pointer records),
796a26dc213SDarrick J. Wong 		 * which means that the file cannot have been moved in the
797a26dc213SDarrick J. Wong 		 * directory tree, and there are no parents.
798a26dc213SDarrick J. Wong 		 */
799a26dc213SDarrick J. Wong 		orig_parent = NULLFSINO;
800a26dc213SDarrick J. Wong 	}
8011e58a8ccSDarrick J. Wong 
8021e58a8ccSDarrick J. Wong 	/*
8031e58a8ccSDarrick J. Wong 	 * Drop the ILOCK on the scrub target and commit the transaction.
8041e58a8ccSDarrick J. Wong 	 * Adoption computes its own resource requirements and gathers the
8051e58a8ccSDarrick J. Wong 	 * necessary components.
8061e58a8ccSDarrick J. Wong 	 */
8071e58a8ccSDarrick J. Wong 	error = xrep_trans_commit(sc);
8081e58a8ccSDarrick J. Wong 	if (error)
8091e58a8ccSDarrick J. Wong 		return error;
8101e58a8ccSDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
8111e58a8ccSDarrick J. Wong 
8121e58a8ccSDarrick J. Wong 	/* If we can take the orphanage's iolock then we're ready to move. */
8131e58a8ccSDarrick J. Wong 	if (!xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) {
8141e58a8ccSDarrick J. Wong 		xchk_iunlock(sc, sc->ilock_flags);
8151e58a8ccSDarrick J. Wong 		error = xrep_orphanage_iolock_two(sc);
8161e58a8ccSDarrick J. Wong 		if (error)
8171e58a8ccSDarrick J. Wong 			return error;
8181e58a8ccSDarrick J. Wong 	}
8191e58a8ccSDarrick J. Wong 
8201e58a8ccSDarrick J. Wong 	/* Grab transaction and ILOCK the two files. */
8211e58a8ccSDarrick J. Wong 	error = xrep_adoption_trans_alloc(sc, &rp->adoption);
8221e58a8ccSDarrick J. Wong 	if (error)
8231e58a8ccSDarrick J. Wong 		return error;
8241e58a8ccSDarrick J. Wong 
8251e58a8ccSDarrick J. Wong 	error = xrep_adoption_compute_name(&rp->adoption, &rp->xname);
8261e58a8ccSDarrick J. Wong 	if (error)
8271e58a8ccSDarrick J. Wong 		return error;
8281e58a8ccSDarrick J. Wong 
8291e58a8ccSDarrick J. Wong 	/*
8301e58a8ccSDarrick J. Wong 	 * Now that we've reacquired the ILOCK on sc->ip, look up the dotdot
8311e58a8ccSDarrick J. Wong 	 * entry again.  If the parent changed or the child was unlinked while
8321e58a8ccSDarrick J. Wong 	 * the child directory was unlocked, we don't need to move the child to
833a26dc213SDarrick J. Wong 	 * the orphanage after all.  For a non-directory, we have to scan for
834a26dc213SDarrick J. Wong 	 * the first parent pointer to see if one has been added.
8351e58a8ccSDarrick J. Wong 	 */
836a26dc213SDarrick J. Wong 	if (S_ISDIR(VFS_I(sc->ip)->i_mode))
837a26dc213SDarrick J. Wong 		error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,
838a26dc213SDarrick J. Wong 				&new_parent);
839a26dc213SDarrick J. Wong 	else
840a26dc213SDarrick J. Wong 		error = xrep_parent_lookup_pptrs(sc, &new_parent);
8411e58a8ccSDarrick J. Wong 	if (error)
8421e58a8ccSDarrick J. Wong 		return error;
8431e58a8ccSDarrick J. Wong 
8441e58a8ccSDarrick J. Wong 	/*
8451e58a8ccSDarrick J. Wong 	 * Attach to the orphanage if we still have a linked directory and it
8461e58a8ccSDarrick J. Wong 	 * hasn't been moved.
8471e58a8ccSDarrick J. Wong 	 */
8481e58a8ccSDarrick J. Wong 	if (orig_parent == new_parent && VFS_I(sc->ip)->i_nlink > 0) {
8491e58a8ccSDarrick J. Wong 		error = xrep_adoption_move(&rp->adoption);
8501e58a8ccSDarrick J. Wong 		if (error)
8511e58a8ccSDarrick J. Wong 			return error;
8521e58a8ccSDarrick J. Wong 	}
8531e58a8ccSDarrick J. Wong 
8541e58a8ccSDarrick J. Wong 	/*
8551e58a8ccSDarrick J. Wong 	 * Launder the scrub transaction so we can drop the orphanage ILOCK
8561e58a8ccSDarrick J. Wong 	 * and IOLOCK.  Return holding the scrub target's ILOCK and IOLOCK.
8571e58a8ccSDarrick J. Wong 	 */
8581e58a8ccSDarrick J. Wong 	error = xrep_adoption_trans_roll(&rp->adoption);
8591e58a8ccSDarrick J. Wong 	if (error)
8601e58a8ccSDarrick J. Wong 		return error;
8611e58a8ccSDarrick J. Wong 
8621e58a8ccSDarrick J. Wong 	xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
8631e58a8ccSDarrick J. Wong 	xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
8641e58a8ccSDarrick J. Wong 	return 0;
8651e58a8ccSDarrick J. Wong }
8661e58a8ccSDarrick J. Wong 
867a26dc213SDarrick J. Wong /* Ensure that the xattr value buffer is large enough. */
868a26dc213SDarrick J. Wong STATIC int
869a26dc213SDarrick J. Wong xrep_parent_alloc_xattr_value(
870a26dc213SDarrick J. Wong 	struct xrep_parent	*rp,
871a26dc213SDarrick J. Wong 	size_t			bufsize)
872a26dc213SDarrick J. Wong {
873a26dc213SDarrick J. Wong 	void			*new_val;
874a26dc213SDarrick J. Wong 
875a26dc213SDarrick J. Wong 	if (rp->xattr_value_sz >= bufsize)
876a26dc213SDarrick J. Wong 		return 0;
877a26dc213SDarrick J. Wong 
878a26dc213SDarrick J. Wong 	if (rp->xattr_value) {
879a26dc213SDarrick J. Wong 		kvfree(rp->xattr_value);
880a26dc213SDarrick J. Wong 		rp->xattr_value = NULL;
881a26dc213SDarrick J. Wong 		rp->xattr_value_sz = 0;
882a26dc213SDarrick J. Wong 	}
883a26dc213SDarrick J. Wong 
884a26dc213SDarrick J. Wong 	new_val = kvmalloc(bufsize, XCHK_GFP_FLAGS);
885a26dc213SDarrick J. Wong 	if (!new_val)
886a26dc213SDarrick J. Wong 		return -ENOMEM;
887a26dc213SDarrick J. Wong 
888a26dc213SDarrick J. Wong 	rp->xattr_value = new_val;
889a26dc213SDarrick J. Wong 	rp->xattr_value_sz = bufsize;
890a26dc213SDarrick J. Wong 	return 0;
891a26dc213SDarrick J. Wong }
892a26dc213SDarrick J. Wong 
893a26dc213SDarrick J. Wong /* Retrieve the (remote) value of a non-pptr xattr. */
894a26dc213SDarrick J. Wong STATIC int
895a26dc213SDarrick J. Wong xrep_parent_fetch_xattr_remote(
896a26dc213SDarrick J. Wong 	struct xrep_parent	*rp,
897a26dc213SDarrick J. Wong 	struct xfs_inode	*ip,
898a26dc213SDarrick J. Wong 	unsigned int		attr_flags,
899a26dc213SDarrick J. Wong 	const unsigned char	*name,
900a26dc213SDarrick J. Wong 	unsigned int		namelen,
901a26dc213SDarrick J. Wong 	unsigned int		valuelen)
902a26dc213SDarrick J. Wong {
903a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
904a26dc213SDarrick J. Wong 	struct xfs_da_args	args = {
905a26dc213SDarrick J. Wong 		.attr_filter	= attr_flags & XFS_ATTR_NSP_ONDISK_MASK,
906a26dc213SDarrick J. Wong 		.geo		= sc->mp->m_attr_geo,
907a26dc213SDarrick J. Wong 		.whichfork	= XFS_ATTR_FORK,
908a26dc213SDarrick J. Wong 		.dp		= ip,
909a26dc213SDarrick J. Wong 		.name		= name,
910a26dc213SDarrick J. Wong 		.namelen	= namelen,
911a26dc213SDarrick J. Wong 		.trans		= sc->tp,
912a26dc213SDarrick J. Wong 		.valuelen	= valuelen,
913a26dc213SDarrick J. Wong 		.owner		= ip->i_ino,
914a26dc213SDarrick J. Wong 	};
915a26dc213SDarrick J. Wong 	int			error;
916a26dc213SDarrick J. Wong 
917a26dc213SDarrick J. Wong 	/*
918a26dc213SDarrick J. Wong 	 * If we need a larger value buffer, try to allocate one.  If that
919a26dc213SDarrick J. Wong 	 * fails, return with -EDEADLOCK to try harder.
920a26dc213SDarrick J. Wong 	 */
921a26dc213SDarrick J. Wong 	error = xrep_parent_alloc_xattr_value(rp, valuelen);
922a26dc213SDarrick J. Wong 	if (error == -ENOMEM)
923a26dc213SDarrick J. Wong 		return -EDEADLOCK;
924a26dc213SDarrick J. Wong 	if (error)
925a26dc213SDarrick J. Wong 		return error;
926a26dc213SDarrick J. Wong 
927a26dc213SDarrick J. Wong 	args.value = rp->xattr_value;
928a26dc213SDarrick J. Wong 	xfs_attr_sethash(&args);
929a26dc213SDarrick J. Wong 	return xfs_attr_get_ilocked(&args);
930a26dc213SDarrick J. Wong }
931a26dc213SDarrick J. Wong 
932a26dc213SDarrick J. Wong /* Stash non-pptr attributes for later replay into the temporary file. */
933a26dc213SDarrick J. Wong STATIC int
934a26dc213SDarrick J. Wong xrep_parent_stash_xattr(
935a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc,
936a26dc213SDarrick J. Wong 	struct xfs_inode	*ip,
937a26dc213SDarrick J. Wong 	unsigned int		attr_flags,
938a26dc213SDarrick J. Wong 	const unsigned char	*name,
939a26dc213SDarrick J. Wong 	unsigned int		namelen,
940a26dc213SDarrick J. Wong 	const void		*value,
941a26dc213SDarrick J. Wong 	unsigned int		valuelen,
942a26dc213SDarrick J. Wong 	void			*priv)
943a26dc213SDarrick J. Wong {
944a26dc213SDarrick J. Wong 	struct xrep_parent_xattr key = {
945a26dc213SDarrick J. Wong 		.valuelen	= valuelen,
946a26dc213SDarrick J. Wong 		.namelen	= namelen,
947a26dc213SDarrick J. Wong 		.flags		= attr_flags & XFS_ATTR_NSP_ONDISK_MASK,
948a26dc213SDarrick J. Wong 	};
949a26dc213SDarrick J. Wong 	struct xrep_parent	*rp = priv;
950a26dc213SDarrick J. Wong 	int			error;
951a26dc213SDarrick J. Wong 
952a26dc213SDarrick J. Wong 	if (attr_flags & (XFS_ATTR_INCOMPLETE | XFS_ATTR_PARENT))
953a26dc213SDarrick J. Wong 		return 0;
954a26dc213SDarrick J. Wong 
955a26dc213SDarrick J. Wong 	if (!value) {
956a26dc213SDarrick J. Wong 		error = xrep_parent_fetch_xattr_remote(rp, ip, attr_flags,
957a26dc213SDarrick J. Wong 				name, namelen, valuelen);
958a26dc213SDarrick J. Wong 		if (error)
959a26dc213SDarrick J. Wong 			return error;
960a26dc213SDarrick J. Wong 
961a26dc213SDarrick J. Wong 		value = rp->xattr_value;
962a26dc213SDarrick J. Wong 	}
963a26dc213SDarrick J. Wong 
964a26dc213SDarrick J. Wong 	trace_xrep_parent_stash_xattr(rp->sc->tempip, key.flags, (void *)name,
965a26dc213SDarrick J. Wong 			key.namelen, key.valuelen);
966a26dc213SDarrick J. Wong 
967a26dc213SDarrick J. Wong 	error = xfblob_store(rp->xattr_blobs, &key.name_cookie, name,
968a26dc213SDarrick J. Wong 			key.namelen);
969a26dc213SDarrick J. Wong 	if (error)
970a26dc213SDarrick J. Wong 		return error;
971a26dc213SDarrick J. Wong 
972a26dc213SDarrick J. Wong 	error = xfblob_store(rp->xattr_blobs, &key.value_cookie, value,
973a26dc213SDarrick J. Wong 			key.valuelen);
974a26dc213SDarrick J. Wong 	if (error)
975a26dc213SDarrick J. Wong 		return error;
976a26dc213SDarrick J. Wong 
977a26dc213SDarrick J. Wong 	return xfarray_append(rp->xattr_records, &key);
978a26dc213SDarrick J. Wong }
979a26dc213SDarrick J. Wong 
980a26dc213SDarrick J. Wong /* Insert one xattr key/value. */
981a26dc213SDarrick J. Wong STATIC int
982a26dc213SDarrick J. Wong xrep_parent_insert_xattr(
983a26dc213SDarrick J. Wong 	struct xrep_parent		*rp,
984a26dc213SDarrick J. Wong 	const struct xrep_parent_xattr	*key)
985a26dc213SDarrick J. Wong {
986a26dc213SDarrick J. Wong 	struct xfs_da_args		args = {
987a26dc213SDarrick J. Wong 		.dp			= rp->sc->tempip,
988a26dc213SDarrick J. Wong 		.attr_filter		= key->flags,
989a26dc213SDarrick J. Wong 		.namelen		= key->namelen,
990a26dc213SDarrick J. Wong 		.valuelen		= key->valuelen,
991a26dc213SDarrick J. Wong 		.owner			= rp->sc->ip->i_ino,
992a26dc213SDarrick J. Wong 		.geo			= rp->sc->mp->m_attr_geo,
993a26dc213SDarrick J. Wong 		.whichfork		= XFS_ATTR_FORK,
994a26dc213SDarrick J. Wong 		.op_flags		= XFS_DA_OP_OKNOENT,
995a26dc213SDarrick J. Wong 	};
996a26dc213SDarrick J. Wong 	int				error;
997a26dc213SDarrick J. Wong 
998a26dc213SDarrick J. Wong 	ASSERT(!(key->flags & XFS_ATTR_PARENT));
999a26dc213SDarrick J. Wong 
1000a26dc213SDarrick J. Wong 	/*
1001a26dc213SDarrick J. Wong 	 * Grab pointers to the scrub buffer so that we can use them to insert
1002a26dc213SDarrick J. Wong 	 * attrs into the temp file.
1003a26dc213SDarrick J. Wong 	 */
1004a26dc213SDarrick J. Wong 	args.name = rp->xattr_name;
1005a26dc213SDarrick J. Wong 	args.value = rp->xattr_value;
1006a26dc213SDarrick J. Wong 
1007a26dc213SDarrick J. Wong 	/*
1008a26dc213SDarrick J. Wong 	 * The attribute name is stored near the end of the in-core buffer,
1009a26dc213SDarrick J. Wong 	 * though we reserve one more byte to ensure null termination.
1010a26dc213SDarrick J. Wong 	 */
1011a26dc213SDarrick J. Wong 	rp->xattr_name[XATTR_NAME_MAX] = 0;
1012a26dc213SDarrick J. Wong 
1013a26dc213SDarrick J. Wong 	error = xfblob_load(rp->xattr_blobs, key->name_cookie, rp->xattr_name,
1014a26dc213SDarrick J. Wong 			key->namelen);
1015a26dc213SDarrick J. Wong 	if (error)
1016a26dc213SDarrick J. Wong 		return error;
1017a26dc213SDarrick J. Wong 
1018a26dc213SDarrick J. Wong 	error = xfblob_free(rp->xattr_blobs, key->name_cookie);
1019a26dc213SDarrick J. Wong 	if (error)
1020a26dc213SDarrick J. Wong 		return error;
1021a26dc213SDarrick J. Wong 
1022a26dc213SDarrick J. Wong 	error = xfblob_load(rp->xattr_blobs, key->value_cookie, args.value,
1023a26dc213SDarrick J. Wong 			key->valuelen);
1024a26dc213SDarrick J. Wong 	if (error)
1025a26dc213SDarrick J. Wong 		return error;
1026a26dc213SDarrick J. Wong 
1027a26dc213SDarrick J. Wong 	error = xfblob_free(rp->xattr_blobs, key->value_cookie);
1028a26dc213SDarrick J. Wong 	if (error)
1029a26dc213SDarrick J. Wong 		return error;
1030a26dc213SDarrick J. Wong 
1031a26dc213SDarrick J. Wong 	rp->xattr_name[key->namelen] = 0;
1032a26dc213SDarrick J. Wong 
1033a26dc213SDarrick J. Wong 	trace_xrep_parent_insert_xattr(rp->sc->tempip, key->flags,
1034a26dc213SDarrick J. Wong 			rp->xattr_name, key->namelen, key->valuelen);
1035a26dc213SDarrick J. Wong 
1036a26dc213SDarrick J. Wong 	xfs_attr_sethash(&args);
1037a26dc213SDarrick J. Wong 	return xfs_attr_set(&args, XFS_ATTRUPDATE_UPSERT, false);
1038a26dc213SDarrick J. Wong }
1039a26dc213SDarrick J. Wong 
1040a26dc213SDarrick J. Wong /*
1041a26dc213SDarrick J. Wong  * Periodically flush salvaged attributes to the temporary file.  This is done
1042a26dc213SDarrick J. Wong  * to reduce the memory requirements of the xattr rebuild because files can
1043a26dc213SDarrick J. Wong  * contain millions of attributes.
1044a26dc213SDarrick J. Wong  */
1045a26dc213SDarrick J. Wong STATIC int
1046a26dc213SDarrick J. Wong xrep_parent_flush_xattrs(
1047a26dc213SDarrick J. Wong 	struct xrep_parent	*rp)
1048a26dc213SDarrick J. Wong {
1049a26dc213SDarrick J. Wong 	xfarray_idx_t		array_cur;
1050a26dc213SDarrick J. Wong 	int			error;
1051a26dc213SDarrick J. Wong 
1052a26dc213SDarrick J. Wong 	/*
1053a26dc213SDarrick J. Wong 	 * Entering this function, the scrub context has a reference to the
1054a26dc213SDarrick J. Wong 	 * inode being repaired, the temporary file, and the empty scrub
1055a26dc213SDarrick J. Wong 	 * transaction that we created for the xattr scan.  We hold ILOCK_EXCL
1056a26dc213SDarrick J. Wong 	 * on the inode being repaired.
1057a26dc213SDarrick J. Wong 	 *
1058a26dc213SDarrick J. Wong 	 * To constrain kernel memory use, we occasionally flush salvaged
1059a26dc213SDarrick J. Wong 	 * xattrs from the xfarray and xfblob structures into the temporary
1060a26dc213SDarrick J. Wong 	 * file in preparation for exchanging the xattr structures at the end.
1061a26dc213SDarrick J. Wong 	 * Updating the temporary file requires a transaction, so we commit the
1062a26dc213SDarrick J. Wong 	 * scrub transaction and drop the ILOCK so that xfs_attr_set can
1063a26dc213SDarrick J. Wong 	 * allocate whatever transaction it wants.
1064a26dc213SDarrick J. Wong 	 *
1065a26dc213SDarrick J. Wong 	 * We still hold IOLOCK_EXCL on the inode being repaired, which
1066a26dc213SDarrick J. Wong 	 * prevents anyone from adding xattrs (or parent pointers) while we're
1067a26dc213SDarrick J. Wong 	 * flushing.
1068a26dc213SDarrick J. Wong 	 */
1069a26dc213SDarrick J. Wong 	xchk_trans_cancel(rp->sc);
1070a26dc213SDarrick J. Wong 	xchk_iunlock(rp->sc, XFS_ILOCK_EXCL);
1071a26dc213SDarrick J. Wong 
1072a26dc213SDarrick J. Wong 	/*
1073a26dc213SDarrick J. Wong 	 * Take the IOLOCK of the temporary file while we modify xattrs.  This
1074a26dc213SDarrick J. Wong 	 * isn't strictly required because the temporary file is never revealed
1075a26dc213SDarrick J. Wong 	 * to userspace, but we follow the same locking rules.  We still hold
1076a26dc213SDarrick J. Wong 	 * sc->ip's IOLOCK.
1077a26dc213SDarrick J. Wong 	 */
1078a26dc213SDarrick J. Wong 	error = xrep_tempfile_iolock_polled(rp->sc);
1079a26dc213SDarrick J. Wong 	if (error)
1080a26dc213SDarrick J. Wong 		return error;
1081a26dc213SDarrick J. Wong 
1082a26dc213SDarrick J. Wong 	/* Add all the salvaged attrs to the temporary file. */
1083a26dc213SDarrick J. Wong 	foreach_xfarray_idx(rp->xattr_records, array_cur) {
1084a26dc213SDarrick J. Wong 		struct xrep_parent_xattr	key;
1085a26dc213SDarrick J. Wong 
1086a26dc213SDarrick J. Wong 		error = xfarray_load(rp->xattr_records, array_cur, &key);
1087a26dc213SDarrick J. Wong 		if (error)
1088a26dc213SDarrick J. Wong 			return error;
1089a26dc213SDarrick J. Wong 
1090a26dc213SDarrick J. Wong 		error = xrep_parent_insert_xattr(rp, &key);
1091a26dc213SDarrick J. Wong 		if (error)
1092a26dc213SDarrick J. Wong 			return error;
1093a26dc213SDarrick J. Wong 	}
1094a26dc213SDarrick J. Wong 
1095a26dc213SDarrick J. Wong 	/* Empty out both arrays now that we've added the entries. */
1096a26dc213SDarrick J. Wong 	xfarray_truncate(rp->xattr_records);
1097a26dc213SDarrick J. Wong 	xfblob_truncate(rp->xattr_blobs);
1098a26dc213SDarrick J. Wong 
1099a26dc213SDarrick J. Wong 	xrep_tempfile_iounlock(rp->sc);
1100a26dc213SDarrick J. Wong 
1101a26dc213SDarrick J. Wong 	/* Recreate the empty transaction and relock the inode. */
1102a26dc213SDarrick J. Wong 	error = xchk_trans_alloc_empty(rp->sc);
1103a26dc213SDarrick J. Wong 	if (error)
1104a26dc213SDarrick J. Wong 		return error;
1105a26dc213SDarrick J. Wong 	xchk_ilock(rp->sc, XFS_ILOCK_EXCL);
1106a26dc213SDarrick J. Wong 	return 0;
1107a26dc213SDarrick J. Wong }
1108a26dc213SDarrick J. Wong 
1109a26dc213SDarrick J. Wong /* Decide if we've stashed too much xattr data in memory. */
1110a26dc213SDarrick J. Wong static inline bool
1111a26dc213SDarrick J. Wong xrep_parent_want_flush_xattrs(
1112a26dc213SDarrick J. Wong 	struct xrep_parent	*rp)
1113a26dc213SDarrick J. Wong {
1114a26dc213SDarrick J. Wong 	unsigned long long	bytes;
1115a26dc213SDarrick J. Wong 
1116a26dc213SDarrick J. Wong 	bytes = xfarray_bytes(rp->xattr_records) +
1117a26dc213SDarrick J. Wong 		xfblob_bytes(rp->xattr_blobs);
1118a26dc213SDarrick J. Wong 	return bytes > XREP_PARENT_XATTR_MAX_STASH_BYTES;
1119a26dc213SDarrick J. Wong }
1120a26dc213SDarrick J. Wong 
1121a26dc213SDarrick J. Wong /* Flush staged attributes to the temporary file if we're over the limit. */
1122a26dc213SDarrick J. Wong STATIC int
1123a26dc213SDarrick J. Wong xrep_parent_try_flush_xattrs(
1124a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc,
1125a26dc213SDarrick J. Wong 	void			*priv)
1126a26dc213SDarrick J. Wong {
1127a26dc213SDarrick J. Wong 	struct xrep_parent	*rp = priv;
1128a26dc213SDarrick J. Wong 	int			error;
1129a26dc213SDarrick J. Wong 
1130a26dc213SDarrick J. Wong 	if (!xrep_parent_want_flush_xattrs(rp))
1131a26dc213SDarrick J. Wong 		return 0;
1132a26dc213SDarrick J. Wong 
1133a26dc213SDarrick J. Wong 	error = xrep_parent_flush_xattrs(rp);
1134a26dc213SDarrick J. Wong 	if (error)
1135a26dc213SDarrick J. Wong 		return error;
1136a26dc213SDarrick J. Wong 
1137a26dc213SDarrick J. Wong 	/*
1138a26dc213SDarrick J. Wong 	 * If there were any parent pointer updates to the xattr structure
1139a26dc213SDarrick J. Wong 	 * while we dropped the ILOCK, the xattr structure is now stale.
1140a26dc213SDarrick J. Wong 	 * Signal to the attr copy process that we need to start over, but
1141a26dc213SDarrick J. Wong 	 * this time without opportunistic attr flushing.
1142a26dc213SDarrick J. Wong 	 *
1143a26dc213SDarrick J. Wong 	 * This is unlikely to happen, so we're ok with restarting the copy.
1144a26dc213SDarrick J. Wong 	 */
1145a26dc213SDarrick J. Wong 	mutex_lock(&rp->pscan.lock);
1146a26dc213SDarrick J. Wong 	if (rp->saw_pptr_updates)
1147a26dc213SDarrick J. Wong 		error = -ESTALE;
1148a26dc213SDarrick J. Wong 	mutex_unlock(&rp->pscan.lock);
1149a26dc213SDarrick J. Wong 	return error;
1150a26dc213SDarrick J. Wong }
1151a26dc213SDarrick J. Wong 
1152a26dc213SDarrick J. Wong /* Copy all the non-pptr extended attributes into the temporary file. */
1153a26dc213SDarrick J. Wong STATIC int
1154a26dc213SDarrick J. Wong xrep_parent_copy_xattrs(
1155a26dc213SDarrick J. Wong 	struct xrep_parent	*rp)
1156a26dc213SDarrick J. Wong {
1157a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
1158a26dc213SDarrick J. Wong 	int			error;
1159a26dc213SDarrick J. Wong 
1160a26dc213SDarrick J. Wong 	/*
1161a26dc213SDarrick J. Wong 	 * Clear the pptr updates flag.  We hold sc->ip ILOCKed, so there
1162a26dc213SDarrick J. Wong 	 * can't be any parent pointer updates in progress.
1163a26dc213SDarrick J. Wong 	 */
1164a26dc213SDarrick J. Wong 	mutex_lock(&rp->pscan.lock);
1165a26dc213SDarrick J. Wong 	rp->saw_pptr_updates = false;
1166a26dc213SDarrick J. Wong 	mutex_unlock(&rp->pscan.lock);
1167a26dc213SDarrick J. Wong 
1168a26dc213SDarrick J. Wong 	/* Copy xattrs, stopping periodically to flush the incore buffers. */
1169a26dc213SDarrick J. Wong 	error = xchk_xattr_walk(sc, sc->ip, xrep_parent_stash_xattr,
1170a26dc213SDarrick J. Wong 			xrep_parent_try_flush_xattrs, rp);
1171a26dc213SDarrick J. Wong 	if (error && error != -ESTALE)
1172a26dc213SDarrick J. Wong 		return error;
1173a26dc213SDarrick J. Wong 
1174a26dc213SDarrick J. Wong 	if (error == -ESTALE) {
1175a26dc213SDarrick J. Wong 		/*
1176a26dc213SDarrick J. Wong 		 * The xattr copy collided with a parent pointer update.
1177a26dc213SDarrick J. Wong 		 * Restart the copy, but this time hold the ILOCK all the way
1178a26dc213SDarrick J. Wong 		 * to the end to lock out any directory parent pointer updates.
1179a26dc213SDarrick J. Wong 		 */
1180a26dc213SDarrick J. Wong 		error = xchk_xattr_walk(sc, sc->ip, xrep_parent_stash_xattr,
1181a26dc213SDarrick J. Wong 				NULL, rp);
1182a26dc213SDarrick J. Wong 		if (error)
1183a26dc213SDarrick J. Wong 			return error;
1184a26dc213SDarrick J. Wong 	}
1185a26dc213SDarrick J. Wong 
1186a26dc213SDarrick J. Wong 	/* Flush any remaining stashed xattrs to the temporary file. */
1187a26dc213SDarrick J. Wong 	if (xfarray_bytes(rp->xattr_records) == 0)
1188a26dc213SDarrick J. Wong 		return 0;
1189a26dc213SDarrick J. Wong 
1190a26dc213SDarrick J. Wong 	return xrep_parent_flush_xattrs(rp);
1191a26dc213SDarrick J. Wong }
1192a26dc213SDarrick J. Wong 
1193a26dc213SDarrick J. Wong /*
1194a26dc213SDarrick J. Wong  * Ensure that @sc->ip and @sc->tempip both have attribute forks before we head
1195a26dc213SDarrick J. Wong  * into the attr fork exchange transaction.  All files on a filesystem with
1196a26dc213SDarrick J. Wong  * parent pointers must have an attr fork because the parent pointer code does
1197a26dc213SDarrick J. Wong  * not itself add attribute forks.
1198a26dc213SDarrick J. Wong  *
1199a26dc213SDarrick J. Wong  * Note: Unlinkable unlinked files don't need one, but the overhead of having
1200a26dc213SDarrick J. Wong  * an unnecessary attr fork is not justified by the additional code complexity
1201a26dc213SDarrick J. Wong  * that would be needed to track that state correctly.
1202a26dc213SDarrick J. Wong  */
1203a26dc213SDarrick J. Wong STATIC int
1204a26dc213SDarrick J. Wong xrep_parent_ensure_attr_fork(
1205a26dc213SDarrick J. Wong 	struct xrep_parent	*rp)
1206a26dc213SDarrick J. Wong {
1207a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
1208a26dc213SDarrick J. Wong 	int			error;
1209a26dc213SDarrick J. Wong 
1210a26dc213SDarrick J. Wong 	error = xfs_attr_add_fork(sc->tempip,
1211a26dc213SDarrick J. Wong 			sizeof(struct xfs_attr_sf_hdr), 1);
1212a26dc213SDarrick J. Wong 	if (error)
1213a26dc213SDarrick J. Wong 		return error;
1214a26dc213SDarrick J. Wong 	return xfs_attr_add_fork(sc->ip, sizeof(struct xfs_attr_sf_hdr), 1);
1215a26dc213SDarrick J. Wong }
1216a26dc213SDarrick J. Wong 
1217a26dc213SDarrick J. Wong /*
1218a26dc213SDarrick J. Wong  * Finish replaying stashed parent pointer updates, allocate a transaction for
1219a26dc213SDarrick J. Wong  * exchanging extent mappings, and take the ILOCKs of both files before we
1220a26dc213SDarrick J. Wong  * commit the new attribute structure.
1221a26dc213SDarrick J. Wong  */
1222a26dc213SDarrick J. Wong STATIC int
1223a26dc213SDarrick J. Wong xrep_parent_finalize_tempfile(
1224a26dc213SDarrick J. Wong 	struct xrep_parent	*rp)
1225a26dc213SDarrick J. Wong {
1226a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
1227a26dc213SDarrick J. Wong 	int			error;
1228a26dc213SDarrick J. Wong 
1229a26dc213SDarrick J. Wong 	/*
1230a26dc213SDarrick J. Wong 	 * Repair relies on the ILOCK to quiesce all possible xattr updates.
1231a26dc213SDarrick J. Wong 	 * Replay all queued parent pointer updates into the tempfile before
1232a26dc213SDarrick J. Wong 	 * exchanging the contents, even if that means dropping the ILOCKs and
1233a26dc213SDarrick J. Wong 	 * the transaction.
1234a26dc213SDarrick J. Wong 	 */
1235a26dc213SDarrick J. Wong 	do {
1236a26dc213SDarrick J. Wong 		error = xrep_parent_replay_updates(rp);
1237a26dc213SDarrick J. Wong 		if (error)
1238a26dc213SDarrick J. Wong 			return error;
1239a26dc213SDarrick J. Wong 
1240a26dc213SDarrick J. Wong 		error = xrep_parent_ensure_attr_fork(rp);
1241a26dc213SDarrick J. Wong 		if (error)
1242a26dc213SDarrick J. Wong 			return error;
1243a26dc213SDarrick J. Wong 
1244a26dc213SDarrick J. Wong 		error = xrep_tempexch_trans_alloc(sc, XFS_ATTR_FORK, &rp->tx);
1245a26dc213SDarrick J. Wong 		if (error)
1246a26dc213SDarrick J. Wong 			return error;
1247a26dc213SDarrick J. Wong 
1248a26dc213SDarrick J. Wong 		if (xfarray_length(rp->pptr_recs) == 0)
1249a26dc213SDarrick J. Wong 			break;
1250a26dc213SDarrick J. Wong 
1251a26dc213SDarrick J. Wong 		xchk_trans_cancel(sc);
1252a26dc213SDarrick J. Wong 		xrep_tempfile_iunlock_both(sc);
1253a26dc213SDarrick J. Wong 	} while (!xchk_should_terminate(sc, &error));
1254a26dc213SDarrick J. Wong 	return error;
1255a26dc213SDarrick J. Wong }
1256a26dc213SDarrick J. Wong 
1257a26dc213SDarrick J. Wong /*
1258a26dc213SDarrick J. Wong  * Replay all the stashed parent pointers into the temporary file, copy all
1259a26dc213SDarrick J. Wong  * the non-pptr xattrs from the file being repaired into the temporary file,
1260a26dc213SDarrick J. Wong  * and exchange the attr fork contents atomically.
1261a26dc213SDarrick J. Wong  */
1262a26dc213SDarrick J. Wong STATIC int
1263a26dc213SDarrick J. Wong xrep_parent_rebuild_pptrs(
1264a26dc213SDarrick J. Wong 	struct xrep_parent	*rp)
1265a26dc213SDarrick J. Wong {
1266a26dc213SDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
1267a26dc213SDarrick J. Wong 	xfs_ino_t		parent_ino = NULLFSINO;
1268a26dc213SDarrick J. Wong 	int			error;
1269a26dc213SDarrick J. Wong 
1270a26dc213SDarrick J. Wong 	/*
1271a26dc213SDarrick J. Wong 	 * Copy non-ppttr xattrs from the file being repaired into the
1272a26dc213SDarrick J. Wong 	 * temporary file's xattr structure.  We hold sc->ip's IOLOCK, which
1273a26dc213SDarrick J. Wong 	 * prevents setxattr/removexattr calls from occurring, but renames
1274a26dc213SDarrick J. Wong 	 * update the parent pointers without holding IOLOCK.  If we detect
1275a26dc213SDarrick J. Wong 	 * stale attr structures, we restart the scan but only flush at the
1276a26dc213SDarrick J. Wong 	 * end.
1277a26dc213SDarrick J. Wong 	 */
1278a26dc213SDarrick J. Wong 	error = xrep_parent_copy_xattrs(rp);
1279a26dc213SDarrick J. Wong 	if (error)
1280a26dc213SDarrick J. Wong 		return error;
1281a26dc213SDarrick J. Wong 
1282a26dc213SDarrick J. Wong 	/*
1283a26dc213SDarrick J. Wong 	 * Cancel the empty transaction that we used to walk and copy attrs,
1284a26dc213SDarrick J. Wong 	 * and drop the ILOCK so that we can take the IOLOCK on the temporary
1285a26dc213SDarrick J. Wong 	 * file.  We still hold sc->ip's IOLOCK.
1286a26dc213SDarrick J. Wong 	 */
1287a26dc213SDarrick J. Wong 	xchk_trans_cancel(sc);
1288a26dc213SDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
1289a26dc213SDarrick J. Wong 
1290a26dc213SDarrick J. Wong 	error = xrep_tempfile_iolock_polled(sc);
1291a26dc213SDarrick J. Wong 	if (error)
1292a26dc213SDarrick J. Wong 		return error;
1293a26dc213SDarrick J. Wong 
1294a26dc213SDarrick J. Wong 	/*
1295a26dc213SDarrick J. Wong 	 * Allocate transaction, lock inodes, and make sure that we've replayed
1296a26dc213SDarrick J. Wong 	 * all the stashed pptr updates to the tempdir.  After this point,
1297a26dc213SDarrick J. Wong 	 * we're ready to exchange the attr fork mappings.
1298a26dc213SDarrick J. Wong 	 */
1299a26dc213SDarrick J. Wong 	error = xrep_parent_finalize_tempfile(rp);
1300a26dc213SDarrick J. Wong 	if (error)
1301a26dc213SDarrick J. Wong 		return error;
1302a26dc213SDarrick J. Wong 
1303a26dc213SDarrick J. Wong 	/* Last chance to abort before we start committing pptr fixes. */
1304a26dc213SDarrick J. Wong 	if (xchk_should_terminate(sc, &error))
1305a26dc213SDarrick J. Wong 		return error;
1306a26dc213SDarrick J. Wong 
1307a26dc213SDarrick J. Wong 	if (xchk_iscan_aborted(&rp->pscan.iscan))
1308a26dc213SDarrick J. Wong 		return -ECANCELED;
1309a26dc213SDarrick J. Wong 
1310a26dc213SDarrick J. Wong 	/*
1311a26dc213SDarrick J. Wong 	 * Exchange the attr fork contents and junk the old attr fork contents,
1312a26dc213SDarrick J. Wong 	 * which are now in the tempfile.
1313a26dc213SDarrick J. Wong 	 */
1314a26dc213SDarrick J. Wong 	error = xrep_xattr_swap(sc, &rp->tx);
1315a26dc213SDarrick J. Wong 	if (error)
1316a26dc213SDarrick J. Wong 		return error;
1317a26dc213SDarrick J. Wong 	error = xrep_xattr_reset_tempfile_fork(sc);
1318a26dc213SDarrick J. Wong 	if (error)
1319a26dc213SDarrick J. Wong 		return error;
1320a26dc213SDarrick J. Wong 
1321a26dc213SDarrick J. Wong 	/*
1322a26dc213SDarrick J. Wong 	 * Roll to get a transaction without any inodes joined to it.  Then we
1323a26dc213SDarrick J. Wong 	 * can drop the tempfile's ILOCK and IOLOCK before doing more work on
1324a26dc213SDarrick J. Wong 	 * the scrub target file.
1325a26dc213SDarrick J. Wong 	 */
1326a26dc213SDarrick J. Wong 	error = xfs_trans_roll(&sc->tp);
1327a26dc213SDarrick J. Wong 	if (error)
1328a26dc213SDarrick J. Wong 		return error;
1329a26dc213SDarrick J. Wong 	xrep_tempfile_iunlock(sc);
1330a26dc213SDarrick J. Wong 	xrep_tempfile_iounlock(sc);
1331a26dc213SDarrick J. Wong 
1332a26dc213SDarrick J. Wong 	/*
1333a26dc213SDarrick J. Wong 	 * We've committed the new parent pointers.  Find at least one parent
1334a26dc213SDarrick J. Wong 	 * so that we can decide if we're moving this file to the orphanage.
1335a26dc213SDarrick J. Wong 	 * For this purpose, root directories are their own parents.
1336a26dc213SDarrick J. Wong 	 */
1337679b098bSDarrick J. Wong 	if (xchk_inode_is_dirtree_root(sc->ip)) {
1338a26dc213SDarrick J. Wong 		xrep_findparent_scan_found(&rp->pscan, sc->ip->i_ino);
1339a26dc213SDarrick J. Wong 	} else {
1340a26dc213SDarrick J. Wong 		error = xrep_parent_lookup_pptrs(sc, &parent_ino);
1341a26dc213SDarrick J. Wong 		if (error)
1342a26dc213SDarrick J. Wong 			return error;
1343a26dc213SDarrick J. Wong 		if (parent_ino != NULLFSINO)
1344a26dc213SDarrick J. Wong 			xrep_findparent_scan_found(&rp->pscan, parent_ino);
1345a26dc213SDarrick J. Wong 	}
1346a26dc213SDarrick J. Wong 	return 0;
1347a26dc213SDarrick J. Wong }
1348a26dc213SDarrick J. Wong 
13491e58a8ccSDarrick J. Wong /*
1350cc22edabSDarrick J. Wong  * Commit the new parent pointer structure (currently only the dotdot entry) to
1351cc22edabSDarrick J. Wong  * the file that we're repairing.
1352cc22edabSDarrick J. Wong  */
1353cc22edabSDarrick J. Wong STATIC int
1354cc22edabSDarrick J. Wong xrep_parent_rebuild_tree(
1355cc22edabSDarrick J. Wong 	struct xrep_parent	*rp)
1356cc22edabSDarrick J. Wong {
1357*aec2eb7dSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
1358*aec2eb7dSDarrick J. Wong 	bool			try_adoption;
1359a26dc213SDarrick J. Wong 	int			error;
1360a26dc213SDarrick J. Wong 
1361*aec2eb7dSDarrick J. Wong 	if (xfs_has_parent(sc->mp)) {
1362a26dc213SDarrick J. Wong 		error = xrep_parent_rebuild_pptrs(rp);
1363a26dc213SDarrick J. Wong 		if (error)
1364a26dc213SDarrick J. Wong 			return error;
1365a26dc213SDarrick J. Wong 	}
1366a26dc213SDarrick J. Wong 
1367*aec2eb7dSDarrick J. Wong 	/*
1368*aec2eb7dSDarrick J. Wong 	 * Any file with no parent could be adopted.  This check happens after
1369*aec2eb7dSDarrick J. Wong 	 * rebuilding the parent pointer structure because we might have cycled
1370*aec2eb7dSDarrick J. Wong 	 * the ILOCK during that process.
1371*aec2eb7dSDarrick J. Wong 	 */
1372*aec2eb7dSDarrick J. Wong 	try_adoption = rp->pscan.parent_ino == NULLFSINO;
1373*aec2eb7dSDarrick J. Wong 
1374*aec2eb7dSDarrick J. Wong 	/*
1375*aec2eb7dSDarrick J. Wong 	 * Starting with metadir, we allow checking of parent pointers
1376*aec2eb7dSDarrick J. Wong 	 * of non-directory files that are children of the superblock.
1377*aec2eb7dSDarrick J. Wong 	 * Lack of parent is ok here.
1378*aec2eb7dSDarrick J. Wong 	 */
1379*aec2eb7dSDarrick J. Wong 	if (try_adoption && xfs_has_metadir(sc->mp) &&
1380*aec2eb7dSDarrick J. Wong 	    xchk_inode_is_sb_rooted(sc->ip))
1381*aec2eb7dSDarrick J. Wong 		try_adoption = false;
1382*aec2eb7dSDarrick J. Wong 
1383*aec2eb7dSDarrick J. Wong 	if (try_adoption) {
1384*aec2eb7dSDarrick J. Wong 		if (xrep_orphanage_can_adopt(sc))
13851e58a8ccSDarrick J. Wong 			return xrep_parent_move_to_orphanage(rp);
1386cc22edabSDarrick J. Wong 		return -EFSCORRUPTED;
1387*aec2eb7dSDarrick J. Wong 
1388cc22edabSDarrick J. Wong 	}
1389cc22edabSDarrick J. Wong 
1390*aec2eb7dSDarrick J. Wong 	if (S_ISDIR(VFS_I(sc->ip)->i_mode))
1391cc22edabSDarrick J. Wong 		return xrep_parent_reset_dotdot(rp);
1392a26dc213SDarrick J. Wong 
1393a26dc213SDarrick J. Wong 	return 0;
1394cc22edabSDarrick J. Wong }
1395cc22edabSDarrick J. Wong 
13963f50ddbfSDarrick J. Wong /* Count the number of parent pointers. */
13973f50ddbfSDarrick J. Wong STATIC int
13983f50ddbfSDarrick J. Wong xrep_parent_count_pptr(
13993f50ddbfSDarrick J. Wong 	struct xfs_scrub	*sc,
14003f50ddbfSDarrick J. Wong 	struct xfs_inode	*ip,
14013f50ddbfSDarrick J. Wong 	unsigned int		attr_flags,
14023f50ddbfSDarrick J. Wong 	const unsigned char	*name,
14033f50ddbfSDarrick J. Wong 	unsigned int		namelen,
14043f50ddbfSDarrick J. Wong 	const void		*value,
14053f50ddbfSDarrick J. Wong 	unsigned int		valuelen,
14063f50ddbfSDarrick J. Wong 	void			*priv)
14073f50ddbfSDarrick J. Wong {
14083f50ddbfSDarrick J. Wong 	struct xrep_parent	*rp = priv;
14093f50ddbfSDarrick J. Wong 	int			error;
14103f50ddbfSDarrick J. Wong 
14113f50ddbfSDarrick J. Wong 	if (!(attr_flags & XFS_ATTR_PARENT))
14123f50ddbfSDarrick J. Wong 		return 0;
14133f50ddbfSDarrick J. Wong 
14143f50ddbfSDarrick J. Wong 	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,
14153f50ddbfSDarrick J. Wong 			valuelen, NULL, NULL);
14163f50ddbfSDarrick J. Wong 	if (error)
14173f50ddbfSDarrick J. Wong 		return error;
14183f50ddbfSDarrick J. Wong 
14193f50ddbfSDarrick J. Wong 	rp->parents++;
14203f50ddbfSDarrick J. Wong 	return 0;
14213f50ddbfSDarrick J. Wong }
14223f50ddbfSDarrick J. Wong 
14233f50ddbfSDarrick J. Wong /*
14243f50ddbfSDarrick J. Wong  * After all parent pointer rebuilding and adoption activity completes, reset
14253f50ddbfSDarrick J. Wong  * the link count of this nondirectory, having scanned the fs to rebuild all
14263f50ddbfSDarrick J. Wong  * parent pointers.
14273f50ddbfSDarrick J. Wong  */
14283f50ddbfSDarrick J. Wong STATIC int
14293f50ddbfSDarrick J. Wong xrep_parent_set_nondir_nlink(
14303f50ddbfSDarrick J. Wong 	struct xrep_parent	*rp)
14313f50ddbfSDarrick J. Wong {
14323f50ddbfSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
14333f50ddbfSDarrick J. Wong 	struct xfs_inode	*ip = sc->ip;
14343f50ddbfSDarrick J. Wong 	struct xfs_perag	*pag;
14353f50ddbfSDarrick J. Wong 	bool			joined = false;
14363f50ddbfSDarrick J. Wong 	int			error;
14373f50ddbfSDarrick J. Wong 
14383f50ddbfSDarrick J. Wong 	/* Count parent pointers so we can reset the file link count. */
14393f50ddbfSDarrick J. Wong 	rp->parents = 0;
14403f50ddbfSDarrick J. Wong 	error = xchk_xattr_walk(sc, ip, xrep_parent_count_pptr, NULL, rp);
14413f50ddbfSDarrick J. Wong 	if (error)
14423f50ddbfSDarrick J. Wong 		return error;
14433f50ddbfSDarrick J. Wong 
1444*aec2eb7dSDarrick J. Wong 	/*
1445*aec2eb7dSDarrick J. Wong 	 * Starting with metadir, we allow checking of parent pointers of
1446*aec2eb7dSDarrick J. Wong 	 * non-directory files that are children of the superblock.  Pretend
1447*aec2eb7dSDarrick J. Wong 	 * that we found a parent pointer attr.
1448*aec2eb7dSDarrick J. Wong 	 */
1449*aec2eb7dSDarrick J. Wong 	if (xfs_has_metadir(sc->mp) && xchk_inode_is_sb_rooted(sc->ip))
1450*aec2eb7dSDarrick J. Wong 		rp->parents++;
1451*aec2eb7dSDarrick J. Wong 
14523f50ddbfSDarrick J. Wong 	if (rp->parents > 0 && xfs_inode_on_unlinked_list(ip)) {
14533f50ddbfSDarrick J. Wong 		xfs_trans_ijoin(sc->tp, sc->ip, 0);
14543f50ddbfSDarrick J. Wong 		joined = true;
14553f50ddbfSDarrick J. Wong 
14563f50ddbfSDarrick J. Wong 		/*
14573f50ddbfSDarrick J. Wong 		 * The file is on the unlinked list but we found parents.
14583f50ddbfSDarrick J. Wong 		 * Remove the file from the unlinked list.
14593f50ddbfSDarrick J. Wong 		 */
14603f50ddbfSDarrick J. Wong 		pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, ip->i_ino));
14613f50ddbfSDarrick J. Wong 		if (!pag) {
14623f50ddbfSDarrick J. Wong 			ASSERT(0);
14633f50ddbfSDarrick J. Wong 			return -EFSCORRUPTED;
14643f50ddbfSDarrick J. Wong 		}
14653f50ddbfSDarrick J. Wong 
14663f50ddbfSDarrick J. Wong 		error = xfs_iunlink_remove(sc->tp, pag, ip);
14673f50ddbfSDarrick J. Wong 		xfs_perag_put(pag);
14683f50ddbfSDarrick J. Wong 		if (error)
14693f50ddbfSDarrick J. Wong 			return error;
14703f50ddbfSDarrick J. Wong 	} else if (rp->parents == 0 && !xfs_inode_on_unlinked_list(ip)) {
14713f50ddbfSDarrick J. Wong 		xfs_trans_ijoin(sc->tp, sc->ip, 0);
14723f50ddbfSDarrick J. Wong 		joined = true;
14733f50ddbfSDarrick J. Wong 
14743f50ddbfSDarrick J. Wong 		/*
14753f50ddbfSDarrick J. Wong 		 * The file is not on the unlinked list but we found no
14763f50ddbfSDarrick J. Wong 		 * parents.  Add the file to the unlinked list.
14773f50ddbfSDarrick J. Wong 		 */
14783f50ddbfSDarrick J. Wong 		error = xfs_iunlink(sc->tp, ip);
14793f50ddbfSDarrick J. Wong 		if (error)
14803f50ddbfSDarrick J. Wong 			return error;
14813f50ddbfSDarrick J. Wong 	}
14823f50ddbfSDarrick J. Wong 
14833f50ddbfSDarrick J. Wong 	/* Set the correct link count. */
14843f50ddbfSDarrick J. Wong 	if (VFS_I(ip)->i_nlink != rp->parents) {
14853f50ddbfSDarrick J. Wong 		if (!joined) {
14863f50ddbfSDarrick J. Wong 			xfs_trans_ijoin(sc->tp, sc->ip, 0);
14873f50ddbfSDarrick J. Wong 			joined = true;
14883f50ddbfSDarrick J. Wong 		}
14893f50ddbfSDarrick J. Wong 
14903f50ddbfSDarrick J. Wong 		set_nlink(VFS_I(ip), min_t(unsigned long long, rp->parents,
14913f50ddbfSDarrick J. Wong 					   XFS_NLINK_PINNED));
14923f50ddbfSDarrick J. Wong 	}
14933f50ddbfSDarrick J. Wong 
14943f50ddbfSDarrick J. Wong 	/* Log the inode to keep it moving forward if we dirtied anything. */
14953f50ddbfSDarrick J. Wong 	if (joined)
14963f50ddbfSDarrick J. Wong 		xfs_trans_log_inode(sc->tp, ip, XFS_ILOG_CORE);
14973f50ddbfSDarrick J. Wong 	return 0;
14983f50ddbfSDarrick J. Wong }
14993f50ddbfSDarrick J. Wong 
1500cc22edabSDarrick J. Wong /* Set up the filesystem scan so we can look for parents. */
1501cc22edabSDarrick J. Wong STATIC int
1502cc22edabSDarrick J. Wong xrep_parent_setup_scan(
1503cc22edabSDarrick J. Wong 	struct xrep_parent	*rp)
1504cc22edabSDarrick J. Wong {
1505cc22edabSDarrick J. Wong 	struct xfs_scrub	*sc = rp->sc;
1506b334f7faSDarrick J. Wong 	char			*descr;
1507a26dc213SDarrick J. Wong 	struct xfs_da_geometry	*geo = sc->mp->m_attr_geo;
1508a26dc213SDarrick J. Wong 	int			max_len;
1509b334f7faSDarrick J. Wong 	int			error;
1510cc22edabSDarrick J. Wong 
1511b334f7faSDarrick J. Wong 	if (!xfs_has_parent(sc->mp))
1512cc22edabSDarrick J. Wong 		return xrep_findparent_scan_start(sc, &rp->pscan);
1513b334f7faSDarrick J. Wong 
1514a26dc213SDarrick J. Wong 	/* Buffers for copying non-pptr attrs to the tempfile */
1515a26dc213SDarrick J. Wong 	rp->xattr_name = kvmalloc(XATTR_NAME_MAX + 1, XCHK_GFP_FLAGS);
1516a26dc213SDarrick J. Wong 	if (!rp->xattr_name)
1517a26dc213SDarrick J. Wong 		return -ENOMEM;
1518a26dc213SDarrick J. Wong 
1519a26dc213SDarrick J. Wong 	/*
1520a26dc213SDarrick J. Wong 	 * Allocate enough memory to handle loading local attr values from the
1521a26dc213SDarrick J. Wong 	 * xfblob data while flushing stashed attrs to the temporary file.
1522a26dc213SDarrick J. Wong 	 * We only realloc the buffer when salvaging remote attr values, so
1523a26dc213SDarrick J. Wong 	 * TRY_HARDER means we allocate the maximal attr value size.
1524a26dc213SDarrick J. Wong 	 */
1525a26dc213SDarrick J. Wong 	if (sc->flags & XCHK_TRY_HARDER)
1526a26dc213SDarrick J. Wong 		max_len = XATTR_SIZE_MAX;
1527a26dc213SDarrick J. Wong 	else
1528a26dc213SDarrick J. Wong 		max_len = xfs_attr_leaf_entsize_local_max(geo->blksize);
1529a26dc213SDarrick J. Wong 	error = xrep_parent_alloc_xattr_value(rp, max_len);
1530a26dc213SDarrick J. Wong 	if (error)
1531a26dc213SDarrick J. Wong 		goto out_xattr_name;
1532a26dc213SDarrick J. Wong 
1533b334f7faSDarrick J. Wong 	/* Set up some staging memory for logging parent pointer updates. */
1534b334f7faSDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc, "parent pointer entries");
1535b334f7faSDarrick J. Wong 	error = xfarray_create(descr, 0, sizeof(struct xrep_pptr),
1536b334f7faSDarrick J. Wong 			&rp->pptr_recs);
1537b334f7faSDarrick J. Wong 	kfree(descr);
1538b334f7faSDarrick J. Wong 	if (error)
1539a26dc213SDarrick J. Wong 		goto out_xattr_value;
1540b334f7faSDarrick J. Wong 
1541b334f7faSDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc, "parent pointer names");
1542b334f7faSDarrick J. Wong 	error = xfblob_create(descr, &rp->pptr_names);
1543b334f7faSDarrick J. Wong 	kfree(descr);
1544b334f7faSDarrick J. Wong 	if (error)
1545b334f7faSDarrick J. Wong 		goto out_recs;
1546b334f7faSDarrick J. Wong 
1547a26dc213SDarrick J. Wong 	/* Set up some storage for copying attrs before the mapping exchange */
1548a26dc213SDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc,
1549a26dc213SDarrick J. Wong 				"parent pointer retained xattr entries");
1550a26dc213SDarrick J. Wong 	error = xfarray_create(descr, 0, sizeof(struct xrep_parent_xattr),
1551a26dc213SDarrick J. Wong 			&rp->xattr_records);
1552a26dc213SDarrick J. Wong 	kfree(descr);
1553b334f7faSDarrick J. Wong 	if (error)
1554b334f7faSDarrick J. Wong 		goto out_names;
1555b334f7faSDarrick J. Wong 
1556a26dc213SDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc,
1557a26dc213SDarrick J. Wong 				"parent pointer retained xattr values");
1558a26dc213SDarrick J. Wong 	error = xfblob_create(descr, &rp->xattr_blobs);
1559a26dc213SDarrick J. Wong 	kfree(descr);
1560a26dc213SDarrick J. Wong 	if (error)
1561a26dc213SDarrick J. Wong 		goto out_attr_keys;
1562a26dc213SDarrick J. Wong 
1563a26dc213SDarrick J. Wong 	error = __xrep_findparent_scan_start(sc, &rp->pscan,
1564a26dc213SDarrick J. Wong 			xrep_parent_live_update);
1565a26dc213SDarrick J. Wong 	if (error)
1566a26dc213SDarrick J. Wong 		goto out_attr_values;
1567a26dc213SDarrick J. Wong 
1568b334f7faSDarrick J. Wong 	return 0;
1569b334f7faSDarrick J. Wong 
1570a26dc213SDarrick J. Wong out_attr_values:
1571a26dc213SDarrick J. Wong 	xfblob_destroy(rp->xattr_blobs);
1572a26dc213SDarrick J. Wong 	rp->xattr_blobs = NULL;
1573a26dc213SDarrick J. Wong out_attr_keys:
1574a26dc213SDarrick J. Wong 	xfarray_destroy(rp->xattr_records);
1575a26dc213SDarrick J. Wong 	rp->xattr_records = NULL;
1576b334f7faSDarrick J. Wong out_names:
1577b334f7faSDarrick J. Wong 	xfblob_destroy(rp->pptr_names);
1578b334f7faSDarrick J. Wong 	rp->pptr_names = NULL;
1579b334f7faSDarrick J. Wong out_recs:
1580b334f7faSDarrick J. Wong 	xfarray_destroy(rp->pptr_recs);
1581b334f7faSDarrick J. Wong 	rp->pptr_recs = NULL;
1582a26dc213SDarrick J. Wong out_xattr_value:
1583a26dc213SDarrick J. Wong 	kvfree(rp->xattr_value);
1584a26dc213SDarrick J. Wong 	rp->xattr_value = NULL;
1585a26dc213SDarrick J. Wong out_xattr_name:
1586a26dc213SDarrick J. Wong 	kvfree(rp->xattr_name);
1587a26dc213SDarrick J. Wong 	rp->xattr_name = NULL;
1588b334f7faSDarrick J. Wong 	return error;
1589cc22edabSDarrick J. Wong }
1590cc22edabSDarrick J. Wong 
1591cc22edabSDarrick J. Wong int
1592cc22edabSDarrick J. Wong xrep_parent(
1593cc22edabSDarrick J. Wong 	struct xfs_scrub	*sc)
1594cc22edabSDarrick J. Wong {
1595cc22edabSDarrick J. Wong 	struct xrep_parent	*rp = sc->buf;
1596cc22edabSDarrick J. Wong 	int			error;
1597cc22edabSDarrick J. Wong 
1598b334f7faSDarrick J. Wong 	/*
1599b334f7faSDarrick J. Wong 	 * When the parent pointers feature is enabled, repairs are committed
1600b334f7faSDarrick J. Wong 	 * by atomically committing a new xattr structure and reaping the old
16016d335233SDarrick J. Wong 	 * attr fork.  Reaping requires rmap and exchange-range to be enabled.
1602b334f7faSDarrick J. Wong 	 */
16036d335233SDarrick J. Wong 	if (xfs_has_parent(sc->mp)) {
16046d335233SDarrick J. Wong 		if (!xfs_has_rmapbt(sc->mp))
1605b334f7faSDarrick J. Wong 			return -EOPNOTSUPP;
16066d335233SDarrick J. Wong 		if (!xfs_has_exchange_range(sc->mp))
16076d335233SDarrick J. Wong 			return -EOPNOTSUPP;
16086d335233SDarrick J. Wong 	}
1609b334f7faSDarrick J. Wong 
1610cc22edabSDarrick J. Wong 	error = xrep_parent_setup_scan(rp);
1611cc22edabSDarrick J. Wong 	if (error)
1612cc22edabSDarrick J. Wong 		return error;
1613cc22edabSDarrick J. Wong 
1614b334f7faSDarrick J. Wong 	if (xfs_has_parent(sc->mp))
1615b334f7faSDarrick J. Wong 		error = xrep_parent_scan_dirtree(rp);
1616b334f7faSDarrick J. Wong 	else
1617cc22edabSDarrick J. Wong 		error = xrep_parent_find_dotdot(rp);
1618cc22edabSDarrick J. Wong 	if (error)
1619cc22edabSDarrick J. Wong 		goto out_teardown;
1620cc22edabSDarrick J. Wong 
1621a26dc213SDarrick J. Wong 	/* Last chance to abort before we start committing dotdot fixes. */
1622cc22edabSDarrick J. Wong 	if (xchk_should_terminate(sc, &error))
1623cc22edabSDarrick J. Wong 		goto out_teardown;
1624cc22edabSDarrick J. Wong 
1625cc22edabSDarrick J. Wong 	error = xrep_parent_rebuild_tree(rp);
1626cc22edabSDarrick J. Wong 	if (error)
1627cc22edabSDarrick J. Wong 		goto out_teardown;
16283f50ddbfSDarrick J. Wong 	if (xfs_has_parent(sc->mp) && !S_ISDIR(VFS_I(sc->ip)->i_mode)) {
16293f50ddbfSDarrick J. Wong 		error = xrep_parent_set_nondir_nlink(rp);
16303f50ddbfSDarrick J. Wong 		if (error)
16313f50ddbfSDarrick J. Wong 			goto out_teardown;
16323f50ddbfSDarrick J. Wong 	}
16333f50ddbfSDarrick J. Wong 
16343f50ddbfSDarrick J. Wong 	error = xrep_defer_finish(sc);
1635cc22edabSDarrick J. Wong 
1636cc22edabSDarrick J. Wong out_teardown:
1637cc22edabSDarrick J. Wong 	xrep_parent_teardown(rp);
1638cc22edabSDarrick J. Wong 	return error;
1639cc22edabSDarrick J. Wong }
1640