xref: /linux/fs/xfs/scrub/attr_repair.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1e47dcf11SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
2e47dcf11SDarrick J. Wong /*
3e47dcf11SDarrick J. Wong  * Copyright (c) 2018-2024 Oracle.  All Rights Reserved.
4e47dcf11SDarrick J. Wong  * Author: Darrick J. Wong <djwong@kernel.org>
5e47dcf11SDarrick J. Wong  */
6e47dcf11SDarrick J. Wong #include "xfs.h"
7e47dcf11SDarrick J. Wong #include "xfs_fs.h"
8e47dcf11SDarrick J. Wong #include "xfs_shared.h"
9e47dcf11SDarrick J. Wong #include "xfs_format.h"
10e47dcf11SDarrick J. Wong #include "xfs_trans_resv.h"
11e47dcf11SDarrick J. Wong #include "xfs_mount.h"
12e47dcf11SDarrick J. Wong #include "xfs_defer.h"
13e47dcf11SDarrick J. Wong #include "xfs_btree.h"
14e47dcf11SDarrick J. Wong #include "xfs_bit.h"
15e47dcf11SDarrick J. Wong #include "xfs_log_format.h"
16e47dcf11SDarrick J. Wong #include "xfs_trans.h"
17e47dcf11SDarrick J. Wong #include "xfs_sb.h"
18e47dcf11SDarrick J. Wong #include "xfs_inode.h"
19e47dcf11SDarrick J. Wong #include "xfs_da_format.h"
20e47dcf11SDarrick J. Wong #include "xfs_da_btree.h"
21e47dcf11SDarrick J. Wong #include "xfs_dir2.h"
22e47dcf11SDarrick J. Wong #include "xfs_attr.h"
23e47dcf11SDarrick J. Wong #include "xfs_attr_leaf.h"
24e47dcf11SDarrick J. Wong #include "xfs_attr_sf.h"
25e47dcf11SDarrick J. Wong #include "xfs_attr_remote.h"
26e47dcf11SDarrick J. Wong #include "xfs_bmap.h"
27e47dcf11SDarrick J. Wong #include "xfs_bmap_util.h"
28e47dcf11SDarrick J. Wong #include "xfs_exchmaps.h"
29e47dcf11SDarrick J. Wong #include "xfs_exchrange.h"
30e47dcf11SDarrick J. Wong #include "xfs_acl.h"
31086e934fSDarrick J. Wong #include "xfs_parent.h"
32e47dcf11SDarrick J. Wong #include "scrub/xfs_scrub.h"
33e47dcf11SDarrick J. Wong #include "scrub/scrub.h"
34e47dcf11SDarrick J. Wong #include "scrub/common.h"
35e47dcf11SDarrick J. Wong #include "scrub/trace.h"
36e47dcf11SDarrick J. Wong #include "scrub/repair.h"
37e47dcf11SDarrick J. Wong #include "scrub/tempfile.h"
38e47dcf11SDarrick J. Wong #include "scrub/tempexch.h"
39e47dcf11SDarrick J. Wong #include "scrub/xfile.h"
40e47dcf11SDarrick J. Wong #include "scrub/xfarray.h"
41e47dcf11SDarrick J. Wong #include "scrub/xfblob.h"
42e47dcf11SDarrick J. Wong #include "scrub/attr.h"
43e47dcf11SDarrick J. Wong #include "scrub/reap.h"
44e47dcf11SDarrick J. Wong #include "scrub/attr_repair.h"
45e47dcf11SDarrick J. Wong 
46e47dcf11SDarrick J. Wong /*
47e47dcf11SDarrick J. Wong  * Extended Attribute Repair
48e47dcf11SDarrick J. Wong  * =========================
49e47dcf11SDarrick J. Wong  *
50e47dcf11SDarrick J. Wong  * We repair extended attributes by reading the attr leaf blocks looking for
51e47dcf11SDarrick J. Wong  * attributes entries that look salvageable (name passes verifiers, value can
52e47dcf11SDarrick J. Wong  * be retrieved, etc).  Each extended attribute worth salvaging is stashed in
53e47dcf11SDarrick J. Wong  * memory, and the stashed entries are periodically replayed into a temporary
54e47dcf11SDarrick J. Wong  * file to constrain memory use.  Batching the construction of the temporary
55e47dcf11SDarrick J. Wong  * extended attribute structure in this fashion reduces lock cycling of the
56e47dcf11SDarrick J. Wong  * file being repaired and the temporary file.
57e47dcf11SDarrick J. Wong  *
58e47dcf11SDarrick J. Wong  * When salvaging completes, the remaining stashed attributes are replayed to
59e47dcf11SDarrick J. Wong  * the temporary file.  An atomic file contents exchange is used to commit the
60e47dcf11SDarrick J. Wong  * new xattr blocks to the file being repaired.  This will disrupt attrmulti
61e47dcf11SDarrick J. Wong  * cursors.
62e47dcf11SDarrick J. Wong  */
63e47dcf11SDarrick J. Wong 
64e47dcf11SDarrick J. Wong struct xrep_xattr_key {
65e47dcf11SDarrick J. Wong 	/* Cookie for retrieval of the xattr name. */
66e47dcf11SDarrick J. Wong 	xfblob_cookie		name_cookie;
67e47dcf11SDarrick J. Wong 
68e47dcf11SDarrick J. Wong 	/* Cookie for retrieval of the xattr value. */
69e47dcf11SDarrick J. Wong 	xfblob_cookie		value_cookie;
70e47dcf11SDarrick J. Wong 
71e47dcf11SDarrick J. Wong 	/* XFS_ATTR_* flags */
72e47dcf11SDarrick J. Wong 	int			flags;
73e47dcf11SDarrick J. Wong 
74e47dcf11SDarrick J. Wong 	/* Length of the value and name. */
75e47dcf11SDarrick J. Wong 	uint32_t		valuelen;
76e47dcf11SDarrick J. Wong 	uint16_t		namelen;
77e47dcf11SDarrick J. Wong };
78e47dcf11SDarrick J. Wong 
79e47dcf11SDarrick J. Wong /*
80e47dcf11SDarrick J. Wong  * Stash up to 8 pages of attrs in xattr_records/xattr_blobs before we write
81e47dcf11SDarrick J. Wong  * them to the temp file.
82e47dcf11SDarrick J. Wong  */
83e47dcf11SDarrick J. Wong #define XREP_XATTR_MAX_STASH_BYTES	(PAGE_SIZE * 8)
84e47dcf11SDarrick J. Wong 
85e47dcf11SDarrick J. Wong struct xrep_xattr {
86e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc;
87e47dcf11SDarrick J. Wong 
88e47dcf11SDarrick J. Wong 	/* Information for exchanging attr fork mappings at the end. */
89e47dcf11SDarrick J. Wong 	struct xrep_tempexch	tx;
90e47dcf11SDarrick J. Wong 
91e47dcf11SDarrick J. Wong 	/* xattr keys */
92e47dcf11SDarrick J. Wong 	struct xfarray		*xattr_records;
93e47dcf11SDarrick J. Wong 
94e47dcf11SDarrick J. Wong 	/* xattr values */
95e47dcf11SDarrick J. Wong 	struct xfblob		*xattr_blobs;
96e47dcf11SDarrick J. Wong 
97e47dcf11SDarrick J. Wong 	/* Number of attributes that we are salvaging. */
98e47dcf11SDarrick J. Wong 	unsigned long long	attrs_found;
99e5d7ce03SDarrick J. Wong 
100e5d7ce03SDarrick J. Wong 	/* Can we flush stashed attrs to the tempfile? */
101e5d7ce03SDarrick J. Wong 	bool			can_flush;
102e5d7ce03SDarrick J. Wong 
103e5d7ce03SDarrick J. Wong 	/* Did the live update fail, and hence the repair is now out of date? */
104e5d7ce03SDarrick J. Wong 	bool			live_update_aborted;
105e5d7ce03SDarrick J. Wong 
106e5d7ce03SDarrick J. Wong 	/* Lock protecting parent pointer updates */
107e5d7ce03SDarrick J. Wong 	struct mutex		lock;
108e5d7ce03SDarrick J. Wong 
109e5d7ce03SDarrick J. Wong 	/* Fixed-size array of xrep_xattr_pptr structures. */
110e5d7ce03SDarrick J. Wong 	struct xfarray		*pptr_recs;
111e5d7ce03SDarrick J. Wong 
112e5d7ce03SDarrick J. Wong 	/* Blobs containing parent pointer names. */
113e5d7ce03SDarrick J. Wong 	struct xfblob		*pptr_names;
114e5d7ce03SDarrick J. Wong 
115e5d7ce03SDarrick J. Wong 	/* Hook to capture parent pointer updates. */
116e5d7ce03SDarrick J. Wong 	struct xfs_dir_hook	dhook;
117e5d7ce03SDarrick J. Wong 
118e5d7ce03SDarrick J. Wong 	/* Scratch buffer for capturing parent pointers. */
119e5d7ce03SDarrick J. Wong 	struct xfs_da_args	pptr_args;
120e5d7ce03SDarrick J. Wong 
121e5d7ce03SDarrick J. Wong 	/* Name buffer */
122e5d7ce03SDarrick J. Wong 	struct xfs_name		xname;
123e5d7ce03SDarrick J. Wong 	char			namebuf[MAXNAMELEN];
124e5d7ce03SDarrick J. Wong };
125e5d7ce03SDarrick J. Wong 
126e5d7ce03SDarrick J. Wong /* Create a parent pointer in the tempfile. */
127e5d7ce03SDarrick J. Wong #define XREP_XATTR_PPTR_ADD	(1)
128e5d7ce03SDarrick J. Wong 
129e5d7ce03SDarrick J. Wong /* Remove a parent pointer from the tempfile. */
130e5d7ce03SDarrick J. Wong #define XREP_XATTR_PPTR_REMOVE	(2)
131e5d7ce03SDarrick J. Wong 
132e5d7ce03SDarrick J. Wong /* A stashed parent pointer update. */
133e5d7ce03SDarrick J. Wong struct xrep_xattr_pptr {
134e5d7ce03SDarrick J. Wong 	/* Cookie for retrieval of the pptr name. */
135e5d7ce03SDarrick J. Wong 	xfblob_cookie		name_cookie;
136e5d7ce03SDarrick J. Wong 
137e5d7ce03SDarrick J. Wong 	/* Parent pointer record. */
138e5d7ce03SDarrick J. Wong 	struct xfs_parent_rec	pptr_rec;
139e5d7ce03SDarrick J. Wong 
140e5d7ce03SDarrick J. Wong 	/* Length of the pptr name. */
141e5d7ce03SDarrick J. Wong 	uint8_t			namelen;
142e5d7ce03SDarrick J. Wong 
143e5d7ce03SDarrick J. Wong 	/* XREP_XATTR_PPTR_{ADD,REMOVE} */
144e5d7ce03SDarrick J. Wong 	uint8_t			action;
145e47dcf11SDarrick J. Wong };
146e47dcf11SDarrick J. Wong 
147e47dcf11SDarrick J. Wong /* Set up to recreate the extended attributes. */
148e47dcf11SDarrick J. Wong int
149e47dcf11SDarrick J. Wong xrep_setup_xattr(
150e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc)
151e47dcf11SDarrick J. Wong {
152e5d7ce03SDarrick J. Wong 	if (xfs_has_parent(sc->mp))
153e5d7ce03SDarrick J. Wong 		xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
154e5d7ce03SDarrick J. Wong 
155e47dcf11SDarrick J. Wong 	return xrep_tempfile_create(sc, S_IFREG);
156e47dcf11SDarrick J. Wong }
157e47dcf11SDarrick J. Wong 
158e47dcf11SDarrick J. Wong /*
159e47dcf11SDarrick J. Wong  * Decide if we want to salvage this attribute.  We don't bother with
160e47dcf11SDarrick J. Wong  * incomplete or oversized keys or values.  The @value parameter can be null
161e47dcf11SDarrick J. Wong  * for remote attrs.
162e47dcf11SDarrick J. Wong  */
163e47dcf11SDarrick J. Wong STATIC int
164e47dcf11SDarrick J. Wong xrep_xattr_want_salvage(
165e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx,
166e47dcf11SDarrick J. Wong 	unsigned int		attr_flags,
167e47dcf11SDarrick J. Wong 	const void		*name,
168e47dcf11SDarrick J. Wong 	int			namelen,
169e47dcf11SDarrick J. Wong 	const void		*value,
170e47dcf11SDarrick J. Wong 	int			valuelen)
171e47dcf11SDarrick J. Wong {
172e47dcf11SDarrick J. Wong 	if (attr_flags & XFS_ATTR_INCOMPLETE)
173e47dcf11SDarrick J. Wong 		return false;
174e47dcf11SDarrick J. Wong 	if (namelen > XATTR_NAME_MAX || namelen <= 0)
175e47dcf11SDarrick J. Wong 		return false;
176ea0b3e81SDarrick J. Wong 	if (!xfs_attr_namecheck(attr_flags, name, namelen))
177e47dcf11SDarrick J. Wong 		return false;
178e47dcf11SDarrick J. Wong 	if (valuelen > XATTR_SIZE_MAX || valuelen < 0)
179e47dcf11SDarrick J. Wong 		return false;
180086e934fSDarrick J. Wong 	if (attr_flags & XFS_ATTR_PARENT)
181086e934fSDarrick J. Wong 		return xfs_parent_valuecheck(rx->sc->mp, value, valuelen);
182086e934fSDarrick J. Wong 
183e47dcf11SDarrick J. Wong 	return true;
184e47dcf11SDarrick J. Wong }
185e47dcf11SDarrick J. Wong 
186e47dcf11SDarrick J. Wong /* Allocate an in-core record to hold xattrs while we rebuild the xattr data. */
187e47dcf11SDarrick J. Wong STATIC int
188e47dcf11SDarrick J. Wong xrep_xattr_salvage_key(
189e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx,
190e47dcf11SDarrick J. Wong 	int			flags,
191e47dcf11SDarrick J. Wong 	unsigned char		*name,
192e47dcf11SDarrick J. Wong 	int			namelen,
193e47dcf11SDarrick J. Wong 	unsigned char		*value,
194e47dcf11SDarrick J. Wong 	int			valuelen)
195e47dcf11SDarrick J. Wong {
196e47dcf11SDarrick J. Wong 	struct xrep_xattr_key	key = {
197e47dcf11SDarrick J. Wong 		.valuelen	= valuelen,
198e47dcf11SDarrick J. Wong 		.flags		= flags & XFS_ATTR_NSP_ONDISK_MASK,
199e47dcf11SDarrick J. Wong 	};
200e47dcf11SDarrick J. Wong 	unsigned int		i = 0;
201e47dcf11SDarrick J. Wong 	int			error = 0;
202e47dcf11SDarrick J. Wong 
203e47dcf11SDarrick J. Wong 	if (xchk_should_terminate(rx->sc, &error))
204e47dcf11SDarrick J. Wong 		return error;
205e47dcf11SDarrick J. Wong 
206e47dcf11SDarrick J. Wong 	/*
207e47dcf11SDarrick J. Wong 	 * Truncate the name to the first character that would trip namecheck.
208e47dcf11SDarrick J. Wong 	 * If we no longer have a name after that, ignore this attribute.
209e47dcf11SDarrick J. Wong 	 */
210086e934fSDarrick J. Wong 	if (flags & XFS_ATTR_PARENT) {
211086e934fSDarrick J. Wong 		key.namelen = namelen;
212086e934fSDarrick J. Wong 
213086e934fSDarrick J. Wong 		trace_xrep_xattr_salvage_pptr(rx->sc->ip, flags, name,
214086e934fSDarrick J. Wong 				key.namelen, value, valuelen);
215086e934fSDarrick J. Wong 	} else {
216e47dcf11SDarrick J. Wong 		while (i < namelen && name[i] != 0)
217e47dcf11SDarrick J. Wong 			i++;
218e47dcf11SDarrick J. Wong 		if (i == 0)
219e47dcf11SDarrick J. Wong 			return 0;
220e47dcf11SDarrick J. Wong 		key.namelen = i;
221e47dcf11SDarrick J. Wong 
222086e934fSDarrick J. Wong 		trace_xrep_xattr_salvage_rec(rx->sc->ip, flags, name,
223086e934fSDarrick J. Wong 				key.namelen, valuelen);
224086e934fSDarrick J. Wong 	}
225e47dcf11SDarrick J. Wong 
226e47dcf11SDarrick J. Wong 	error = xfblob_store(rx->xattr_blobs, &key.name_cookie, name,
227e47dcf11SDarrick J. Wong 			key.namelen);
228e47dcf11SDarrick J. Wong 	if (error)
229e47dcf11SDarrick J. Wong 		return error;
230e47dcf11SDarrick J. Wong 
231e47dcf11SDarrick J. Wong 	error = xfblob_store(rx->xattr_blobs, &key.value_cookie, value,
232e47dcf11SDarrick J. Wong 			key.valuelen);
233e47dcf11SDarrick J. Wong 	if (error)
234e47dcf11SDarrick J. Wong 		return error;
235e47dcf11SDarrick J. Wong 
236e47dcf11SDarrick J. Wong 	error = xfarray_append(rx->xattr_records, &key);
237e47dcf11SDarrick J. Wong 	if (error)
238e47dcf11SDarrick J. Wong 		return error;
239e47dcf11SDarrick J. Wong 
240e47dcf11SDarrick J. Wong 	rx->attrs_found++;
241e47dcf11SDarrick J. Wong 	return 0;
242e47dcf11SDarrick J. Wong }
243e47dcf11SDarrick J. Wong 
244e47dcf11SDarrick J. Wong /*
245e47dcf11SDarrick J. Wong  * Record a shortform extended attribute key & value for later reinsertion
246e47dcf11SDarrick J. Wong  * into the inode.
247e47dcf11SDarrick J. Wong  */
248e47dcf11SDarrick J. Wong STATIC int
249e47dcf11SDarrick J. Wong xrep_xattr_salvage_sf_attr(
250e47dcf11SDarrick J. Wong 	struct xrep_xattr		*rx,
251e47dcf11SDarrick J. Wong 	struct xfs_attr_sf_hdr		*hdr,
252e47dcf11SDarrick J. Wong 	struct xfs_attr_sf_entry	*sfe)
253e47dcf11SDarrick J. Wong {
254e47dcf11SDarrick J. Wong 	struct xfs_scrub		*sc = rx->sc;
255e47dcf11SDarrick J. Wong 	struct xchk_xattr_buf		*ab = sc->buf;
256e47dcf11SDarrick J. Wong 	unsigned char			*name = sfe->nameval;
257e47dcf11SDarrick J. Wong 	unsigned char			*value = &sfe->nameval[sfe->namelen];
258e47dcf11SDarrick J. Wong 
259e47dcf11SDarrick J. Wong 	if (!xchk_xattr_set_map(sc, ab->usedmap, (char *)name - (char *)hdr,
260e47dcf11SDarrick J. Wong 			sfe->namelen))
261e47dcf11SDarrick J. Wong 		return 0;
262e47dcf11SDarrick J. Wong 
263e47dcf11SDarrick J. Wong 	if (!xchk_xattr_set_map(sc, ab->usedmap, (char *)value - (char *)hdr,
264e47dcf11SDarrick J. Wong 			sfe->valuelen))
265e47dcf11SDarrick J. Wong 		return 0;
266e47dcf11SDarrick J. Wong 
267e47dcf11SDarrick J. Wong 	if (!xrep_xattr_want_salvage(rx, sfe->flags, sfe->nameval,
268e47dcf11SDarrick J. Wong 			sfe->namelen, value, sfe->valuelen))
269e47dcf11SDarrick J. Wong 		return 0;
270e47dcf11SDarrick J. Wong 
271e47dcf11SDarrick J. Wong 	return xrep_xattr_salvage_key(rx, sfe->flags, sfe->nameval,
272e47dcf11SDarrick J. Wong 			sfe->namelen, value, sfe->valuelen);
273e47dcf11SDarrick J. Wong }
274e47dcf11SDarrick J. Wong 
275e47dcf11SDarrick J. Wong /*
276e47dcf11SDarrick J. Wong  * Record a local format extended attribute key & value for later reinsertion
277e47dcf11SDarrick J. Wong  * into the inode.
278e47dcf11SDarrick J. Wong  */
279e47dcf11SDarrick J. Wong STATIC int
280e47dcf11SDarrick J. Wong xrep_xattr_salvage_local_attr(
281e47dcf11SDarrick J. Wong 	struct xrep_xattr		*rx,
282e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_entry	*ent,
283e47dcf11SDarrick J. Wong 	unsigned int			nameidx,
284e47dcf11SDarrick J. Wong 	const char			*buf_end,
285e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_name_local	*lentry)
286e47dcf11SDarrick J. Wong {
287e47dcf11SDarrick J. Wong 	struct xchk_xattr_buf		*ab = rx->sc->buf;
288e47dcf11SDarrick J. Wong 	unsigned char			*value;
289e47dcf11SDarrick J. Wong 	unsigned int			valuelen;
290e47dcf11SDarrick J. Wong 	unsigned int			namesize;
291e47dcf11SDarrick J. Wong 
292e47dcf11SDarrick J. Wong 	/*
293e47dcf11SDarrick J. Wong 	 * Decode the leaf local entry format.  If something seems wrong, we
294e47dcf11SDarrick J. Wong 	 * junk the attribute.
295e47dcf11SDarrick J. Wong 	 */
296e47dcf11SDarrick J. Wong 	value = &lentry->nameval[lentry->namelen];
297e47dcf11SDarrick J. Wong 	valuelen = be16_to_cpu(lentry->valuelen);
298e47dcf11SDarrick J. Wong 	namesize = xfs_attr_leaf_entsize_local(lentry->namelen, valuelen);
299e47dcf11SDarrick J. Wong 	if ((char *)lentry + namesize > buf_end)
300e47dcf11SDarrick J. Wong 		return 0;
301e47dcf11SDarrick J. Wong 	if (!xrep_xattr_want_salvage(rx, ent->flags, lentry->nameval,
302e47dcf11SDarrick J. Wong 			lentry->namelen, value, valuelen))
303e47dcf11SDarrick J. Wong 		return 0;
304e47dcf11SDarrick J. Wong 	if (!xchk_xattr_set_map(rx->sc, ab->usedmap, nameidx, namesize))
305e47dcf11SDarrick J. Wong 		return 0;
306e47dcf11SDarrick J. Wong 
307e47dcf11SDarrick J. Wong 	/* Try to save this attribute. */
308e47dcf11SDarrick J. Wong 	return xrep_xattr_salvage_key(rx, ent->flags, lentry->nameval,
309e47dcf11SDarrick J. Wong 			lentry->namelen, value, valuelen);
310e47dcf11SDarrick J. Wong }
311e47dcf11SDarrick J. Wong 
312e47dcf11SDarrick J. Wong /*
313e47dcf11SDarrick J. Wong  * Record a remote format extended attribute key & value for later reinsertion
314e47dcf11SDarrick J. Wong  * into the inode.
315e47dcf11SDarrick J. Wong  */
316e47dcf11SDarrick J. Wong STATIC int
317e47dcf11SDarrick J. Wong xrep_xattr_salvage_remote_attr(
318e47dcf11SDarrick J. Wong 	struct xrep_xattr		*rx,
319e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_entry	*ent,
320e47dcf11SDarrick J. Wong 	unsigned int			nameidx,
321e47dcf11SDarrick J. Wong 	const char			*buf_end,
322e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_name_remote *rentry,
323e47dcf11SDarrick J. Wong 	unsigned int			ent_idx,
324e47dcf11SDarrick J. Wong 	struct xfs_buf			*leaf_bp)
325e47dcf11SDarrick J. Wong {
326e47dcf11SDarrick J. Wong 	struct xchk_xattr_buf		*ab = rx->sc->buf;
327e47dcf11SDarrick J. Wong 	struct xfs_da_args		args = {
328e47dcf11SDarrick J. Wong 		.trans			= rx->sc->tp,
329e47dcf11SDarrick J. Wong 		.dp			= rx->sc->ip,
330e47dcf11SDarrick J. Wong 		.index			= ent_idx,
331e47dcf11SDarrick J. Wong 		.geo			= rx->sc->mp->m_attr_geo,
332e47dcf11SDarrick J. Wong 		.owner			= rx->sc->ip->i_ino,
333e47dcf11SDarrick J. Wong 		.attr_filter		= ent->flags & XFS_ATTR_NSP_ONDISK_MASK,
334e47dcf11SDarrick J. Wong 		.namelen		= rentry->namelen,
335e47dcf11SDarrick J. Wong 		.name			= rentry->name,
336e47dcf11SDarrick J. Wong 		.value			= ab->value,
337e47dcf11SDarrick J. Wong 		.valuelen		= be32_to_cpu(rentry->valuelen),
338e47dcf11SDarrick J. Wong 	};
339e47dcf11SDarrick J. Wong 	unsigned int			namesize;
340e47dcf11SDarrick J. Wong 	int				error;
341e47dcf11SDarrick J. Wong 
342e47dcf11SDarrick J. Wong 	/*
343e47dcf11SDarrick J. Wong 	 * Decode the leaf remote entry format.  If something seems wrong, we
344e47dcf11SDarrick J. Wong 	 * junk the attribute.  Note that we should never find a zero-length
345e47dcf11SDarrick J. Wong 	 * remote attribute value.
346e47dcf11SDarrick J. Wong 	 */
347e47dcf11SDarrick J. Wong 	namesize = xfs_attr_leaf_entsize_remote(rentry->namelen);
348e47dcf11SDarrick J. Wong 	if ((char *)rentry + namesize > buf_end)
349e47dcf11SDarrick J. Wong 		return 0;
350e47dcf11SDarrick J. Wong 	if (args.valuelen == 0 ||
351e47dcf11SDarrick J. Wong 	    !xrep_xattr_want_salvage(rx, ent->flags, rentry->name,
352e47dcf11SDarrick J. Wong 			rentry->namelen, NULL, args.valuelen))
353e47dcf11SDarrick J. Wong 		return 0;
354e47dcf11SDarrick J. Wong 	if (!xchk_xattr_set_map(rx->sc, ab->usedmap, nameidx, namesize))
355e47dcf11SDarrick J. Wong 		return 0;
356e47dcf11SDarrick J. Wong 
357e47dcf11SDarrick J. Wong 	/*
358e47dcf11SDarrick J. Wong 	 * Enlarge the buffer (if needed) to hold the value that we're trying
359e47dcf11SDarrick J. Wong 	 * to salvage from the old extended attribute data.
360e47dcf11SDarrick J. Wong 	 */
361e47dcf11SDarrick J. Wong 	error = xchk_setup_xattr_buf(rx->sc, args.valuelen);
362e47dcf11SDarrick J. Wong 	if (error == -ENOMEM)
363e47dcf11SDarrick J. Wong 		error = -EDEADLOCK;
364e47dcf11SDarrick J. Wong 	if (error)
365e47dcf11SDarrick J. Wong 		return error;
366e47dcf11SDarrick J. Wong 
367e47dcf11SDarrick J. Wong 	/* Look up the remote value and stash it for reconstruction. */
368e47dcf11SDarrick J. Wong 	error = xfs_attr3_leaf_getvalue(leaf_bp, &args);
369e47dcf11SDarrick J. Wong 	if (error || args.rmtblkno == 0)
370e47dcf11SDarrick J. Wong 		goto err_free;
371e47dcf11SDarrick J. Wong 
372e47dcf11SDarrick J. Wong 	error = xfs_attr_rmtval_get(&args);
373e47dcf11SDarrick J. Wong 	if (error)
374e47dcf11SDarrick J. Wong 		goto err_free;
375e47dcf11SDarrick J. Wong 
376e47dcf11SDarrick J. Wong 	/* Try to save this attribute. */
377e47dcf11SDarrick J. Wong 	error = xrep_xattr_salvage_key(rx, ent->flags, rentry->name,
378e47dcf11SDarrick J. Wong 			rentry->namelen, ab->value, args.valuelen);
379e47dcf11SDarrick J. Wong err_free:
380e47dcf11SDarrick J. Wong 	/* remote value was garbage, junk it */
381e47dcf11SDarrick J. Wong 	if (error == -EFSBADCRC || error == -EFSCORRUPTED)
382e47dcf11SDarrick J. Wong 		error = 0;
383e47dcf11SDarrick J. Wong 	return error;
384e47dcf11SDarrick J. Wong }
385e47dcf11SDarrick J. Wong 
386e47dcf11SDarrick J. Wong /* Extract every xattr key that we can from this attr fork block. */
387e47dcf11SDarrick J. Wong STATIC int
388e47dcf11SDarrick J. Wong xrep_xattr_recover_leaf(
389e47dcf11SDarrick J. Wong 	struct xrep_xattr		*rx,
390e47dcf11SDarrick J. Wong 	struct xfs_buf			*bp)
391e47dcf11SDarrick J. Wong {
392e47dcf11SDarrick J. Wong 	struct xfs_attr3_icleaf_hdr	leafhdr;
393e47dcf11SDarrick J. Wong 	struct xfs_scrub		*sc = rx->sc;
394e47dcf11SDarrick J. Wong 	struct xfs_mount		*mp = sc->mp;
395e47dcf11SDarrick J. Wong 	struct xfs_attr_leafblock	*leaf;
396e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_name_local	*lentry;
397e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_name_remote *rentry;
398e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_entry	*ent;
399e47dcf11SDarrick J. Wong 	struct xfs_attr_leaf_entry	*entries;
400e47dcf11SDarrick J. Wong 	struct xchk_xattr_buf		*ab = rx->sc->buf;
401e47dcf11SDarrick J. Wong 	char				*buf_end;
402e47dcf11SDarrick J. Wong 	size_t				off;
403e47dcf11SDarrick J. Wong 	unsigned int			nameidx;
404e47dcf11SDarrick J. Wong 	unsigned int			hdrsize;
405e47dcf11SDarrick J. Wong 	int				i;
406e47dcf11SDarrick J. Wong 	int				error = 0;
407e47dcf11SDarrick J. Wong 
408e47dcf11SDarrick J. Wong 	bitmap_zero(ab->usedmap, mp->m_attr_geo->blksize);
409e47dcf11SDarrick J. Wong 
410e47dcf11SDarrick J. Wong 	/* Check the leaf header */
411e47dcf11SDarrick J. Wong 	leaf = bp->b_addr;
412e47dcf11SDarrick J. Wong 	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
413e47dcf11SDarrick J. Wong 	hdrsize = xfs_attr3_leaf_hdr_size(leaf);
414e47dcf11SDarrick J. Wong 	xchk_xattr_set_map(sc, ab->usedmap, 0, hdrsize);
415e47dcf11SDarrick J. Wong 	entries = xfs_attr3_leaf_entryp(leaf);
416e47dcf11SDarrick J. Wong 
417e47dcf11SDarrick J. Wong 	buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize;
418e47dcf11SDarrick J. Wong 	for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) {
419e47dcf11SDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
420e47dcf11SDarrick J. Wong 			return error;
421e47dcf11SDarrick J. Wong 
422e47dcf11SDarrick J. Wong 		/* Skip key if it conflicts with something else? */
423e47dcf11SDarrick J. Wong 		off = (char *)ent - (char *)leaf;
424e47dcf11SDarrick J. Wong 		if (!xchk_xattr_set_map(sc, ab->usedmap, off,
425e47dcf11SDarrick J. Wong 				sizeof(xfs_attr_leaf_entry_t)))
426e47dcf11SDarrick J. Wong 			continue;
427e47dcf11SDarrick J. Wong 
428e47dcf11SDarrick J. Wong 		/* Check the name information. */
429e47dcf11SDarrick J. Wong 		nameidx = be16_to_cpu(ent->nameidx);
430e47dcf11SDarrick J. Wong 		if (nameidx < leafhdr.firstused ||
431e47dcf11SDarrick J. Wong 		    nameidx >= mp->m_attr_geo->blksize)
432e47dcf11SDarrick J. Wong 			continue;
433e47dcf11SDarrick J. Wong 
434e47dcf11SDarrick J. Wong 		if (ent->flags & XFS_ATTR_LOCAL) {
435e47dcf11SDarrick J. Wong 			lentry = xfs_attr3_leaf_name_local(leaf, i);
436e47dcf11SDarrick J. Wong 			error = xrep_xattr_salvage_local_attr(rx, ent, nameidx,
437e47dcf11SDarrick J. Wong 					buf_end, lentry);
438e47dcf11SDarrick J. Wong 		} else {
439e47dcf11SDarrick J. Wong 			rentry = xfs_attr3_leaf_name_remote(leaf, i);
440e47dcf11SDarrick J. Wong 			error = xrep_xattr_salvage_remote_attr(rx, ent, nameidx,
441e47dcf11SDarrick J. Wong 					buf_end, rentry, i, bp);
442e47dcf11SDarrick J. Wong 		}
443e47dcf11SDarrick J. Wong 		if (error)
444e47dcf11SDarrick J. Wong 			return error;
445e47dcf11SDarrick J. Wong 	}
446e47dcf11SDarrick J. Wong 
447e47dcf11SDarrick J. Wong 	return 0;
448e47dcf11SDarrick J. Wong }
449e47dcf11SDarrick J. Wong 
450e47dcf11SDarrick J. Wong /* Try to recover shortform attrs. */
451e47dcf11SDarrick J. Wong STATIC int
452e47dcf11SDarrick J. Wong xrep_xattr_recover_sf(
453e47dcf11SDarrick J. Wong 	struct xrep_xattr		*rx)
454e47dcf11SDarrick J. Wong {
455e47dcf11SDarrick J. Wong 	struct xfs_scrub		*sc = rx->sc;
456e47dcf11SDarrick J. Wong 	struct xchk_xattr_buf		*ab = sc->buf;
457e47dcf11SDarrick J. Wong 	struct xfs_attr_sf_hdr		*hdr;
458e47dcf11SDarrick J. Wong 	struct xfs_attr_sf_entry	*sfe;
459e47dcf11SDarrick J. Wong 	struct xfs_attr_sf_entry	*next;
460e47dcf11SDarrick J. Wong 	struct xfs_ifork		*ifp;
461e47dcf11SDarrick J. Wong 	unsigned char			*end;
462e47dcf11SDarrick J. Wong 	int				i;
463e47dcf11SDarrick J. Wong 	int				error = 0;
464e47dcf11SDarrick J. Wong 
465e47dcf11SDarrick J. Wong 	ifp = xfs_ifork_ptr(rx->sc->ip, XFS_ATTR_FORK);
466e47dcf11SDarrick J. Wong 	hdr = ifp->if_data;
467e47dcf11SDarrick J. Wong 
468e47dcf11SDarrick J. Wong 	bitmap_zero(ab->usedmap, ifp->if_bytes);
469e47dcf11SDarrick J. Wong 	end = (unsigned char *)ifp->if_data + ifp->if_bytes;
470e47dcf11SDarrick J. Wong 	xchk_xattr_set_map(sc, ab->usedmap, 0, sizeof(*hdr));
471e47dcf11SDarrick J. Wong 
472e47dcf11SDarrick J. Wong 	sfe = xfs_attr_sf_firstentry(hdr);
473e47dcf11SDarrick J. Wong 	if ((unsigned char *)sfe > end)
474e47dcf11SDarrick J. Wong 		return 0;
475e47dcf11SDarrick J. Wong 
476e47dcf11SDarrick J. Wong 	for (i = 0; i < hdr->count; i++) {
477e47dcf11SDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
478e47dcf11SDarrick J. Wong 			return error;
479e47dcf11SDarrick J. Wong 
480e47dcf11SDarrick J. Wong 		next = xfs_attr_sf_nextentry(sfe);
481e47dcf11SDarrick J. Wong 		if ((unsigned char *)next > end)
482e47dcf11SDarrick J. Wong 			break;
483e47dcf11SDarrick J. Wong 
484e47dcf11SDarrick J. Wong 		if (xchk_xattr_set_map(sc, ab->usedmap,
485e47dcf11SDarrick J. Wong 				(char *)sfe - (char *)hdr,
486e47dcf11SDarrick J. Wong 				sizeof(struct xfs_attr_sf_entry))) {
487e47dcf11SDarrick J. Wong 			/*
488e47dcf11SDarrick J. Wong 			 * No conflicts with the sf entry; let's save this
489e47dcf11SDarrick J. Wong 			 * attribute.
490e47dcf11SDarrick J. Wong 			 */
491e47dcf11SDarrick J. Wong 			error = xrep_xattr_salvage_sf_attr(rx, hdr, sfe);
492e47dcf11SDarrick J. Wong 			if (error)
493e47dcf11SDarrick J. Wong 				return error;
494e47dcf11SDarrick J. Wong 		}
495e47dcf11SDarrick J. Wong 
496e47dcf11SDarrick J. Wong 		sfe = next;
497e47dcf11SDarrick J. Wong 	}
498e47dcf11SDarrick J. Wong 
499e47dcf11SDarrick J. Wong 	return 0;
500e47dcf11SDarrick J. Wong }
501e47dcf11SDarrick J. Wong 
502e47dcf11SDarrick J. Wong /*
503e47dcf11SDarrick J. Wong  * Try to return a buffer of xattr data for a given physical extent.
504e47dcf11SDarrick J. Wong  *
505e47dcf11SDarrick J. Wong  * Because the buffer cache get function complains if it finds a buffer
506e47dcf11SDarrick J. Wong  * matching the block number but not matching the length, we must be careful to
507e47dcf11SDarrick J. Wong  * look for incore buffers (up to the maximum length of a remote value) that
508e47dcf11SDarrick J. Wong  * could be hiding anywhere in the physical range.  If we find an incore
509e47dcf11SDarrick J. Wong  * buffer, we can pass that to the caller.  Optionally, read a single block and
510e47dcf11SDarrick J. Wong  * pass that back.
511e47dcf11SDarrick J. Wong  *
512e47dcf11SDarrick J. Wong  * Note the subtlety that remote attr value blocks for which there is no incore
513e47dcf11SDarrick J. Wong  * buffer will be passed to the callback one block at a time.  These buffers
514e47dcf11SDarrick J. Wong  * will not have any ops attached and must be staled to prevent aliasing with
515e47dcf11SDarrick J. Wong  * multiblock buffers once we drop the ILOCK.
516e47dcf11SDarrick J. Wong  */
517e47dcf11SDarrick J. Wong STATIC int
518e47dcf11SDarrick J. Wong xrep_xattr_find_buf(
519e47dcf11SDarrick J. Wong 	struct xfs_mount	*mp,
520e47dcf11SDarrick J. Wong 	xfs_fsblock_t		fsbno,
521e47dcf11SDarrick J. Wong 	xfs_extlen_t		max_len,
522e47dcf11SDarrick J. Wong 	bool			can_read,
523e47dcf11SDarrick J. Wong 	struct xfs_buf		**bpp)
524e47dcf11SDarrick J. Wong {
525e47dcf11SDarrick J. Wong 	struct xrep_bufscan	scan = {
526e47dcf11SDarrick J. Wong 		.daddr		= XFS_FSB_TO_DADDR(mp, fsbno),
527e47dcf11SDarrick J. Wong 		.max_sectors	= xrep_bufscan_max_sectors(mp, max_len),
528e47dcf11SDarrick J. Wong 		.daddr_step	= XFS_FSB_TO_BB(mp, 1),
529e47dcf11SDarrick J. Wong 	};
530e47dcf11SDarrick J. Wong 	struct xfs_buf		*bp;
531e47dcf11SDarrick J. Wong 
532e47dcf11SDarrick J. Wong 	while ((bp = xrep_bufscan_advance(mp, &scan)) != NULL) {
533e47dcf11SDarrick J. Wong 		*bpp = bp;
534e47dcf11SDarrick J. Wong 		return 0;
535e47dcf11SDarrick J. Wong 	}
536e47dcf11SDarrick J. Wong 
537e47dcf11SDarrick J. Wong 	if (!can_read) {
538e47dcf11SDarrick J. Wong 		*bpp = NULL;
539e47dcf11SDarrick J. Wong 		return 0;
540e47dcf11SDarrick J. Wong 	}
541e47dcf11SDarrick J. Wong 
542e47dcf11SDarrick J. Wong 	return xfs_buf_read(mp->m_ddev_targp, scan.daddr, XFS_FSB_TO_BB(mp, 1),
543e47dcf11SDarrick J. Wong 			XBF_TRYLOCK, bpp, NULL);
544e47dcf11SDarrick J. Wong }
545e47dcf11SDarrick J. Wong 
546e47dcf11SDarrick J. Wong /*
547e47dcf11SDarrick J. Wong  * Deal with a buffer that we found during our walk of the attr fork.
548e47dcf11SDarrick J. Wong  *
549e47dcf11SDarrick J. Wong  * Attribute leaf and node blocks are simple -- they're a single block, so we
550e47dcf11SDarrick J. Wong  * can walk them one at a time and we never have to worry about discontiguous
551e47dcf11SDarrick J. Wong  * multiblock buffers like we do for directories.
552e47dcf11SDarrick J. Wong  *
553e47dcf11SDarrick J. Wong  * Unfortunately, remote attr blocks add a lot of complexity here.  Each disk
554e47dcf11SDarrick J. Wong  * block is totally self contained, in the sense that the v5 header provides no
555e47dcf11SDarrick J. Wong  * indication that there could be more data in the next block.  The incore
556e47dcf11SDarrick J. Wong  * buffers can span multiple blocks, though they never cross extent records.
557e47dcf11SDarrick J. Wong  * However, they don't necessarily start or end on an extent record boundary.
558e47dcf11SDarrick J. Wong  * Therefore, we need a special buffer find function to walk the buffer cache
559e47dcf11SDarrick J. Wong  * for us.
560e47dcf11SDarrick J. Wong  *
561e47dcf11SDarrick J. Wong  * The caller must hold the ILOCK on the file being repaired.  We use
562e47dcf11SDarrick J. Wong  * XBF_TRYLOCK here to skip any locked buffer on the assumption that we don't
563e47dcf11SDarrick J. Wong  * own the block and don't want to hang the system on a potentially garbage
564e47dcf11SDarrick J. Wong  * buffer.
565e47dcf11SDarrick J. Wong  */
566e47dcf11SDarrick J. Wong STATIC int
567e47dcf11SDarrick J. Wong xrep_xattr_recover_block(
568e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx,
569e47dcf11SDarrick J. Wong 	xfs_dablk_t		dabno,
570e47dcf11SDarrick J. Wong 	xfs_fsblock_t		fsbno,
571e47dcf11SDarrick J. Wong 	xfs_extlen_t		max_len,
572e47dcf11SDarrick J. Wong 	xfs_extlen_t		*actual_len)
573e47dcf11SDarrick J. Wong {
574e47dcf11SDarrick J. Wong 	struct xfs_da_blkinfo	*info;
575e47dcf11SDarrick J. Wong 	struct xfs_buf		*bp;
576e47dcf11SDarrick J. Wong 	int			error;
577e47dcf11SDarrick J. Wong 
578e47dcf11SDarrick J. Wong 	error = xrep_xattr_find_buf(rx->sc->mp, fsbno, max_len, true, &bp);
579e47dcf11SDarrick J. Wong 	if (error)
580e47dcf11SDarrick J. Wong 		return error;
581e47dcf11SDarrick J. Wong 	info = bp->b_addr;
582e47dcf11SDarrick J. Wong 	*actual_len = XFS_BB_TO_FSB(rx->sc->mp, bp->b_length);
583e47dcf11SDarrick J. Wong 
584e47dcf11SDarrick J. Wong 	trace_xrep_xattr_recover_leafblock(rx->sc->ip, dabno,
585e47dcf11SDarrick J. Wong 			be16_to_cpu(info->magic));
586e47dcf11SDarrick J. Wong 
587e47dcf11SDarrick J. Wong 	/*
588e47dcf11SDarrick J. Wong 	 * If the buffer has the right magic number for an attr leaf block and
589e47dcf11SDarrick J. Wong 	 * passes a structure check (we don't care about checksums), salvage
590e47dcf11SDarrick J. Wong 	 * as much as we can from the block. */
591e47dcf11SDarrick J. Wong 	if (info->magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) &&
592e47dcf11SDarrick J. Wong 	    xrep_buf_verify_struct(bp, &xfs_attr3_leaf_buf_ops) &&
593e47dcf11SDarrick J. Wong 	    xfs_attr3_leaf_header_check(bp, rx->sc->ip->i_ino) == NULL)
594e47dcf11SDarrick J. Wong 		error = xrep_xattr_recover_leaf(rx, bp);
595e47dcf11SDarrick J. Wong 
596e47dcf11SDarrick J. Wong 	/*
597e47dcf11SDarrick J. Wong 	 * If the buffer didn't already have buffer ops set, it was read in by
598e47dcf11SDarrick J. Wong 	 * the _find_buf function and could very well be /part/ of a multiblock
599e47dcf11SDarrick J. Wong 	 * remote block.  Mark it stale so that it doesn't hang around in
600e47dcf11SDarrick J. Wong 	 * memory to cause problems.
601e47dcf11SDarrick J. Wong 	 */
602e47dcf11SDarrick J. Wong 	if (bp->b_ops == NULL)
603e47dcf11SDarrick J. Wong 		xfs_buf_stale(bp);
604e47dcf11SDarrick J. Wong 
605e47dcf11SDarrick J. Wong 	xfs_buf_relse(bp);
606e47dcf11SDarrick J. Wong 	return error;
607e47dcf11SDarrick J. Wong }
608e47dcf11SDarrick J. Wong 
609e47dcf11SDarrick J. Wong /* Insert one xattr key/value. */
610e47dcf11SDarrick J. Wong STATIC int
611e47dcf11SDarrick J. Wong xrep_xattr_insert_rec(
612e47dcf11SDarrick J. Wong 	struct xrep_xattr		*rx,
613e47dcf11SDarrick J. Wong 	const struct xrep_xattr_key	*key)
614e47dcf11SDarrick J. Wong {
615e47dcf11SDarrick J. Wong 	struct xfs_da_args		args = {
616e47dcf11SDarrick J. Wong 		.dp			= rx->sc->tempip,
617e47dcf11SDarrick J. Wong 		.attr_filter		= key->flags,
618e47dcf11SDarrick J. Wong 		.namelen		= key->namelen,
619e47dcf11SDarrick J. Wong 		.valuelen		= key->valuelen,
620e47dcf11SDarrick J. Wong 		.owner			= rx->sc->ip->i_ino,
621e7420e75SDarrick J. Wong 		.geo			= rx->sc->mp->m_attr_geo,
622e7420e75SDarrick J. Wong 		.whichfork		= XFS_ATTR_FORK,
623e7420e75SDarrick J. Wong 		.op_flags		= XFS_DA_OP_OKNOENT,
624e47dcf11SDarrick J. Wong 	};
625e47dcf11SDarrick J. Wong 	struct xchk_xattr_buf		*ab = rx->sc->buf;
626e47dcf11SDarrick J. Wong 	int				error;
627e47dcf11SDarrick J. Wong 
628e47dcf11SDarrick J. Wong 	/*
629e47dcf11SDarrick J. Wong 	 * Grab pointers to the scrub buffer so that we can use them to insert
630e47dcf11SDarrick J. Wong 	 * attrs into the temp file.
631e47dcf11SDarrick J. Wong 	 */
632e47dcf11SDarrick J. Wong 	args.name = ab->name;
633e47dcf11SDarrick J. Wong 	args.value = ab->value;
634e47dcf11SDarrick J. Wong 
635e47dcf11SDarrick J. Wong 	/*
636e47dcf11SDarrick J. Wong 	 * The attribute name is stored near the end of the in-core buffer,
637e47dcf11SDarrick J. Wong 	 * though we reserve one more byte to ensure null termination.
638e47dcf11SDarrick J. Wong 	 */
639e47dcf11SDarrick J. Wong 	ab->name[XATTR_NAME_MAX] = 0;
640e47dcf11SDarrick J. Wong 
641e47dcf11SDarrick J. Wong 	error = xfblob_load(rx->xattr_blobs, key->name_cookie, ab->name,
642e47dcf11SDarrick J. Wong 			key->namelen);
643e47dcf11SDarrick J. Wong 	if (error)
644e47dcf11SDarrick J. Wong 		return error;
645e47dcf11SDarrick J. Wong 
646e47dcf11SDarrick J. Wong 	error = xfblob_free(rx->xattr_blobs, key->name_cookie);
647e47dcf11SDarrick J. Wong 	if (error)
648e47dcf11SDarrick J. Wong 		return error;
649e47dcf11SDarrick J. Wong 
650e47dcf11SDarrick J. Wong 	error = xfblob_load(rx->xattr_blobs, key->value_cookie, args.value,
651e47dcf11SDarrick J. Wong 			key->valuelen);
652e47dcf11SDarrick J. Wong 	if (error)
653e47dcf11SDarrick J. Wong 		return error;
654e47dcf11SDarrick J. Wong 
655e47dcf11SDarrick J. Wong 	error = xfblob_free(rx->xattr_blobs, key->value_cookie);
656e47dcf11SDarrick J. Wong 	if (error)
657e47dcf11SDarrick J. Wong 		return error;
658e47dcf11SDarrick J. Wong 
659e47dcf11SDarrick J. Wong 	ab->name[key->namelen] = 0;
660e47dcf11SDarrick J. Wong 
661086e934fSDarrick J. Wong 	if (key->flags & XFS_ATTR_PARENT) {
662086e934fSDarrick J. Wong 		trace_xrep_xattr_insert_pptr(rx->sc->tempip, key->flags,
663086e934fSDarrick J. Wong 				ab->name, key->namelen, ab->value,
664086e934fSDarrick J. Wong 				key->valuelen);
665086e934fSDarrick J. Wong 		args.op_flags |= XFS_DA_OP_LOGGED;
666086e934fSDarrick J. Wong 	} else {
667086e934fSDarrick J. Wong 		trace_xrep_xattr_insert_rec(rx->sc->tempip, key->flags,
668086e934fSDarrick J. Wong 				ab->name, key->namelen, key->valuelen);
669086e934fSDarrick J. Wong 	}
670e47dcf11SDarrick J. Wong 
671e47dcf11SDarrick J. Wong 	/*
672e47dcf11SDarrick J. Wong 	 * xfs_attr_set creates and commits its own transaction.  If the attr
673e47dcf11SDarrick J. Wong 	 * already exists, we'll just drop it during the rebuild.
674e47dcf11SDarrick J. Wong 	 */
675e7420e75SDarrick J. Wong 	xfs_attr_sethash(&args);
676bf61c36aSDarrick J. Wong 	error = xfs_attr_set(&args, XFS_ATTRUPDATE_CREATE, false);
677e47dcf11SDarrick J. Wong 	if (error == -EEXIST)
678e47dcf11SDarrick J. Wong 		error = 0;
679e47dcf11SDarrick J. Wong 
680e47dcf11SDarrick J. Wong 	return error;
681e47dcf11SDarrick J. Wong }
682e47dcf11SDarrick J. Wong 
683e47dcf11SDarrick J. Wong /*
684e47dcf11SDarrick J. Wong  * Periodically flush salvaged attributes to the temporary file.  This is done
685e47dcf11SDarrick J. Wong  * to reduce the memory requirements of the xattr rebuild because files can
686e47dcf11SDarrick J. Wong  * contain millions of attributes.
687e47dcf11SDarrick J. Wong  */
688e47dcf11SDarrick J. Wong STATIC int
689e47dcf11SDarrick J. Wong xrep_xattr_flush_stashed(
690e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx)
691e47dcf11SDarrick J. Wong {
692e47dcf11SDarrick J. Wong 	xfarray_idx_t		array_cur;
693e47dcf11SDarrick J. Wong 	int			error;
694e47dcf11SDarrick J. Wong 
695e47dcf11SDarrick J. Wong 	/*
696e47dcf11SDarrick J. Wong 	 * Entering this function, the scrub context has a reference to the
697e47dcf11SDarrick J. Wong 	 * inode being repaired, the temporary file, and a scrub transaction
698e47dcf11SDarrick J. Wong 	 * that we use during xattr salvaging to avoid livelocking if there
699e47dcf11SDarrick J. Wong 	 * are cycles in the xattr structures.  We hold ILOCK_EXCL on both
700e47dcf11SDarrick J. Wong 	 * the inode being repaired, though it is not ijoined to the scrub
701e47dcf11SDarrick J. Wong 	 * transaction.
702e47dcf11SDarrick J. Wong 	 *
703e47dcf11SDarrick J. Wong 	 * To constrain kernel memory use, we occasionally flush salvaged
704e47dcf11SDarrick J. Wong 	 * xattrs from the xfarray and xfblob structures into the temporary
705e47dcf11SDarrick J. Wong 	 * file in preparation for exchanging the xattr structures at the end.
706e47dcf11SDarrick J. Wong 	 * Updating the temporary file requires a transaction, so we commit the
707e47dcf11SDarrick J. Wong 	 * scrub transaction and drop the two ILOCKs so that xfs_attr_set can
708e47dcf11SDarrick J. Wong 	 * allocate whatever transaction it wants.
709e47dcf11SDarrick J. Wong 	 *
710e47dcf11SDarrick J. Wong 	 * We still hold IOLOCK_EXCL on the inode being repaired, which
711e47dcf11SDarrick J. Wong 	 * prevents anyone from modifying the damaged xattr data while we
712e47dcf11SDarrick J. Wong 	 * repair it.
713e47dcf11SDarrick J. Wong 	 */
714e47dcf11SDarrick J. Wong 	error = xrep_trans_commit(rx->sc);
715e47dcf11SDarrick J. Wong 	if (error)
716e47dcf11SDarrick J. Wong 		return error;
717e47dcf11SDarrick J. Wong 	xchk_iunlock(rx->sc, XFS_ILOCK_EXCL);
718e47dcf11SDarrick J. Wong 
719e47dcf11SDarrick J. Wong 	/*
720e47dcf11SDarrick J. Wong 	 * Take the IOLOCK of the temporary file while we modify xattrs.  This
721e47dcf11SDarrick J. Wong 	 * isn't strictly required because the temporary file is never revealed
722e47dcf11SDarrick J. Wong 	 * to userspace, but we follow the same locking rules.  We still hold
723e47dcf11SDarrick J. Wong 	 * sc->ip's IOLOCK.
724e47dcf11SDarrick J. Wong 	 */
725e47dcf11SDarrick J. Wong 	error = xrep_tempfile_iolock_polled(rx->sc);
726e47dcf11SDarrick J. Wong 	if (error)
727e47dcf11SDarrick J. Wong 		return error;
728e47dcf11SDarrick J. Wong 
729e47dcf11SDarrick J. Wong 	/* Add all the salvaged attrs to the temporary file. */
730e47dcf11SDarrick J. Wong 	foreach_xfarray_idx(rx->xattr_records, array_cur) {
731e47dcf11SDarrick J. Wong 		struct xrep_xattr_key	key;
732e47dcf11SDarrick J. Wong 
733e47dcf11SDarrick J. Wong 		error = xfarray_load(rx->xattr_records, array_cur, &key);
734e47dcf11SDarrick J. Wong 		if (error)
735e47dcf11SDarrick J. Wong 			return error;
736e47dcf11SDarrick J. Wong 
737e47dcf11SDarrick J. Wong 		error = xrep_xattr_insert_rec(rx, &key);
738e47dcf11SDarrick J. Wong 		if (error)
739e47dcf11SDarrick J. Wong 			return error;
740e47dcf11SDarrick J. Wong 	}
741e47dcf11SDarrick J. Wong 
742e47dcf11SDarrick J. Wong 	/* Empty out both arrays now that we've added the entries. */
743e47dcf11SDarrick J. Wong 	xfarray_truncate(rx->xattr_records);
744e47dcf11SDarrick J. Wong 	xfblob_truncate(rx->xattr_blobs);
745e47dcf11SDarrick J. Wong 
746e47dcf11SDarrick J. Wong 	xrep_tempfile_iounlock(rx->sc);
747e47dcf11SDarrick J. Wong 
748e47dcf11SDarrick J. Wong 	/* Recreate the salvage transaction and relock the inode. */
749e47dcf11SDarrick J. Wong 	error = xchk_trans_alloc(rx->sc, 0);
750e47dcf11SDarrick J. Wong 	if (error)
751e47dcf11SDarrick J. Wong 		return error;
752e47dcf11SDarrick J. Wong 	xchk_ilock(rx->sc, XFS_ILOCK_EXCL);
753e47dcf11SDarrick J. Wong 	return 0;
754e47dcf11SDarrick J. Wong }
755e47dcf11SDarrick J. Wong 
756e47dcf11SDarrick J. Wong /* Decide if we've stashed too much xattr data in memory. */
757e47dcf11SDarrick J. Wong static inline bool
758e47dcf11SDarrick J. Wong xrep_xattr_want_flush_stashed(
759e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx)
760e47dcf11SDarrick J. Wong {
761e47dcf11SDarrick J. Wong 	unsigned long long	bytes;
762e47dcf11SDarrick J. Wong 
763e5d7ce03SDarrick J. Wong 	if (!rx->can_flush)
764e5d7ce03SDarrick J. Wong 		return false;
765e5d7ce03SDarrick J. Wong 
766e47dcf11SDarrick J. Wong 	bytes = xfarray_bytes(rx->xattr_records) +
767e47dcf11SDarrick J. Wong 		xfblob_bytes(rx->xattr_blobs);
768e47dcf11SDarrick J. Wong 	return bytes > XREP_XATTR_MAX_STASH_BYTES;
769e47dcf11SDarrick J. Wong }
770e47dcf11SDarrick J. Wong 
771e5d7ce03SDarrick J. Wong /*
772e5d7ce03SDarrick J. Wong  * Did we observe rename changing parent pointer xattrs while we were flushing
773e5d7ce03SDarrick J. Wong  * salvaged attrs?
774e5d7ce03SDarrick J. Wong  */
775e5d7ce03SDarrick J. Wong static inline bool
776e5d7ce03SDarrick J. Wong xrep_xattr_saw_pptr_conflict(
777e5d7ce03SDarrick J. Wong 	struct xrep_xattr	*rx)
778e5d7ce03SDarrick J. Wong {
779e5d7ce03SDarrick J. Wong 	bool			ret;
780e5d7ce03SDarrick J. Wong 
781e5d7ce03SDarrick J. Wong 	ASSERT(rx->can_flush);
782e5d7ce03SDarrick J. Wong 
783e5d7ce03SDarrick J. Wong 	if (!xfs_has_parent(rx->sc->mp))
784e5d7ce03SDarrick J. Wong 		return false;
785e5d7ce03SDarrick J. Wong 
786e5d7ce03SDarrick J. Wong 	xfs_assert_ilocked(rx->sc->ip, XFS_ILOCK_EXCL);
787e5d7ce03SDarrick J. Wong 
788e5d7ce03SDarrick J. Wong 	mutex_lock(&rx->lock);
789e5d7ce03SDarrick J. Wong 	ret = xfarray_bytes(rx->pptr_recs) > 0;
790e5d7ce03SDarrick J. Wong 	mutex_unlock(&rx->lock);
791e5d7ce03SDarrick J. Wong 
792e5d7ce03SDarrick J. Wong 	return ret;
793e5d7ce03SDarrick J. Wong }
794e5d7ce03SDarrick J. Wong 
795e5d7ce03SDarrick J. Wong /*
796e5d7ce03SDarrick J. Wong  * Reset the entire repair state back to initial conditions, now that we've
797e5d7ce03SDarrick J. Wong  * detected a parent pointer update to the attr structure while we were
798e5d7ce03SDarrick J. Wong  * flushing salvaged attrs.  See the locking notes in dir_repair.c for more
799e5d7ce03SDarrick J. Wong  * information on why this is all necessary.
800e5d7ce03SDarrick J. Wong  */
801e5d7ce03SDarrick J. Wong STATIC int
802e5d7ce03SDarrick J. Wong xrep_xattr_full_reset(
803e5d7ce03SDarrick J. Wong 	struct xrep_xattr	*rx)
804e5d7ce03SDarrick J. Wong {
805e5d7ce03SDarrick J. Wong 	struct xfs_scrub	*sc = rx->sc;
806e5d7ce03SDarrick J. Wong 	struct xfs_attr_sf_hdr	*hdr;
807e5d7ce03SDarrick J. Wong 	struct xfs_ifork	*ifp = &sc->tempip->i_af;
808e5d7ce03SDarrick J. Wong 	int			error;
809e5d7ce03SDarrick J. Wong 
810e5d7ce03SDarrick J. Wong 	trace_xrep_xattr_full_reset(sc->ip, sc->tempip);
811e5d7ce03SDarrick J. Wong 
812e5d7ce03SDarrick J. Wong 	/* The temporary file's data fork had better not be in btree format. */
813e5d7ce03SDarrick J. Wong 	if (sc->tempip->i_df.if_format == XFS_DINODE_FMT_BTREE) {
814e5d7ce03SDarrick J. Wong 		ASSERT(0);
815e5d7ce03SDarrick J. Wong 		return -EIO;
816e5d7ce03SDarrick J. Wong 	}
817e5d7ce03SDarrick J. Wong 
818e5d7ce03SDarrick J. Wong 	/*
819e5d7ce03SDarrick J. Wong 	 * We begin in transaction context with sc->ip ILOCKed but not joined
820e5d7ce03SDarrick J. Wong 	 * to the transaction.  To reset to the initial state, we must hold
821e5d7ce03SDarrick J. Wong 	 * sc->ip's ILOCK to prevent rename from updating parent pointer
822e5d7ce03SDarrick J. Wong 	 * information and the tempfile's ILOCK to clear its contents.
823e5d7ce03SDarrick J. Wong 	 */
824e5d7ce03SDarrick J. Wong 	xchk_iunlock(rx->sc, XFS_ILOCK_EXCL);
825e5d7ce03SDarrick J. Wong 	xrep_tempfile_ilock_both(sc);
826e5d7ce03SDarrick J. Wong 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
827e5d7ce03SDarrick J. Wong 	xfs_trans_ijoin(sc->tp, sc->tempip, 0);
828e5d7ce03SDarrick J. Wong 
829e5d7ce03SDarrick J. Wong 	/*
830e5d7ce03SDarrick J. Wong 	 * Free all the blocks of the attr fork of the temp file, and reset
831e5d7ce03SDarrick J. Wong 	 * it back to local format.
832e5d7ce03SDarrick J. Wong 	 */
833e5d7ce03SDarrick J. Wong 	if (xfs_ifork_has_extents(&sc->tempip->i_af)) {
834e5d7ce03SDarrick J. Wong 		error = xrep_reap_ifork(sc, sc->tempip, XFS_ATTR_FORK);
835e5d7ce03SDarrick J. Wong 		if (error)
836e5d7ce03SDarrick J. Wong 			return error;
837e5d7ce03SDarrick J. Wong 
838e5d7ce03SDarrick J. Wong 		ASSERT(ifp->if_bytes == 0);
839e5d7ce03SDarrick J. Wong 		ifp->if_format = XFS_DINODE_FMT_LOCAL;
840e5d7ce03SDarrick J. Wong 		xfs_idata_realloc(sc->tempip, sizeof(*hdr), XFS_ATTR_FORK);
841e5d7ce03SDarrick J. Wong 	}
842e5d7ce03SDarrick J. Wong 
843e5d7ce03SDarrick J. Wong 	/* Reinitialize the attr fork to an empty shortform structure. */
844e5d7ce03SDarrick J. Wong 	hdr = ifp->if_data;
845e5d7ce03SDarrick J. Wong 	memset(hdr, 0, sizeof(*hdr));
846e5d7ce03SDarrick J. Wong 	hdr->totsize = cpu_to_be16(sizeof(*hdr));
847e5d7ce03SDarrick J. Wong 	xfs_trans_log_inode(sc->tp, sc->tempip, XFS_ILOG_CORE | XFS_ILOG_ADATA);
848e5d7ce03SDarrick J. Wong 
849e5d7ce03SDarrick J. Wong 	/*
850e5d7ce03SDarrick J. Wong 	 * Roll this transaction to commit our reset ondisk.  The tempfile
851e5d7ce03SDarrick J. Wong 	 * should no longer be joined to the transaction, so we drop its ILOCK.
852e5d7ce03SDarrick J. Wong 	 * This should leave us in transaction context with sc->ip ILOCKed but
853e5d7ce03SDarrick J. Wong 	 * not joined to the transaction.
854e5d7ce03SDarrick J. Wong 	 */
855e5d7ce03SDarrick J. Wong 	error = xrep_roll_trans(sc);
856e5d7ce03SDarrick J. Wong 	if (error)
857e5d7ce03SDarrick J. Wong 		return error;
858e5d7ce03SDarrick J. Wong 	xrep_tempfile_iunlock(sc);
859e5d7ce03SDarrick J. Wong 
860e5d7ce03SDarrick J. Wong 	/*
861e5d7ce03SDarrick J. Wong 	 * Erase any accumulated parent pointer updates now that we've erased
862e5d7ce03SDarrick J. Wong 	 * the tempfile's attr fork.  We're resetting the entire repair state
863e5d7ce03SDarrick J. Wong 	 * back to where we were initially, except now we won't flush salvaged
864e5d7ce03SDarrick J. Wong 	 * xattrs until the very end.
865e5d7ce03SDarrick J. Wong 	 */
866e5d7ce03SDarrick J. Wong 	mutex_lock(&rx->lock);
867e5d7ce03SDarrick J. Wong 	xfarray_truncate(rx->pptr_recs);
868e5d7ce03SDarrick J. Wong 	xfblob_truncate(rx->pptr_names);
869e5d7ce03SDarrick J. Wong 	mutex_unlock(&rx->lock);
870e5d7ce03SDarrick J. Wong 
871e5d7ce03SDarrick J. Wong 	rx->can_flush = false;
872e5d7ce03SDarrick J. Wong 	rx->attrs_found = 0;
873e5d7ce03SDarrick J. Wong 
874e5d7ce03SDarrick J. Wong 	ASSERT(xfarray_bytes(rx->xattr_records) == 0);
875e5d7ce03SDarrick J. Wong 	ASSERT(xfblob_bytes(rx->xattr_blobs) == 0);
876e5d7ce03SDarrick J. Wong 	return 0;
877e5d7ce03SDarrick J. Wong }
878e5d7ce03SDarrick J. Wong 
879e47dcf11SDarrick J. Wong /* Extract as many attribute keys and values as we can. */
880e47dcf11SDarrick J. Wong STATIC int
881e47dcf11SDarrick J. Wong xrep_xattr_recover(
882e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx)
883e47dcf11SDarrick J. Wong {
884e47dcf11SDarrick J. Wong 	struct xfs_bmbt_irec	got;
885e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc = rx->sc;
886e47dcf11SDarrick J. Wong 	struct xfs_da_geometry	*geo = sc->mp->m_attr_geo;
887e47dcf11SDarrick J. Wong 	xfs_fileoff_t		offset;
888e47dcf11SDarrick J. Wong 	xfs_extlen_t		len;
889e47dcf11SDarrick J. Wong 	xfs_dablk_t		dabno;
890e47dcf11SDarrick J. Wong 	int			nmap;
891e47dcf11SDarrick J. Wong 	int			error;
892e47dcf11SDarrick J. Wong 
893e5d7ce03SDarrick J. Wong restart:
894e47dcf11SDarrick J. Wong 	/*
895e47dcf11SDarrick J. Wong 	 * Iterate each xattr leaf block in the attr fork to scan them for any
896e47dcf11SDarrick J. Wong 	 * attributes that we might salvage.
897e47dcf11SDarrick J. Wong 	 */
898e47dcf11SDarrick J. Wong 	for (offset = 0;
899e47dcf11SDarrick J. Wong 	     offset < XFS_MAX_FILEOFF;
900e47dcf11SDarrick J. Wong 	     offset = got.br_startoff + got.br_blockcount) {
901e47dcf11SDarrick J. Wong 		nmap = 1;
902e47dcf11SDarrick J. Wong 		error = xfs_bmapi_read(sc->ip, offset, XFS_MAX_FILEOFF - offset,
903e47dcf11SDarrick J. Wong 				&got, &nmap, XFS_BMAPI_ATTRFORK);
904e47dcf11SDarrick J. Wong 		if (error)
905e47dcf11SDarrick J. Wong 			return error;
906e47dcf11SDarrick J. Wong 		if (nmap != 1)
907e47dcf11SDarrick J. Wong 			return -EFSCORRUPTED;
908e47dcf11SDarrick J. Wong 		if (!xfs_bmap_is_written_extent(&got))
909e47dcf11SDarrick J. Wong 			continue;
910e47dcf11SDarrick J. Wong 
911e47dcf11SDarrick J. Wong 		for (dabno = round_up(got.br_startoff, geo->fsbcount);
912e47dcf11SDarrick J. Wong 		     dabno < got.br_startoff + got.br_blockcount;
913e47dcf11SDarrick J. Wong 		     dabno += len) {
914e47dcf11SDarrick J. Wong 			xfs_fileoff_t	curr_offset = dabno - got.br_startoff;
915e47dcf11SDarrick J. Wong 			xfs_extlen_t	maxlen;
916e47dcf11SDarrick J. Wong 
917e47dcf11SDarrick J. Wong 			if (xchk_should_terminate(rx->sc, &error))
918e47dcf11SDarrick J. Wong 				return error;
919e47dcf11SDarrick J. Wong 
920e47dcf11SDarrick J. Wong 			maxlen = min_t(xfs_filblks_t, INT_MAX,
921e47dcf11SDarrick J. Wong 					got.br_blockcount - curr_offset);
922e47dcf11SDarrick J. Wong 			error = xrep_xattr_recover_block(rx, dabno,
923e47dcf11SDarrick J. Wong 					curr_offset + got.br_startblock,
924e47dcf11SDarrick J. Wong 					maxlen, &len);
925e47dcf11SDarrick J. Wong 			if (error)
926e47dcf11SDarrick J. Wong 				return error;
927e47dcf11SDarrick J. Wong 
928e47dcf11SDarrick J. Wong 			if (xrep_xattr_want_flush_stashed(rx)) {
929e47dcf11SDarrick J. Wong 				error = xrep_xattr_flush_stashed(rx);
930e47dcf11SDarrick J. Wong 				if (error)
931e47dcf11SDarrick J. Wong 					return error;
932e5d7ce03SDarrick J. Wong 
933e5d7ce03SDarrick J. Wong 				if (xrep_xattr_saw_pptr_conflict(rx)) {
934e5d7ce03SDarrick J. Wong 					error = xrep_xattr_full_reset(rx);
935e5d7ce03SDarrick J. Wong 					if (error)
936e5d7ce03SDarrick J. Wong 						return error;
937e5d7ce03SDarrick J. Wong 
938e5d7ce03SDarrick J. Wong 					goto restart;
939e5d7ce03SDarrick J. Wong 				}
940e47dcf11SDarrick J. Wong 			}
941e47dcf11SDarrick J. Wong 		}
942e47dcf11SDarrick J. Wong 	}
943e47dcf11SDarrick J. Wong 
944e47dcf11SDarrick J. Wong 	return 0;
945e47dcf11SDarrick J. Wong }
946e47dcf11SDarrick J. Wong 
947e47dcf11SDarrick J. Wong /*
948e47dcf11SDarrick J. Wong  * Reset the extended attribute fork to a state where we can start re-adding
949e47dcf11SDarrick J. Wong  * the salvaged attributes.
950e47dcf11SDarrick J. Wong  */
951e47dcf11SDarrick J. Wong STATIC int
952e47dcf11SDarrick J. Wong xrep_xattr_fork_remove(
953e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc,
954e47dcf11SDarrick J. Wong 	struct xfs_inode	*ip)
955e47dcf11SDarrick J. Wong {
956e47dcf11SDarrick J. Wong 	struct xfs_attr_sf_hdr	*hdr;
957e47dcf11SDarrick J. Wong 	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, XFS_ATTR_FORK);
958e47dcf11SDarrick J. Wong 
959e47dcf11SDarrick J. Wong 	/*
960e47dcf11SDarrick J. Wong 	 * If the data fork is in btree format, we can't change di_forkoff
961e47dcf11SDarrick J. Wong 	 * because we could run afoul of the rule that the data fork isn't
962e47dcf11SDarrick J. Wong 	 * supposed to be in btree format if there's enough space in the fork
963e47dcf11SDarrick J. Wong 	 * that it could have used extents format.  Instead, reinitialize the
964e47dcf11SDarrick J. Wong 	 * attr fork to have a shortform structure with zero attributes.
965e47dcf11SDarrick J. Wong 	 */
966e47dcf11SDarrick J. Wong 	if (ip->i_df.if_format == XFS_DINODE_FMT_BTREE) {
967e47dcf11SDarrick J. Wong 		ifp->if_format = XFS_DINODE_FMT_LOCAL;
968e47dcf11SDarrick J. Wong 		hdr = xfs_idata_realloc(ip, (int)sizeof(*hdr) - ifp->if_bytes,
969e47dcf11SDarrick J. Wong 				XFS_ATTR_FORK);
970e47dcf11SDarrick J. Wong 		hdr->count = 0;
971e47dcf11SDarrick J. Wong 		hdr->totsize = cpu_to_be16(sizeof(*hdr));
972e47dcf11SDarrick J. Wong 		xfs_trans_log_inode(sc->tp, ip,
973e47dcf11SDarrick J. Wong 				XFS_ILOG_CORE | XFS_ILOG_ADATA);
974e47dcf11SDarrick J. Wong 		return 0;
975e47dcf11SDarrick J. Wong 	}
976e47dcf11SDarrick J. Wong 
977e47dcf11SDarrick J. Wong 	/* If we still have attr fork extents, something's wrong. */
978e47dcf11SDarrick J. Wong 	if (ifp->if_nextents != 0) {
979e47dcf11SDarrick J. Wong 		struct xfs_iext_cursor	icur;
980e47dcf11SDarrick J. Wong 		struct xfs_bmbt_irec	irec;
981e47dcf11SDarrick J. Wong 		unsigned int		i = 0;
982e47dcf11SDarrick J. Wong 
983e47dcf11SDarrick J. Wong 		xfs_emerg(sc->mp,
984e47dcf11SDarrick J. Wong 	"inode 0x%llx attr fork still has %llu attr extents, format %d?!",
985e47dcf11SDarrick J. Wong 				ip->i_ino, ifp->if_nextents, ifp->if_format);
986e47dcf11SDarrick J. Wong 		for_each_xfs_iext(ifp, &icur, &irec) {
987e47dcf11SDarrick J. Wong 			xfs_err(sc->mp,
988e47dcf11SDarrick J. Wong 	"[%u]: startoff %llu startblock %llu blockcount %llu state %u",
989e47dcf11SDarrick J. Wong 					i++, irec.br_startoff,
990e47dcf11SDarrick J. Wong 					irec.br_startblock, irec.br_blockcount,
991e47dcf11SDarrick J. Wong 					irec.br_state);
992e47dcf11SDarrick J. Wong 		}
993e47dcf11SDarrick J. Wong 		ASSERT(0);
994e47dcf11SDarrick J. Wong 		return -EFSCORRUPTED;
995e47dcf11SDarrick J. Wong 	}
996e47dcf11SDarrick J. Wong 
997e47dcf11SDarrick J. Wong 	xfs_attr_fork_remove(ip, sc->tp);
998e47dcf11SDarrick J. Wong 	return 0;
999e47dcf11SDarrick J. Wong }
1000e47dcf11SDarrick J. Wong 
1001e47dcf11SDarrick J. Wong /*
1002e47dcf11SDarrick J. Wong  * Free all the attribute fork blocks of the file being repaired and delete the
1003e47dcf11SDarrick J. Wong  * fork.  The caller must ILOCK the scrub file and join it to the transaction.
1004e47dcf11SDarrick J. Wong  * This function returns with the inode joined to a clean transaction.
1005e47dcf11SDarrick J. Wong  */
1006e47dcf11SDarrick J. Wong int
1007e47dcf11SDarrick J. Wong xrep_xattr_reset_fork(
1008e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc)
1009e47dcf11SDarrick J. Wong {
1010e47dcf11SDarrick J. Wong 	int			error;
1011e47dcf11SDarrick J. Wong 
1012e47dcf11SDarrick J. Wong 	trace_xrep_xattr_reset_fork(sc->ip, sc->ip);
1013e47dcf11SDarrick J. Wong 
1014e47dcf11SDarrick J. Wong 	/* Unmap all the attr blocks. */
1015e47dcf11SDarrick J. Wong 	if (xfs_ifork_has_extents(&sc->ip->i_af)) {
1016e47dcf11SDarrick J. Wong 		error = xrep_reap_ifork(sc, sc->ip, XFS_ATTR_FORK);
1017e47dcf11SDarrick J. Wong 		if (error)
1018e47dcf11SDarrick J. Wong 			return error;
1019e47dcf11SDarrick J. Wong 	}
1020e47dcf11SDarrick J. Wong 
1021e47dcf11SDarrick J. Wong 	error = xrep_xattr_fork_remove(sc, sc->ip);
1022e47dcf11SDarrick J. Wong 	if (error)
1023e47dcf11SDarrick J. Wong 		return error;
1024e47dcf11SDarrick J. Wong 
1025e47dcf11SDarrick J. Wong 	return xfs_trans_roll_inode(&sc->tp, sc->ip);
1026e47dcf11SDarrick J. Wong }
1027e47dcf11SDarrick J. Wong 
1028e47dcf11SDarrick J. Wong /*
1029e47dcf11SDarrick J. Wong  * Free all the attribute fork blocks of the temporary file and delete the attr
1030e47dcf11SDarrick J. Wong  * fork.  The caller must ILOCK the tempfile and join it to the transaction.
1031e47dcf11SDarrick J. Wong  * This function returns with the inode joined to a clean scrub transaction.
1032e47dcf11SDarrick J. Wong  */
1033a26dc213SDarrick J. Wong int
1034e47dcf11SDarrick J. Wong xrep_xattr_reset_tempfile_fork(
1035e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc)
1036e47dcf11SDarrick J. Wong {
1037e47dcf11SDarrick J. Wong 	int			error;
1038e47dcf11SDarrick J. Wong 
1039e47dcf11SDarrick J. Wong 	trace_xrep_xattr_reset_fork(sc->ip, sc->tempip);
1040e47dcf11SDarrick J. Wong 
1041e47dcf11SDarrick J. Wong 	/*
1042e47dcf11SDarrick J. Wong 	 * Wipe out the attr fork of the temp file so that regular inode
1043e47dcf11SDarrick J. Wong 	 * inactivation won't trip over the corrupt attr fork.
1044e47dcf11SDarrick J. Wong 	 */
1045e47dcf11SDarrick J. Wong 	if (xfs_ifork_has_extents(&sc->tempip->i_af)) {
1046e47dcf11SDarrick J. Wong 		error = xrep_reap_ifork(sc, sc->tempip, XFS_ATTR_FORK);
1047e47dcf11SDarrick J. Wong 		if (error)
1048e47dcf11SDarrick J. Wong 			return error;
1049e47dcf11SDarrick J. Wong 	}
1050e47dcf11SDarrick J. Wong 
1051e47dcf11SDarrick J. Wong 	return xrep_xattr_fork_remove(sc, sc->tempip);
1052e47dcf11SDarrick J. Wong }
1053e47dcf11SDarrick J. Wong 
1054e47dcf11SDarrick J. Wong /*
1055e47dcf11SDarrick J. Wong  * Find all the extended attributes for this inode by scraping them out of the
1056e47dcf11SDarrick J. Wong  * attribute key blocks by hand, and flushing them into the temp file.
1057e47dcf11SDarrick J. Wong  * When we're done, free the staging memory before exchanging the xattr
1058e47dcf11SDarrick J. Wong  * structures to reduce memory usage.
1059e47dcf11SDarrick J. Wong  */
1060e47dcf11SDarrick J. Wong STATIC int
1061e47dcf11SDarrick J. Wong xrep_xattr_salvage_attributes(
1062e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx)
1063e47dcf11SDarrick J. Wong {
1064e47dcf11SDarrick J. Wong 	struct xfs_inode	*ip = rx->sc->ip;
1065e47dcf11SDarrick J. Wong 	int			error;
1066e47dcf11SDarrick J. Wong 
1067e47dcf11SDarrick J. Wong 	/* Short format xattrs are easy! */
1068e47dcf11SDarrick J. Wong 	if (rx->sc->ip->i_af.if_format == XFS_DINODE_FMT_LOCAL) {
1069e47dcf11SDarrick J. Wong 		error = xrep_xattr_recover_sf(rx);
1070e47dcf11SDarrick J. Wong 		if (error)
1071e47dcf11SDarrick J. Wong 			return error;
1072e47dcf11SDarrick J. Wong 
1073e47dcf11SDarrick J. Wong 		return xrep_xattr_flush_stashed(rx);
1074e47dcf11SDarrick J. Wong 	}
1075e47dcf11SDarrick J. Wong 
1076e47dcf11SDarrick J. Wong 	/*
1077e47dcf11SDarrick J. Wong 	 * For non-inline xattr structures, the salvage function scans the
1078e47dcf11SDarrick J. Wong 	 * buffer cache looking for potential attr leaf blocks.  The scan
1079e47dcf11SDarrick J. Wong 	 * requires the ability to lock any buffer found and runs independently
1080e47dcf11SDarrick J. Wong 	 * of any transaction <-> buffer item <-> buffer linkage.  Therefore,
1081e47dcf11SDarrick J. Wong 	 * roll the transaction to ensure there are no buffers joined.  We hold
1082e47dcf11SDarrick J. Wong 	 * the ILOCK independently of the transaction.
1083e47dcf11SDarrick J. Wong 	 */
1084e47dcf11SDarrick J. Wong 	error = xfs_trans_roll(&rx->sc->tp);
1085e47dcf11SDarrick J. Wong 	if (error)
1086e47dcf11SDarrick J. Wong 		return error;
1087e47dcf11SDarrick J. Wong 
1088e47dcf11SDarrick J. Wong 	error = xfs_iread_extents(rx->sc->tp, ip, XFS_ATTR_FORK);
1089e47dcf11SDarrick J. Wong 	if (error)
1090e47dcf11SDarrick J. Wong 		return error;
1091e47dcf11SDarrick J. Wong 
1092e47dcf11SDarrick J. Wong 	error = xrep_xattr_recover(rx);
1093e47dcf11SDarrick J. Wong 	if (error)
1094e47dcf11SDarrick J. Wong 		return error;
1095e47dcf11SDarrick J. Wong 
1096e47dcf11SDarrick J. Wong 	return xrep_xattr_flush_stashed(rx);
1097e47dcf11SDarrick J. Wong }
1098e47dcf11SDarrick J. Wong 
1099e47dcf11SDarrick J. Wong /*
1100e5d7ce03SDarrick J. Wong  * Add this stashed incore parent pointer to the temporary file.  The caller
1101e5d7ce03SDarrick J. Wong  * must hold the tempdir's IOLOCK, must not hold any ILOCKs, and must not be in
1102e5d7ce03SDarrick J. Wong  * transaction context.
1103e5d7ce03SDarrick J. Wong  */
1104e5d7ce03SDarrick J. Wong STATIC int
1105e5d7ce03SDarrick J. Wong xrep_xattr_replay_pptr_update(
1106e5d7ce03SDarrick J. Wong 	struct xrep_xattr		*rx,
1107e5d7ce03SDarrick J. Wong 	const struct xfs_name		*xname,
1108e5d7ce03SDarrick J. Wong 	struct xrep_xattr_pptr		*pptr)
1109e5d7ce03SDarrick J. Wong {
1110e5d7ce03SDarrick J. Wong 	struct xfs_scrub		*sc = rx->sc;
1111e5d7ce03SDarrick J. Wong 	int				error;
1112e5d7ce03SDarrick J. Wong 
1113e5d7ce03SDarrick J. Wong 	switch (pptr->action) {
1114e5d7ce03SDarrick J. Wong 	case XREP_XATTR_PPTR_ADD:
1115e5d7ce03SDarrick J. Wong 		/* Create parent pointer. */
1116e5d7ce03SDarrick J. Wong 		trace_xrep_xattr_replay_parentadd(sc->tempip, xname,
1117e5d7ce03SDarrick J. Wong 				&pptr->pptr_rec);
1118e5d7ce03SDarrick J. Wong 
1119e5d7ce03SDarrick J. Wong 		error = xfs_parent_set(sc->tempip, sc->ip->i_ino, xname,
1120e5d7ce03SDarrick J. Wong 				&pptr->pptr_rec, &rx->pptr_args);
1121e5d7ce03SDarrick J. Wong 		ASSERT(error != -EEXIST);
1122e5d7ce03SDarrick J. Wong 		return error;
1123e5d7ce03SDarrick J. Wong 	case XREP_XATTR_PPTR_REMOVE:
1124e5d7ce03SDarrick J. Wong 		/* Remove parent pointer. */
1125e5d7ce03SDarrick J. Wong 		trace_xrep_xattr_replay_parentremove(sc->tempip, xname,
1126e5d7ce03SDarrick J. Wong 				&pptr->pptr_rec);
1127e5d7ce03SDarrick J. Wong 
1128e5d7ce03SDarrick J. Wong 		error = xfs_parent_unset(sc->tempip, sc->ip->i_ino, xname,
1129e5d7ce03SDarrick J. Wong 				&pptr->pptr_rec, &rx->pptr_args);
1130e5d7ce03SDarrick J. Wong 		ASSERT(error != -ENOATTR);
1131e5d7ce03SDarrick J. Wong 		return error;
1132e5d7ce03SDarrick J. Wong 	}
1133e5d7ce03SDarrick J. Wong 
1134e5d7ce03SDarrick J. Wong 	ASSERT(0);
1135e5d7ce03SDarrick J. Wong 	return -EIO;
1136e5d7ce03SDarrick J. Wong }
1137e5d7ce03SDarrick J. Wong 
1138e5d7ce03SDarrick J. Wong /*
1139e5d7ce03SDarrick J. Wong  * Flush stashed parent pointer updates that have been recorded by the scanner.
1140e5d7ce03SDarrick J. Wong  * This is done to reduce the memory requirements of the xattr rebuild, since
1141e5d7ce03SDarrick J. Wong  * files can have a lot of hardlinks and the fs can be busy.
1142e5d7ce03SDarrick J. Wong  *
1143e5d7ce03SDarrick J. Wong  * Caller must not hold transactions or ILOCKs.  Caller must hold the tempfile
1144e5d7ce03SDarrick J. Wong  * IOLOCK.
1145e5d7ce03SDarrick J. Wong  */
1146e5d7ce03SDarrick J. Wong STATIC int
1147e5d7ce03SDarrick J. Wong xrep_xattr_replay_pptr_updates(
1148e5d7ce03SDarrick J. Wong 	struct xrep_xattr	*rx)
1149e5d7ce03SDarrick J. Wong {
1150e5d7ce03SDarrick J. Wong 	xfarray_idx_t		array_cur;
1151e5d7ce03SDarrick J. Wong 	int			error;
1152e5d7ce03SDarrick J. Wong 
1153e5d7ce03SDarrick J. Wong 	mutex_lock(&rx->lock);
1154e5d7ce03SDarrick J. Wong 	foreach_xfarray_idx(rx->pptr_recs, array_cur) {
1155e5d7ce03SDarrick J. Wong 		struct xrep_xattr_pptr	pptr;
1156e5d7ce03SDarrick J. Wong 
1157e5d7ce03SDarrick J. Wong 		error = xfarray_load(rx->pptr_recs, array_cur, &pptr);
1158e5d7ce03SDarrick J. Wong 		if (error)
1159e5d7ce03SDarrick J. Wong 			goto out_unlock;
1160e5d7ce03SDarrick J. Wong 
1161e5d7ce03SDarrick J. Wong 		error = xfblob_loadname(rx->pptr_names, pptr.name_cookie,
1162e5d7ce03SDarrick J. Wong 				&rx->xname, pptr.namelen);
1163e5d7ce03SDarrick J. Wong 		if (error)
1164e5d7ce03SDarrick J. Wong 			goto out_unlock;
1165e5d7ce03SDarrick J. Wong 		mutex_unlock(&rx->lock);
1166e5d7ce03SDarrick J. Wong 
1167e5d7ce03SDarrick J. Wong 		error = xrep_xattr_replay_pptr_update(rx, &rx->xname, &pptr);
1168e5d7ce03SDarrick J. Wong 		if (error)
1169e5d7ce03SDarrick J. Wong 			return error;
1170e5d7ce03SDarrick J. Wong 
1171e5d7ce03SDarrick J. Wong 		mutex_lock(&rx->lock);
1172e5d7ce03SDarrick J. Wong 	}
1173e5d7ce03SDarrick J. Wong 
1174e5d7ce03SDarrick J. Wong 	/* Empty out both arrays now that we've added the entries. */
1175e5d7ce03SDarrick J. Wong 	xfarray_truncate(rx->pptr_recs);
1176e5d7ce03SDarrick J. Wong 	xfblob_truncate(rx->pptr_names);
1177e5d7ce03SDarrick J. Wong 	mutex_unlock(&rx->lock);
1178e5d7ce03SDarrick J. Wong 	return 0;
1179e5d7ce03SDarrick J. Wong out_unlock:
1180e5d7ce03SDarrick J. Wong 	mutex_unlock(&rx->lock);
1181e5d7ce03SDarrick J. Wong 	return error;
1182e5d7ce03SDarrick J. Wong }
1183e5d7ce03SDarrick J. Wong 
1184e5d7ce03SDarrick J. Wong /*
1185e5d7ce03SDarrick J. Wong  * Remember that we want to create a parent pointer in the tempfile.  These
1186e5d7ce03SDarrick J. Wong  * stashed actions will be replayed later.
1187e5d7ce03SDarrick J. Wong  */
1188e5d7ce03SDarrick J. Wong STATIC int
1189e5d7ce03SDarrick J. Wong xrep_xattr_stash_parentadd(
1190e5d7ce03SDarrick J. Wong 	struct xrep_xattr	*rx,
1191e5d7ce03SDarrick J. Wong 	const struct xfs_name	*name,
1192e5d7ce03SDarrick J. Wong 	const struct xfs_inode	*dp)
1193e5d7ce03SDarrick J. Wong {
1194e5d7ce03SDarrick J. Wong 	struct xrep_xattr_pptr	pptr = {
1195e5d7ce03SDarrick J. Wong 		.action		= XREP_XATTR_PPTR_ADD,
1196e5d7ce03SDarrick J. Wong 		.namelen	= name->len,
1197e5d7ce03SDarrick J. Wong 	};
1198e5d7ce03SDarrick J. Wong 	int			error;
1199e5d7ce03SDarrick J. Wong 
1200e5d7ce03SDarrick J. Wong 	trace_xrep_xattr_stash_parentadd(rx->sc->tempip, dp, name);
1201e5d7ce03SDarrick J. Wong 
1202e5d7ce03SDarrick J. Wong 	xfs_inode_to_parent_rec(&pptr.pptr_rec, dp);
1203e5d7ce03SDarrick J. Wong 	error = xfblob_storename(rx->pptr_names, &pptr.name_cookie, name);
1204e5d7ce03SDarrick J. Wong 	if (error)
1205e5d7ce03SDarrick J. Wong 		return error;
1206e5d7ce03SDarrick J. Wong 
1207e5d7ce03SDarrick J. Wong 	return xfarray_append(rx->pptr_recs, &pptr);
1208e5d7ce03SDarrick J. Wong }
1209e5d7ce03SDarrick J. Wong 
1210e5d7ce03SDarrick J. Wong /*
1211e5d7ce03SDarrick J. Wong  * Remember that we want to remove a parent pointer from the tempfile.  These
1212e5d7ce03SDarrick J. Wong  * stashed actions will be replayed later.
1213e5d7ce03SDarrick J. Wong  */
1214e5d7ce03SDarrick J. Wong STATIC int
1215e5d7ce03SDarrick J. Wong xrep_xattr_stash_parentremove(
1216e5d7ce03SDarrick J. Wong 	struct xrep_xattr	*rx,
1217e5d7ce03SDarrick J. Wong 	const struct xfs_name	*name,
1218e5d7ce03SDarrick J. Wong 	const struct xfs_inode	*dp)
1219e5d7ce03SDarrick J. Wong {
1220e5d7ce03SDarrick J. Wong 	struct xrep_xattr_pptr	pptr = {
1221e5d7ce03SDarrick J. Wong 		.action		= XREP_XATTR_PPTR_REMOVE,
1222e5d7ce03SDarrick J. Wong 		.namelen	= name->len,
1223e5d7ce03SDarrick J. Wong 	};
1224e5d7ce03SDarrick J. Wong 	int			error;
1225e5d7ce03SDarrick J. Wong 
1226e5d7ce03SDarrick J. Wong 	trace_xrep_xattr_stash_parentremove(rx->sc->tempip, dp, name);
1227e5d7ce03SDarrick J. Wong 
1228e5d7ce03SDarrick J. Wong 	xfs_inode_to_parent_rec(&pptr.pptr_rec, dp);
1229e5d7ce03SDarrick J. Wong 	error = xfblob_storename(rx->pptr_names, &pptr.name_cookie, name);
1230e5d7ce03SDarrick J. Wong 	if (error)
1231e5d7ce03SDarrick J. Wong 		return error;
1232e5d7ce03SDarrick J. Wong 
1233e5d7ce03SDarrick J. Wong 	return xfarray_append(rx->pptr_recs, &pptr);
1234e5d7ce03SDarrick J. Wong }
1235e5d7ce03SDarrick J. Wong 
1236e5d7ce03SDarrick J. Wong /*
1237e5d7ce03SDarrick J. Wong  * Capture dirent updates being made by other threads.  We will have to replay
1238e5d7ce03SDarrick J. Wong  * the parent pointer updates before exchanging attr forks.
1239e5d7ce03SDarrick J. Wong  */
1240e5d7ce03SDarrick J. Wong STATIC int
1241e5d7ce03SDarrick J. Wong xrep_xattr_live_dirent_update(
1242e5d7ce03SDarrick J. Wong 	struct notifier_block		*nb,
1243e5d7ce03SDarrick J. Wong 	unsigned long			action,
1244e5d7ce03SDarrick J. Wong 	void				*data)
1245e5d7ce03SDarrick J. Wong {
1246e5d7ce03SDarrick J. Wong 	struct xfs_dir_update_params	*p = data;
1247e5d7ce03SDarrick J. Wong 	struct xrep_xattr		*rx;
1248e5d7ce03SDarrick J. Wong 	struct xfs_scrub		*sc;
1249e5d7ce03SDarrick J. Wong 	int				error;
1250e5d7ce03SDarrick J. Wong 
1251e5d7ce03SDarrick J. Wong 	rx = container_of(nb, struct xrep_xattr, dhook.dirent_hook.nb);
1252e5d7ce03SDarrick J. Wong 	sc = rx->sc;
1253e5d7ce03SDarrick J. Wong 
1254e5d7ce03SDarrick J. Wong 	/*
1255e5d7ce03SDarrick J. Wong 	 * This thread updated a dirent that points to the file that we're
1256e5d7ce03SDarrick J. Wong 	 * repairing, so stash the update for replay against the temporary
1257e5d7ce03SDarrick J. Wong 	 * file.
1258e5d7ce03SDarrick J. Wong 	 */
1259e5d7ce03SDarrick J. Wong 	if (p->ip->i_ino != sc->ip->i_ino)
1260e5d7ce03SDarrick J. Wong 		return NOTIFY_DONE;
1261e5d7ce03SDarrick J. Wong 
1262e5d7ce03SDarrick J. Wong 	mutex_lock(&rx->lock);
1263e5d7ce03SDarrick J. Wong 	if (p->delta > 0)
1264e5d7ce03SDarrick J. Wong 		error = xrep_xattr_stash_parentadd(rx, p->name, p->dp);
1265e5d7ce03SDarrick J. Wong 	else
1266e5d7ce03SDarrick J. Wong 		error = xrep_xattr_stash_parentremove(rx, p->name, p->dp);
1267e5d7ce03SDarrick J. Wong 	if (error)
1268e5d7ce03SDarrick J. Wong 		rx->live_update_aborted = true;
1269e5d7ce03SDarrick J. Wong 	mutex_unlock(&rx->lock);
1270e5d7ce03SDarrick J. Wong 	return NOTIFY_DONE;
1271e5d7ce03SDarrick J. Wong }
1272e5d7ce03SDarrick J. Wong 
1273e5d7ce03SDarrick J. Wong /*
1274e47dcf11SDarrick J. Wong  * Prepare both inodes' attribute forks for an exchange.  Promote the tempfile
1275e47dcf11SDarrick J. Wong  * from short format to leaf format, and if the file being repaired has a short
1276e47dcf11SDarrick J. Wong  * format attr fork, turn it into an empty extent list.
1277e47dcf11SDarrick J. Wong  */
1278e47dcf11SDarrick J. Wong STATIC int
1279e47dcf11SDarrick J. Wong xrep_xattr_swap_prep(
1280e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc,
1281e47dcf11SDarrick J. Wong 	bool			temp_local,
1282e47dcf11SDarrick J. Wong 	bool			ip_local)
1283e47dcf11SDarrick J. Wong {
1284e47dcf11SDarrick J. Wong 	int			error;
1285e47dcf11SDarrick J. Wong 
1286e47dcf11SDarrick J. Wong 	/*
1287e47dcf11SDarrick J. Wong 	 * If the tempfile's attributes are in shortform format, convert that
1288e47dcf11SDarrick J. Wong 	 * to a single leaf extent so that we can use the atomic mapping
1289e47dcf11SDarrick J. Wong 	 * exchange.
1290e47dcf11SDarrick J. Wong 	 */
1291e47dcf11SDarrick J. Wong 	if (temp_local) {
1292e47dcf11SDarrick J. Wong 		struct xfs_da_args	args = {
1293e47dcf11SDarrick J. Wong 			.dp		= sc->tempip,
1294e47dcf11SDarrick J. Wong 			.geo		= sc->mp->m_attr_geo,
1295e47dcf11SDarrick J. Wong 			.whichfork	= XFS_ATTR_FORK,
1296e47dcf11SDarrick J. Wong 			.trans		= sc->tp,
1297e47dcf11SDarrick J. Wong 			.total		= 1,
1298e47dcf11SDarrick J. Wong 			.owner		= sc->ip->i_ino,
1299e47dcf11SDarrick J. Wong 		};
1300e47dcf11SDarrick J. Wong 
1301e47dcf11SDarrick J. Wong 		error = xfs_attr_shortform_to_leaf(&args);
1302e47dcf11SDarrick J. Wong 		if (error)
1303e47dcf11SDarrick J. Wong 			return error;
1304e47dcf11SDarrick J. Wong 
1305e47dcf11SDarrick J. Wong 		/*
1306e47dcf11SDarrick J. Wong 		 * Roll the deferred log items to get us back to a clean
1307e47dcf11SDarrick J. Wong 		 * transaction.
1308e47dcf11SDarrick J. Wong 		 */
1309e47dcf11SDarrick J. Wong 		error = xfs_defer_finish(&sc->tp);
1310e47dcf11SDarrick J. Wong 		if (error)
1311e47dcf11SDarrick J. Wong 			return error;
1312e47dcf11SDarrick J. Wong 	}
1313e47dcf11SDarrick J. Wong 
1314e47dcf11SDarrick J. Wong 	/*
1315e47dcf11SDarrick J. Wong 	 * If the file being repaired had a shortform attribute fork, convert
1316e47dcf11SDarrick J. Wong 	 * that to an empty extent list in preparation for the atomic mapping
1317e47dcf11SDarrick J. Wong 	 * exchange.
1318e47dcf11SDarrick J. Wong 	 */
1319e47dcf11SDarrick J. Wong 	if (ip_local) {
1320e47dcf11SDarrick J. Wong 		struct xfs_ifork	*ifp;
1321e47dcf11SDarrick J. Wong 
1322e47dcf11SDarrick J. Wong 		ifp = xfs_ifork_ptr(sc->ip, XFS_ATTR_FORK);
1323e47dcf11SDarrick J. Wong 
1324e47dcf11SDarrick J. Wong 		xfs_idestroy_fork(ifp);
1325e47dcf11SDarrick J. Wong 		ifp->if_format = XFS_DINODE_FMT_EXTENTS;
1326e47dcf11SDarrick J. Wong 		ifp->if_nextents = 0;
1327e47dcf11SDarrick J. Wong 		ifp->if_bytes = 0;
1328e47dcf11SDarrick J. Wong 		ifp->if_data = NULL;
1329e47dcf11SDarrick J. Wong 		ifp->if_height = 0;
1330e47dcf11SDarrick J. Wong 
1331e47dcf11SDarrick J. Wong 		xfs_trans_log_inode(sc->tp, sc->ip,
1332e47dcf11SDarrick J. Wong 				XFS_ILOG_CORE | XFS_ILOG_ADATA);
1333e47dcf11SDarrick J. Wong 	}
1334e47dcf11SDarrick J. Wong 
1335e47dcf11SDarrick J. Wong 	return 0;
1336e47dcf11SDarrick J. Wong }
1337e47dcf11SDarrick J. Wong 
1338e47dcf11SDarrick J. Wong /* Exchange the temporary file's attribute fork with the one being repaired. */
1339a26dc213SDarrick J. Wong int
1340e47dcf11SDarrick J. Wong xrep_xattr_swap(
1341e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc,
1342e47dcf11SDarrick J. Wong 	struct xrep_tempexch	*tx)
1343e47dcf11SDarrick J. Wong {
1344e47dcf11SDarrick J. Wong 	bool			ip_local, temp_local;
1345e47dcf11SDarrick J. Wong 	int			error = 0;
1346e47dcf11SDarrick J. Wong 
1347e47dcf11SDarrick J. Wong 	ip_local = sc->ip->i_af.if_format == XFS_DINODE_FMT_LOCAL;
1348e47dcf11SDarrick J. Wong 	temp_local = sc->tempip->i_af.if_format == XFS_DINODE_FMT_LOCAL;
1349e47dcf11SDarrick J. Wong 
1350e47dcf11SDarrick J. Wong 	/*
1351e47dcf11SDarrick J. Wong 	 * If the both files have a local format attr fork and the rebuilt
1352e47dcf11SDarrick J. Wong 	 * xattr data would fit in the repaired file's attr fork, just copy
1353e47dcf11SDarrick J. Wong 	 * the contents from the tempfile and declare ourselves done.
1354e47dcf11SDarrick J. Wong 	 */
1355e47dcf11SDarrick J. Wong 	if (ip_local && temp_local) {
1356e47dcf11SDarrick J. Wong 		int	forkoff;
1357e47dcf11SDarrick J. Wong 		int	newsize;
1358e47dcf11SDarrick J. Wong 
1359e47dcf11SDarrick J. Wong 		newsize = xfs_attr_sf_totsize(sc->tempip);
1360e47dcf11SDarrick J. Wong 		forkoff = xfs_attr_shortform_bytesfit(sc->ip, newsize);
1361e47dcf11SDarrick J. Wong 		if (forkoff > 0) {
1362e47dcf11SDarrick J. Wong 			sc->ip->i_forkoff = forkoff;
1363e47dcf11SDarrick J. Wong 			xrep_tempfile_copyout_local(sc, XFS_ATTR_FORK);
1364e47dcf11SDarrick J. Wong 			return 0;
1365e47dcf11SDarrick J. Wong 		}
1366e47dcf11SDarrick J. Wong 	}
1367e47dcf11SDarrick J. Wong 
1368e47dcf11SDarrick J. Wong 	/* Otherwise, make sure both attr forks are in block-mapping mode. */
1369e47dcf11SDarrick J. Wong 	error = xrep_xattr_swap_prep(sc, temp_local, ip_local);
1370e47dcf11SDarrick J. Wong 	if (error)
1371e47dcf11SDarrick J. Wong 		return error;
1372e47dcf11SDarrick J. Wong 
1373e47dcf11SDarrick J. Wong 	return xrep_tempexch_contents(sc, tx);
1374e47dcf11SDarrick J. Wong }
1375e47dcf11SDarrick J. Wong 
1376e47dcf11SDarrick J. Wong /*
1377e5d7ce03SDarrick J. Wong  * Finish replaying stashed parent pointer updates, allocate a transaction for
1378e5d7ce03SDarrick J. Wong  * exchanging extent mappings, and take the ILOCKs of both files before we
1379e5d7ce03SDarrick J. Wong  * commit the new extended attribute structure.
1380e5d7ce03SDarrick J. Wong  */
1381e5d7ce03SDarrick J. Wong STATIC int
1382e5d7ce03SDarrick J. Wong xrep_xattr_finalize_tempfile(
1383e5d7ce03SDarrick J. Wong 	struct xrep_xattr	*rx)
1384e5d7ce03SDarrick J. Wong {
1385e5d7ce03SDarrick J. Wong 	struct xfs_scrub	*sc = rx->sc;
1386e5d7ce03SDarrick J. Wong 	int			error;
1387e5d7ce03SDarrick J. Wong 
1388e5d7ce03SDarrick J. Wong 	if (!xfs_has_parent(sc->mp))
1389e5d7ce03SDarrick J. Wong 		return xrep_tempexch_trans_alloc(sc, XFS_ATTR_FORK, &rx->tx);
1390e5d7ce03SDarrick J. Wong 
1391e5d7ce03SDarrick J. Wong 	/*
1392e5d7ce03SDarrick J. Wong 	 * Repair relies on the ILOCK to quiesce all possible xattr updates.
1393e5d7ce03SDarrick J. Wong 	 * Replay all queued parent pointer updates into the tempfile before
1394e5d7ce03SDarrick J. Wong 	 * exchanging the contents, even if that means dropping the ILOCKs and
1395e5d7ce03SDarrick J. Wong 	 * the transaction.
1396e5d7ce03SDarrick J. Wong 	 */
1397e5d7ce03SDarrick J. Wong 	do {
1398e5d7ce03SDarrick J. Wong 		error = xrep_xattr_replay_pptr_updates(rx);
1399e5d7ce03SDarrick J. Wong 		if (error)
1400e5d7ce03SDarrick J. Wong 			return error;
1401e5d7ce03SDarrick J. Wong 
1402e5d7ce03SDarrick J. Wong 		error = xrep_tempexch_trans_alloc(sc, XFS_ATTR_FORK, &rx->tx);
1403e5d7ce03SDarrick J. Wong 		if (error)
1404e5d7ce03SDarrick J. Wong 			return error;
1405e5d7ce03SDarrick J. Wong 
1406e5d7ce03SDarrick J. Wong 		if (xfarray_length(rx->pptr_recs) == 0)
1407e5d7ce03SDarrick J. Wong 			break;
1408e5d7ce03SDarrick J. Wong 
1409e5d7ce03SDarrick J. Wong 		xchk_trans_cancel(sc);
1410e5d7ce03SDarrick J. Wong 		xrep_tempfile_iunlock_both(sc);
1411e5d7ce03SDarrick J. Wong 	} while (!xchk_should_terminate(sc, &error));
1412e5d7ce03SDarrick J. Wong 	return error;
1413e5d7ce03SDarrick J. Wong }
1414e5d7ce03SDarrick J. Wong 
1415e5d7ce03SDarrick J. Wong /*
1416e47dcf11SDarrick J. Wong  * Exchange the new extended attribute data (which we created in the tempfile)
1417e47dcf11SDarrick J. Wong  * with the file being repaired.
1418e47dcf11SDarrick J. Wong  */
1419e47dcf11SDarrick J. Wong STATIC int
1420e47dcf11SDarrick J. Wong xrep_xattr_rebuild_tree(
1421e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx)
1422e47dcf11SDarrick J. Wong {
1423e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc = rx->sc;
1424e47dcf11SDarrick J. Wong 	int			error;
1425e47dcf11SDarrick J. Wong 
1426e47dcf11SDarrick J. Wong 	/*
1427e47dcf11SDarrick J. Wong 	 * If we didn't find any attributes to salvage, repair the file by
1428e47dcf11SDarrick J. Wong 	 * zapping its attr fork.
1429e47dcf11SDarrick J. Wong 	 */
1430e47dcf11SDarrick J. Wong 	if (rx->attrs_found == 0) {
1431e47dcf11SDarrick J. Wong 		xfs_trans_ijoin(sc->tp, sc->ip, 0);
1432e47dcf11SDarrick J. Wong 		error = xrep_xattr_reset_fork(sc);
1433e47dcf11SDarrick J. Wong 		if (error)
1434e47dcf11SDarrick J. Wong 			return error;
1435e47dcf11SDarrick J. Wong 
1436e47dcf11SDarrick J. Wong 		goto forget_acls;
1437e47dcf11SDarrick J. Wong 	}
1438e47dcf11SDarrick J. Wong 
1439e47dcf11SDarrick J. Wong 	trace_xrep_xattr_rebuild_tree(sc->ip, sc->tempip);
1440e47dcf11SDarrick J. Wong 
1441e47dcf11SDarrick J. Wong 	/*
1442e47dcf11SDarrick J. Wong 	 * Commit the repair transaction and drop the ILOCKs so that we can use
1443e47dcf11SDarrick J. Wong 	 * the atomic file content exchange helper functions to compute the
1444e47dcf11SDarrick J. Wong 	 * correct resource reservations.
1445e47dcf11SDarrick J. Wong 	 *
1446e47dcf11SDarrick J. Wong 	 * We still hold IOLOCK_EXCL (aka i_rwsem) which will prevent xattr
1447e47dcf11SDarrick J. Wong 	 * modifications, but there's nothing to prevent userspace from reading
1448e47dcf11SDarrick J. Wong 	 * the attributes until we're ready for the exchange operation.  Reads
1449e47dcf11SDarrick J. Wong 	 * will return -EIO without shutting down the fs, so we're ok with
1450e47dcf11SDarrick J. Wong 	 * that.
1451e47dcf11SDarrick J. Wong 	 */
1452e47dcf11SDarrick J. Wong 	error = xrep_trans_commit(sc);
1453e47dcf11SDarrick J. Wong 	if (error)
1454e47dcf11SDarrick J. Wong 		return error;
1455e47dcf11SDarrick J. Wong 
1456e47dcf11SDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
1457e47dcf11SDarrick J. Wong 
1458e47dcf11SDarrick J. Wong 	/*
1459e47dcf11SDarrick J. Wong 	 * Take the IOLOCK on the temporary file so that we can run xattr
1460e47dcf11SDarrick J. Wong 	 * operations with the same locks held as we would for a normal file.
1461e47dcf11SDarrick J. Wong 	 * We still hold sc->ip's IOLOCK.
1462e47dcf11SDarrick J. Wong 	 */
1463e47dcf11SDarrick J. Wong 	error = xrep_tempfile_iolock_polled(rx->sc);
1464e47dcf11SDarrick J. Wong 	if (error)
1465e47dcf11SDarrick J. Wong 		return error;
1466e47dcf11SDarrick J. Wong 
1467e5d7ce03SDarrick J. Wong 	/*
1468e5d7ce03SDarrick J. Wong 	 * Allocate transaction, lock inodes, and make sure that we've replayed
1469e5d7ce03SDarrick J. Wong 	 * all the stashed parent pointer updates to the temp file.  After this
1470e5d7ce03SDarrick J. Wong 	 * point, we're ready to exchange attr fork mappings.
1471e5d7ce03SDarrick J. Wong 	 */
1472e5d7ce03SDarrick J. Wong 	error = xrep_xattr_finalize_tempfile(rx);
1473e47dcf11SDarrick J. Wong 	if (error)
1474e47dcf11SDarrick J. Wong 		return error;
1475e47dcf11SDarrick J. Wong 
1476e47dcf11SDarrick J. Wong 	/*
1477e47dcf11SDarrick J. Wong 	 * Exchange the blocks mapped by the tempfile's attr fork with the file
1478e47dcf11SDarrick J. Wong 	 * being repaired.  The old attr blocks will then be attached to the
1479e47dcf11SDarrick J. Wong 	 * tempfile, so reap its attr fork.
1480e47dcf11SDarrick J. Wong 	 */
1481e47dcf11SDarrick J. Wong 	error = xrep_xattr_swap(sc, &rx->tx);
1482e47dcf11SDarrick J. Wong 	if (error)
1483e47dcf11SDarrick J. Wong 		return error;
1484e47dcf11SDarrick J. Wong 
1485e47dcf11SDarrick J. Wong 	error = xrep_xattr_reset_tempfile_fork(sc);
1486e47dcf11SDarrick J. Wong 	if (error)
1487e47dcf11SDarrick J. Wong 		return error;
1488e47dcf11SDarrick J. Wong 
1489e47dcf11SDarrick J. Wong 	/*
1490e47dcf11SDarrick J. Wong 	 * Roll to get a transaction without any inodes joined to it.  Then we
1491e47dcf11SDarrick J. Wong 	 * can drop the tempfile's ILOCK and IOLOCK before doing more work on
1492e47dcf11SDarrick J. Wong 	 * the scrub target file.
1493e47dcf11SDarrick J. Wong 	 */
1494e47dcf11SDarrick J. Wong 	error = xfs_trans_roll(&sc->tp);
1495e47dcf11SDarrick J. Wong 	if (error)
1496e47dcf11SDarrick J. Wong 		return error;
1497e47dcf11SDarrick J. Wong 
1498e47dcf11SDarrick J. Wong 	xrep_tempfile_iunlock(sc);
1499e47dcf11SDarrick J. Wong 	xrep_tempfile_iounlock(sc);
1500e47dcf11SDarrick J. Wong 
1501e47dcf11SDarrick J. Wong forget_acls:
1502e47dcf11SDarrick J. Wong 	/* Invalidate cached ACLs now that we've reloaded all the xattrs. */
1503e47dcf11SDarrick J. Wong 	xfs_forget_acl(VFS_I(sc->ip), SGI_ACL_FILE);
1504e47dcf11SDarrick J. Wong 	xfs_forget_acl(VFS_I(sc->ip), SGI_ACL_DEFAULT);
1505e47dcf11SDarrick J. Wong 	return 0;
1506e47dcf11SDarrick J. Wong }
1507e47dcf11SDarrick J. Wong 
1508e47dcf11SDarrick J. Wong /* Tear down all the incore scan stuff we created. */
1509e47dcf11SDarrick J. Wong STATIC void
1510e47dcf11SDarrick J. Wong xrep_xattr_teardown(
1511e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx)
1512e47dcf11SDarrick J. Wong {
1513e5d7ce03SDarrick J. Wong 	if (xfs_has_parent(rx->sc->mp))
1514e5d7ce03SDarrick J. Wong 		xfs_dir_hook_del(rx->sc->mp, &rx->dhook);
1515e5d7ce03SDarrick J. Wong 	if (rx->pptr_names)
1516e5d7ce03SDarrick J. Wong 		xfblob_destroy(rx->pptr_names);
1517e5d7ce03SDarrick J. Wong 	if (rx->pptr_recs)
1518e5d7ce03SDarrick J. Wong 		xfarray_destroy(rx->pptr_recs);
1519e47dcf11SDarrick J. Wong 	xfblob_destroy(rx->xattr_blobs);
1520e47dcf11SDarrick J. Wong 	xfarray_destroy(rx->xattr_records);
1521e5d7ce03SDarrick J. Wong 	mutex_destroy(&rx->lock);
1522e47dcf11SDarrick J. Wong 	kfree(rx);
1523e47dcf11SDarrick J. Wong }
1524e47dcf11SDarrick J. Wong 
1525e47dcf11SDarrick J. Wong /* Set up the filesystem scan so we can regenerate extended attributes. */
1526e47dcf11SDarrick J. Wong STATIC int
1527e47dcf11SDarrick J. Wong xrep_xattr_setup_scan(
1528e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc,
1529e47dcf11SDarrick J. Wong 	struct xrep_xattr	**rxp)
1530e47dcf11SDarrick J. Wong {
1531e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx;
1532e47dcf11SDarrick J. Wong 	char			*descr;
1533e47dcf11SDarrick J. Wong 	int			max_len;
1534e47dcf11SDarrick J. Wong 	int			error;
1535e47dcf11SDarrick J. Wong 
1536e47dcf11SDarrick J. Wong 	rx = kzalloc(sizeof(struct xrep_xattr), XCHK_GFP_FLAGS);
1537e47dcf11SDarrick J. Wong 	if (!rx)
1538e47dcf11SDarrick J. Wong 		return -ENOMEM;
1539e47dcf11SDarrick J. Wong 	rx->sc = sc;
1540e5d7ce03SDarrick J. Wong 	rx->can_flush = true;
1541e5d7ce03SDarrick J. Wong 	rx->xname.name = rx->namebuf;
1542e5d7ce03SDarrick J. Wong 
1543e5d7ce03SDarrick J. Wong 	mutex_init(&rx->lock);
1544e47dcf11SDarrick J. Wong 
1545e47dcf11SDarrick J. Wong 	/*
1546e47dcf11SDarrick J. Wong 	 * Allocate enough memory to handle loading local attr values from the
1547e47dcf11SDarrick J. Wong 	 * xfblob data while flushing stashed attrs to the temporary file.
1548e47dcf11SDarrick J. Wong 	 * We only realloc the buffer when salvaging remote attr values.
1549e47dcf11SDarrick J. Wong 	 */
1550e47dcf11SDarrick J. Wong 	max_len = xfs_attr_leaf_entsize_local_max(sc->mp->m_attr_geo->blksize);
1551e47dcf11SDarrick J. Wong 	error = xchk_setup_xattr_buf(rx->sc, max_len);
1552e47dcf11SDarrick J. Wong 	if (error == -ENOMEM)
1553e47dcf11SDarrick J. Wong 		error = -EDEADLOCK;
1554e47dcf11SDarrick J. Wong 	if (error)
1555e47dcf11SDarrick J. Wong 		goto out_rx;
1556e47dcf11SDarrick J. Wong 
1557e47dcf11SDarrick J. Wong 	/* Set up some staging for salvaged attribute keys and values */
1558e47dcf11SDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc, "xattr keys");
1559e47dcf11SDarrick J. Wong 	error = xfarray_create(descr, 0, sizeof(struct xrep_xattr_key),
1560e47dcf11SDarrick J. Wong 			&rx->xattr_records);
1561e47dcf11SDarrick J. Wong 	kfree(descr);
1562e47dcf11SDarrick J. Wong 	if (error)
1563e47dcf11SDarrick J. Wong 		goto out_rx;
1564e47dcf11SDarrick J. Wong 
1565e47dcf11SDarrick J. Wong 	descr = xchk_xfile_ino_descr(sc, "xattr names");
1566e47dcf11SDarrick J. Wong 	error = xfblob_create(descr, &rx->xattr_blobs);
1567e47dcf11SDarrick J. Wong 	kfree(descr);
1568e47dcf11SDarrick J. Wong 	if (error)
1569e47dcf11SDarrick J. Wong 		goto out_keys;
1570e47dcf11SDarrick J. Wong 
1571e5d7ce03SDarrick J. Wong 	if (xfs_has_parent(sc->mp)) {
1572e5d7ce03SDarrick J. Wong 		ASSERT(sc->flags & XCHK_FSGATES_DIRENTS);
1573e5d7ce03SDarrick J. Wong 
1574e5d7ce03SDarrick J. Wong 		descr = xchk_xfile_ino_descr(sc,
1575e5d7ce03SDarrick J. Wong 				"xattr retained parent pointer entries");
1576e5d7ce03SDarrick J. Wong 		error = xfarray_create(descr, 0,
1577e5d7ce03SDarrick J. Wong 				sizeof(struct xrep_xattr_pptr),
1578e5d7ce03SDarrick J. Wong 				&rx->pptr_recs);
1579e5d7ce03SDarrick J. Wong 		kfree(descr);
1580e5d7ce03SDarrick J. Wong 		if (error)
1581e5d7ce03SDarrick J. Wong 			goto out_values;
1582e5d7ce03SDarrick J. Wong 
1583e5d7ce03SDarrick J. Wong 		descr = xchk_xfile_ino_descr(sc,
1584e5d7ce03SDarrick J. Wong 				"xattr retained parent pointer names");
1585e5d7ce03SDarrick J. Wong 		error = xfblob_create(descr, &rx->pptr_names);
1586e5d7ce03SDarrick J. Wong 		kfree(descr);
1587e5d7ce03SDarrick J. Wong 		if (error)
1588e5d7ce03SDarrick J. Wong 			goto out_pprecs;
1589e5d7ce03SDarrick J. Wong 
1590e5d7ce03SDarrick J. Wong 		xfs_dir_hook_setup(&rx->dhook, xrep_xattr_live_dirent_update);
1591e5d7ce03SDarrick J. Wong 		error = xfs_dir_hook_add(sc->mp, &rx->dhook);
1592e5d7ce03SDarrick J. Wong 		if (error)
1593e5d7ce03SDarrick J. Wong 			goto out_ppnames;
1594e5d7ce03SDarrick J. Wong 	}
1595e5d7ce03SDarrick J. Wong 
1596e47dcf11SDarrick J. Wong 	*rxp = rx;
1597e47dcf11SDarrick J. Wong 	return 0;
1598e5d7ce03SDarrick J. Wong out_ppnames:
1599e5d7ce03SDarrick J. Wong 	xfblob_destroy(rx->pptr_names);
1600e5d7ce03SDarrick J. Wong out_pprecs:
1601e5d7ce03SDarrick J. Wong 	xfarray_destroy(rx->pptr_recs);
1602e5d7ce03SDarrick J. Wong out_values:
1603e5d7ce03SDarrick J. Wong 	xfblob_destroy(rx->xattr_blobs);
1604e47dcf11SDarrick J. Wong out_keys:
1605e47dcf11SDarrick J. Wong 	xfarray_destroy(rx->xattr_records);
1606e47dcf11SDarrick J. Wong out_rx:
1607e5d7ce03SDarrick J. Wong 	mutex_destroy(&rx->lock);
1608e47dcf11SDarrick J. Wong 	kfree(rx);
1609e47dcf11SDarrick J. Wong 	return error;
1610e47dcf11SDarrick J. Wong }
1611e47dcf11SDarrick J. Wong 
1612e47dcf11SDarrick J. Wong /*
1613e47dcf11SDarrick J. Wong  * Repair the extended attribute metadata.
1614e47dcf11SDarrick J. Wong  *
1615e47dcf11SDarrick J. Wong  * XXX: Remote attribute value buffers encompass the entire (up to 64k) buffer.
1616e47dcf11SDarrick J. Wong  * The buffer cache in XFS can't handle aliased multiblock buffers, so this
1617e47dcf11SDarrick J. Wong  * might misbehave if the attr fork is crosslinked with other filesystem
1618e47dcf11SDarrick J. Wong  * metadata.
1619e47dcf11SDarrick J. Wong  */
1620e47dcf11SDarrick J. Wong int
1621e47dcf11SDarrick J. Wong xrep_xattr(
1622e47dcf11SDarrick J. Wong 	struct xfs_scrub	*sc)
1623e47dcf11SDarrick J. Wong {
1624e47dcf11SDarrick J. Wong 	struct xrep_xattr	*rx = NULL;
1625e47dcf11SDarrick J. Wong 	int			error;
1626e47dcf11SDarrick J. Wong 
1627e47dcf11SDarrick J. Wong 	if (!xfs_inode_hasattr(sc->ip))
1628e47dcf11SDarrick J. Wong 		return -ENOENT;
1629e47dcf11SDarrick J. Wong 
1630e47dcf11SDarrick J. Wong 	/* The rmapbt is required to reap the old attr fork. */
1631e47dcf11SDarrick J. Wong 	if (!xfs_has_rmapbt(sc->mp))
1632e47dcf11SDarrick J. Wong 		return -EOPNOTSUPP;
1633*6d335233SDarrick J. Wong 	/* We require atomic file exchange range to rebuild anything. */
1634*6d335233SDarrick J. Wong 	if (!xfs_has_exchange_range(sc->mp))
1635*6d335233SDarrick J. Wong 		return -EOPNOTSUPP;
1636e47dcf11SDarrick J. Wong 
1637e47dcf11SDarrick J. Wong 	error = xrep_xattr_setup_scan(sc, &rx);
1638e47dcf11SDarrick J. Wong 	if (error)
1639e47dcf11SDarrick J. Wong 		return error;
1640e47dcf11SDarrick J. Wong 
1641e47dcf11SDarrick J. Wong 	ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
1642e47dcf11SDarrick J. Wong 
1643e47dcf11SDarrick J. Wong 	error = xrep_xattr_salvage_attributes(rx);
1644e47dcf11SDarrick J. Wong 	if (error)
1645e47dcf11SDarrick J. Wong 		goto out_scan;
1646e47dcf11SDarrick J. Wong 
1647e5d7ce03SDarrick J. Wong 	if (rx->live_update_aborted) {
1648e5d7ce03SDarrick J. Wong 		error = -EIO;
1649e5d7ce03SDarrick J. Wong 		goto out_scan;
1650e5d7ce03SDarrick J. Wong 	}
1651e5d7ce03SDarrick J. Wong 
1652e47dcf11SDarrick J. Wong 	/* Last chance to abort before we start committing fixes. */
1653e47dcf11SDarrick J. Wong 	if (xchk_should_terminate(sc, &error))
1654e47dcf11SDarrick J. Wong 		goto out_scan;
1655e47dcf11SDarrick J. Wong 
1656e47dcf11SDarrick J. Wong 	error = xrep_xattr_rebuild_tree(rx);
1657e47dcf11SDarrick J. Wong 	if (error)
1658e47dcf11SDarrick J. Wong 		goto out_scan;
1659e47dcf11SDarrick J. Wong 
1660e47dcf11SDarrick J. Wong out_scan:
1661e47dcf11SDarrick J. Wong 	xrep_xattr_teardown(rx);
1662e47dcf11SDarrick J. Wong 	return error;
1663e47dcf11SDarrick J. Wong }
1664