Commit | Line | Data |
---|---|---|
e47dcf11 DW |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Copyright (c) 2018-2024 Oracle. All Rights Reserved. | |
4 | * Author: Darrick J. Wong <djwong@kernel.org> | |
5 | */ | |
6 | #include "xfs.h" | |
7 | #include "xfs_fs.h" | |
8 | #include "xfs_shared.h" | |
9 | #include "xfs_format.h" | |
10 | #include "xfs_trans_resv.h" | |
11 | #include "xfs_mount.h" | |
12 | #include "xfs_defer.h" | |
13 | #include "xfs_btree.h" | |
14 | #include "xfs_bit.h" | |
15 | #include "xfs_log_format.h" | |
16 | #include "xfs_trans.h" | |
17 | #include "xfs_sb.h" | |
18 | #include "xfs_inode.h" | |
19 | #include "xfs_da_format.h" | |
20 | #include "xfs_da_btree.h" | |
21 | #include "xfs_dir2.h" | |
22 | #include "xfs_attr.h" | |
23 | #include "xfs_attr_leaf.h" | |
24 | #include "xfs_attr_sf.h" | |
25 | #include "xfs_attr_remote.h" | |
26 | #include "xfs_bmap.h" | |
27 | #include "xfs_bmap_util.h" | |
28 | #include "xfs_exchmaps.h" | |
29 | #include "xfs_exchrange.h" | |
30 | #include "xfs_acl.h" | |
31 | #include "scrub/xfs_scrub.h" | |
32 | #include "scrub/scrub.h" | |
33 | #include "scrub/common.h" | |
34 | #include "scrub/trace.h" | |
35 | #include "scrub/repair.h" | |
36 | #include "scrub/tempfile.h" | |
37 | #include "scrub/tempexch.h" | |
38 | #include "scrub/xfile.h" | |
39 | #include "scrub/xfarray.h" | |
40 | #include "scrub/xfblob.h" | |
41 | #include "scrub/attr.h" | |
42 | #include "scrub/reap.h" | |
43 | #include "scrub/attr_repair.h" | |
44 | ||
45 | /* | |
46 | * Extended Attribute Repair | |
47 | * ========================= | |
48 | * | |
49 | * We repair extended attributes by reading the attr leaf blocks looking for | |
50 | * attributes entries that look salvageable (name passes verifiers, value can | |
51 | * be retrieved, etc). Each extended attribute worth salvaging is stashed in | |
52 | * memory, and the stashed entries are periodically replayed into a temporary | |
53 | * file to constrain memory use. Batching the construction of the temporary | |
54 | * extended attribute structure in this fashion reduces lock cycling of the | |
55 | * file being repaired and the temporary file. | |
56 | * | |
57 | * When salvaging completes, the remaining stashed attributes are replayed to | |
58 | * the temporary file. An atomic file contents exchange is used to commit the | |
59 | * new xattr blocks to the file being repaired. This will disrupt attrmulti | |
60 | * cursors. | |
61 | */ | |
62 | ||
63 | struct xrep_xattr_key { | |
64 | /* Cookie for retrieval of the xattr name. */ | |
65 | xfblob_cookie name_cookie; | |
66 | ||
67 | /* Cookie for retrieval of the xattr value. */ | |
68 | xfblob_cookie value_cookie; | |
69 | ||
70 | /* XFS_ATTR_* flags */ | |
71 | int flags; | |
72 | ||
73 | /* Length of the value and name. */ | |
74 | uint32_t valuelen; | |
75 | uint16_t namelen; | |
76 | }; | |
77 | ||
78 | /* | |
79 | * Stash up to 8 pages of attrs in xattr_records/xattr_blobs before we write | |
80 | * them to the temp file. | |
81 | */ | |
82 | #define XREP_XATTR_MAX_STASH_BYTES (PAGE_SIZE * 8) | |
83 | ||
84 | struct xrep_xattr { | |
85 | struct xfs_scrub *sc; | |
86 | ||
87 | /* Information for exchanging attr fork mappings at the end. */ | |
88 | struct xrep_tempexch tx; | |
89 | ||
90 | /* xattr keys */ | |
91 | struct xfarray *xattr_records; | |
92 | ||
93 | /* xattr values */ | |
94 | struct xfblob *xattr_blobs; | |
95 | ||
96 | /* Number of attributes that we are salvaging. */ | |
97 | unsigned long long attrs_found; | |
98 | }; | |
99 | ||
100 | /* Set up to recreate the extended attributes. */ | |
101 | int | |
102 | xrep_setup_xattr( | |
103 | struct xfs_scrub *sc) | |
104 | { | |
105 | return xrep_tempfile_create(sc, S_IFREG); | |
106 | } | |
107 | ||
108 | /* | |
109 | * Decide if we want to salvage this attribute. We don't bother with | |
110 | * incomplete or oversized keys or values. The @value parameter can be null | |
111 | * for remote attrs. | |
112 | */ | |
113 | STATIC int | |
114 | xrep_xattr_want_salvage( | |
115 | struct xrep_xattr *rx, | |
116 | unsigned int attr_flags, | |
117 | const void *name, | |
118 | int namelen, | |
119 | const void *value, | |
120 | int valuelen) | |
121 | { | |
122 | if (attr_flags & XFS_ATTR_INCOMPLETE) | |
123 | return false; | |
124 | if (namelen > XATTR_NAME_MAX || namelen <= 0) | |
125 | return false; | |
ea0b3e81 | 126 | if (!xfs_attr_namecheck(attr_flags, name, namelen)) |
e47dcf11 DW |
127 | return false; |
128 | if (valuelen > XATTR_SIZE_MAX || valuelen < 0) | |
129 | return false; | |
e47dcf11 DW |
130 | return true; |
131 | } | |
132 | ||
133 | /* Allocate an in-core record to hold xattrs while we rebuild the xattr data. */ | |
134 | STATIC int | |
135 | xrep_xattr_salvage_key( | |
136 | struct xrep_xattr *rx, | |
137 | int flags, | |
138 | unsigned char *name, | |
139 | int namelen, | |
140 | unsigned char *value, | |
141 | int valuelen) | |
142 | { | |
143 | struct xrep_xattr_key key = { | |
144 | .valuelen = valuelen, | |
145 | .flags = flags & XFS_ATTR_NSP_ONDISK_MASK, | |
146 | }; | |
147 | unsigned int i = 0; | |
148 | int error = 0; | |
149 | ||
150 | if (xchk_should_terminate(rx->sc, &error)) | |
151 | return error; | |
152 | ||
153 | /* | |
154 | * Truncate the name to the first character that would trip namecheck. | |
155 | * If we no longer have a name after that, ignore this attribute. | |
156 | */ | |
157 | while (i < namelen && name[i] != 0) | |
158 | i++; | |
159 | if (i == 0) | |
160 | return 0; | |
161 | key.namelen = i; | |
162 | ||
163 | trace_xrep_xattr_salvage_rec(rx->sc->ip, flags, name, key.namelen, | |
164 | valuelen); | |
165 | ||
166 | error = xfblob_store(rx->xattr_blobs, &key.name_cookie, name, | |
167 | key.namelen); | |
168 | if (error) | |
169 | return error; | |
170 | ||
171 | error = xfblob_store(rx->xattr_blobs, &key.value_cookie, value, | |
172 | key.valuelen); | |
173 | if (error) | |
174 | return error; | |
175 | ||
176 | error = xfarray_append(rx->xattr_records, &key); | |
177 | if (error) | |
178 | return error; | |
179 | ||
180 | rx->attrs_found++; | |
181 | return 0; | |
182 | } | |
183 | ||
184 | /* | |
185 | * Record a shortform extended attribute key & value for later reinsertion | |
186 | * into the inode. | |
187 | */ | |
188 | STATIC int | |
189 | xrep_xattr_salvage_sf_attr( | |
190 | struct xrep_xattr *rx, | |
191 | struct xfs_attr_sf_hdr *hdr, | |
192 | struct xfs_attr_sf_entry *sfe) | |
193 | { | |
194 | struct xfs_scrub *sc = rx->sc; | |
195 | struct xchk_xattr_buf *ab = sc->buf; | |
196 | unsigned char *name = sfe->nameval; | |
197 | unsigned char *value = &sfe->nameval[sfe->namelen]; | |
198 | ||
199 | if (!xchk_xattr_set_map(sc, ab->usedmap, (char *)name - (char *)hdr, | |
200 | sfe->namelen)) | |
201 | return 0; | |
202 | ||
203 | if (!xchk_xattr_set_map(sc, ab->usedmap, (char *)value - (char *)hdr, | |
204 | sfe->valuelen)) | |
205 | return 0; | |
206 | ||
207 | if (!xrep_xattr_want_salvage(rx, sfe->flags, sfe->nameval, | |
208 | sfe->namelen, value, sfe->valuelen)) | |
209 | return 0; | |
210 | ||
211 | return xrep_xattr_salvage_key(rx, sfe->flags, sfe->nameval, | |
212 | sfe->namelen, value, sfe->valuelen); | |
213 | } | |
214 | ||
215 | /* | |
216 | * Record a local format extended attribute key & value for later reinsertion | |
217 | * into the inode. | |
218 | */ | |
219 | STATIC int | |
220 | xrep_xattr_salvage_local_attr( | |
221 | struct xrep_xattr *rx, | |
222 | struct xfs_attr_leaf_entry *ent, | |
223 | unsigned int nameidx, | |
224 | const char *buf_end, | |
225 | struct xfs_attr_leaf_name_local *lentry) | |
226 | { | |
227 | struct xchk_xattr_buf *ab = rx->sc->buf; | |
228 | unsigned char *value; | |
229 | unsigned int valuelen; | |
230 | unsigned int namesize; | |
231 | ||
232 | /* | |
233 | * Decode the leaf local entry format. If something seems wrong, we | |
234 | * junk the attribute. | |
235 | */ | |
236 | value = &lentry->nameval[lentry->namelen]; | |
237 | valuelen = be16_to_cpu(lentry->valuelen); | |
238 | namesize = xfs_attr_leaf_entsize_local(lentry->namelen, valuelen); | |
239 | if ((char *)lentry + namesize > buf_end) | |
240 | return 0; | |
241 | if (!xrep_xattr_want_salvage(rx, ent->flags, lentry->nameval, | |
242 | lentry->namelen, value, valuelen)) | |
243 | return 0; | |
244 | if (!xchk_xattr_set_map(rx->sc, ab->usedmap, nameidx, namesize)) | |
245 | return 0; | |
246 | ||
247 | /* Try to save this attribute. */ | |
248 | return xrep_xattr_salvage_key(rx, ent->flags, lentry->nameval, | |
249 | lentry->namelen, value, valuelen); | |
250 | } | |
251 | ||
252 | /* | |
253 | * Record a remote format extended attribute key & value for later reinsertion | |
254 | * into the inode. | |
255 | */ | |
256 | STATIC int | |
257 | xrep_xattr_salvage_remote_attr( | |
258 | struct xrep_xattr *rx, | |
259 | struct xfs_attr_leaf_entry *ent, | |
260 | unsigned int nameidx, | |
261 | const char *buf_end, | |
262 | struct xfs_attr_leaf_name_remote *rentry, | |
263 | unsigned int ent_idx, | |
264 | struct xfs_buf *leaf_bp) | |
265 | { | |
266 | struct xchk_xattr_buf *ab = rx->sc->buf; | |
267 | struct xfs_da_args args = { | |
268 | .trans = rx->sc->tp, | |
269 | .dp = rx->sc->ip, | |
270 | .index = ent_idx, | |
271 | .geo = rx->sc->mp->m_attr_geo, | |
272 | .owner = rx->sc->ip->i_ino, | |
273 | .attr_filter = ent->flags & XFS_ATTR_NSP_ONDISK_MASK, | |
274 | .namelen = rentry->namelen, | |
275 | .name = rentry->name, | |
276 | .value = ab->value, | |
277 | .valuelen = be32_to_cpu(rentry->valuelen), | |
278 | }; | |
279 | unsigned int namesize; | |
280 | int error; | |
281 | ||
282 | /* | |
283 | * Decode the leaf remote entry format. If something seems wrong, we | |
284 | * junk the attribute. Note that we should never find a zero-length | |
285 | * remote attribute value. | |
286 | */ | |
287 | namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); | |
288 | if ((char *)rentry + namesize > buf_end) | |
289 | return 0; | |
290 | if (args.valuelen == 0 || | |
291 | !xrep_xattr_want_salvage(rx, ent->flags, rentry->name, | |
292 | rentry->namelen, NULL, args.valuelen)) | |
293 | return 0; | |
294 | if (!xchk_xattr_set_map(rx->sc, ab->usedmap, nameidx, namesize)) | |
295 | return 0; | |
296 | ||
297 | /* | |
298 | * Enlarge the buffer (if needed) to hold the value that we're trying | |
299 | * to salvage from the old extended attribute data. | |
300 | */ | |
301 | error = xchk_setup_xattr_buf(rx->sc, args.valuelen); | |
302 | if (error == -ENOMEM) | |
303 | error = -EDEADLOCK; | |
304 | if (error) | |
305 | return error; | |
306 | ||
307 | /* Look up the remote value and stash it for reconstruction. */ | |
308 | error = xfs_attr3_leaf_getvalue(leaf_bp, &args); | |
309 | if (error || args.rmtblkno == 0) | |
310 | goto err_free; | |
311 | ||
312 | error = xfs_attr_rmtval_get(&args); | |
313 | if (error) | |
314 | goto err_free; | |
315 | ||
316 | /* Try to save this attribute. */ | |
317 | error = xrep_xattr_salvage_key(rx, ent->flags, rentry->name, | |
318 | rentry->namelen, ab->value, args.valuelen); | |
319 | err_free: | |
320 | /* remote value was garbage, junk it */ | |
321 | if (error == -EFSBADCRC || error == -EFSCORRUPTED) | |
322 | error = 0; | |
323 | return error; | |
324 | } | |
325 | ||
326 | /* Extract every xattr key that we can from this attr fork block. */ | |
327 | STATIC int | |
328 | xrep_xattr_recover_leaf( | |
329 | struct xrep_xattr *rx, | |
330 | struct xfs_buf *bp) | |
331 | { | |
332 | struct xfs_attr3_icleaf_hdr leafhdr; | |
333 | struct xfs_scrub *sc = rx->sc; | |
334 | struct xfs_mount *mp = sc->mp; | |
335 | struct xfs_attr_leafblock *leaf; | |
336 | struct xfs_attr_leaf_name_local *lentry; | |
337 | struct xfs_attr_leaf_name_remote *rentry; | |
338 | struct xfs_attr_leaf_entry *ent; | |
339 | struct xfs_attr_leaf_entry *entries; | |
340 | struct xchk_xattr_buf *ab = rx->sc->buf; | |
341 | char *buf_end; | |
342 | size_t off; | |
343 | unsigned int nameidx; | |
344 | unsigned int hdrsize; | |
345 | int i; | |
346 | int error = 0; | |
347 | ||
348 | bitmap_zero(ab->usedmap, mp->m_attr_geo->blksize); | |
349 | ||
350 | /* Check the leaf header */ | |
351 | leaf = bp->b_addr; | |
352 | xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); | |
353 | hdrsize = xfs_attr3_leaf_hdr_size(leaf); | |
354 | xchk_xattr_set_map(sc, ab->usedmap, 0, hdrsize); | |
355 | entries = xfs_attr3_leaf_entryp(leaf); | |
356 | ||
357 | buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; | |
358 | for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { | |
359 | if (xchk_should_terminate(sc, &error)) | |
360 | return error; | |
361 | ||
362 | /* Skip key if it conflicts with something else? */ | |
363 | off = (char *)ent - (char *)leaf; | |
364 | if (!xchk_xattr_set_map(sc, ab->usedmap, off, | |
365 | sizeof(xfs_attr_leaf_entry_t))) | |
366 | continue; | |
367 | ||
368 | /* Check the name information. */ | |
369 | nameidx = be16_to_cpu(ent->nameidx); | |
370 | if (nameidx < leafhdr.firstused || | |
371 | nameidx >= mp->m_attr_geo->blksize) | |
372 | continue; | |
373 | ||
374 | if (ent->flags & XFS_ATTR_LOCAL) { | |
375 | lentry = xfs_attr3_leaf_name_local(leaf, i); | |
376 | error = xrep_xattr_salvage_local_attr(rx, ent, nameidx, | |
377 | buf_end, lentry); | |
378 | } else { | |
379 | rentry = xfs_attr3_leaf_name_remote(leaf, i); | |
380 | error = xrep_xattr_salvage_remote_attr(rx, ent, nameidx, | |
381 | buf_end, rentry, i, bp); | |
382 | } | |
383 | if (error) | |
384 | return error; | |
385 | } | |
386 | ||
387 | return 0; | |
388 | } | |
389 | ||
390 | /* Try to recover shortform attrs. */ | |
391 | STATIC int | |
392 | xrep_xattr_recover_sf( | |
393 | struct xrep_xattr *rx) | |
394 | { | |
395 | struct xfs_scrub *sc = rx->sc; | |
396 | struct xchk_xattr_buf *ab = sc->buf; | |
397 | struct xfs_attr_sf_hdr *hdr; | |
398 | struct xfs_attr_sf_entry *sfe; | |
399 | struct xfs_attr_sf_entry *next; | |
400 | struct xfs_ifork *ifp; | |
401 | unsigned char *end; | |
402 | int i; | |
403 | int error = 0; | |
404 | ||
405 | ifp = xfs_ifork_ptr(rx->sc->ip, XFS_ATTR_FORK); | |
406 | hdr = ifp->if_data; | |
407 | ||
408 | bitmap_zero(ab->usedmap, ifp->if_bytes); | |
409 | end = (unsigned char *)ifp->if_data + ifp->if_bytes; | |
410 | xchk_xattr_set_map(sc, ab->usedmap, 0, sizeof(*hdr)); | |
411 | ||
412 | sfe = xfs_attr_sf_firstentry(hdr); | |
413 | if ((unsigned char *)sfe > end) | |
414 | return 0; | |
415 | ||
416 | for (i = 0; i < hdr->count; i++) { | |
417 | if (xchk_should_terminate(sc, &error)) | |
418 | return error; | |
419 | ||
420 | next = xfs_attr_sf_nextentry(sfe); | |
421 | if ((unsigned char *)next > end) | |
422 | break; | |
423 | ||
424 | if (xchk_xattr_set_map(sc, ab->usedmap, | |
425 | (char *)sfe - (char *)hdr, | |
426 | sizeof(struct xfs_attr_sf_entry))) { | |
427 | /* | |
428 | * No conflicts with the sf entry; let's save this | |
429 | * attribute. | |
430 | */ | |
431 | error = xrep_xattr_salvage_sf_attr(rx, hdr, sfe); | |
432 | if (error) | |
433 | return error; | |
434 | } | |
435 | ||
436 | sfe = next; | |
437 | } | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | /* | |
443 | * Try to return a buffer of xattr data for a given physical extent. | |
444 | * | |
445 | * Because the buffer cache get function complains if it finds a buffer | |
446 | * matching the block number but not matching the length, we must be careful to | |
447 | * look for incore buffers (up to the maximum length of a remote value) that | |
448 | * could be hiding anywhere in the physical range. If we find an incore | |
449 | * buffer, we can pass that to the caller. Optionally, read a single block and | |
450 | * pass that back. | |
451 | * | |
452 | * Note the subtlety that remote attr value blocks for which there is no incore | |
453 | * buffer will be passed to the callback one block at a time. These buffers | |
454 | * will not have any ops attached and must be staled to prevent aliasing with | |
455 | * multiblock buffers once we drop the ILOCK. | |
456 | */ | |
457 | STATIC int | |
458 | xrep_xattr_find_buf( | |
459 | struct xfs_mount *mp, | |
460 | xfs_fsblock_t fsbno, | |
461 | xfs_extlen_t max_len, | |
462 | bool can_read, | |
463 | struct xfs_buf **bpp) | |
464 | { | |
465 | struct xrep_bufscan scan = { | |
466 | .daddr = XFS_FSB_TO_DADDR(mp, fsbno), | |
467 | .max_sectors = xrep_bufscan_max_sectors(mp, max_len), | |
468 | .daddr_step = XFS_FSB_TO_BB(mp, 1), | |
469 | }; | |
470 | struct xfs_buf *bp; | |
471 | ||
472 | while ((bp = xrep_bufscan_advance(mp, &scan)) != NULL) { | |
473 | *bpp = bp; | |
474 | return 0; | |
475 | } | |
476 | ||
477 | if (!can_read) { | |
478 | *bpp = NULL; | |
479 | return 0; | |
480 | } | |
481 | ||
482 | return xfs_buf_read(mp->m_ddev_targp, scan.daddr, XFS_FSB_TO_BB(mp, 1), | |
483 | XBF_TRYLOCK, bpp, NULL); | |
484 | } | |
485 | ||
486 | /* | |
487 | * Deal with a buffer that we found during our walk of the attr fork. | |
488 | * | |
489 | * Attribute leaf and node blocks are simple -- they're a single block, so we | |
490 | * can walk them one at a time and we never have to worry about discontiguous | |
491 | * multiblock buffers like we do for directories. | |
492 | * | |
493 | * Unfortunately, remote attr blocks add a lot of complexity here. Each disk | |
494 | * block is totally self contained, in the sense that the v5 header provides no | |
495 | * indication that there could be more data in the next block. The incore | |
496 | * buffers can span multiple blocks, though they never cross extent records. | |
497 | * However, they don't necessarily start or end on an extent record boundary. | |
498 | * Therefore, we need a special buffer find function to walk the buffer cache | |
499 | * for us. | |
500 | * | |
501 | * The caller must hold the ILOCK on the file being repaired. We use | |
502 | * XBF_TRYLOCK here to skip any locked buffer on the assumption that we don't | |
503 | * own the block and don't want to hang the system on a potentially garbage | |
504 | * buffer. | |
505 | */ | |
506 | STATIC int | |
507 | xrep_xattr_recover_block( | |
508 | struct xrep_xattr *rx, | |
509 | xfs_dablk_t dabno, | |
510 | xfs_fsblock_t fsbno, | |
511 | xfs_extlen_t max_len, | |
512 | xfs_extlen_t *actual_len) | |
513 | { | |
514 | struct xfs_da_blkinfo *info; | |
515 | struct xfs_buf *bp; | |
516 | int error; | |
517 | ||
518 | error = xrep_xattr_find_buf(rx->sc->mp, fsbno, max_len, true, &bp); | |
519 | if (error) | |
520 | return error; | |
521 | info = bp->b_addr; | |
522 | *actual_len = XFS_BB_TO_FSB(rx->sc->mp, bp->b_length); | |
523 | ||
524 | trace_xrep_xattr_recover_leafblock(rx->sc->ip, dabno, | |
525 | be16_to_cpu(info->magic)); | |
526 | ||
527 | /* | |
528 | * If the buffer has the right magic number for an attr leaf block and | |
529 | * passes a structure check (we don't care about checksums), salvage | |
530 | * as much as we can from the block. */ | |
531 | if (info->magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) && | |
532 | xrep_buf_verify_struct(bp, &xfs_attr3_leaf_buf_ops) && | |
533 | xfs_attr3_leaf_header_check(bp, rx->sc->ip->i_ino) == NULL) | |
534 | error = xrep_xattr_recover_leaf(rx, bp); | |
535 | ||
536 | /* | |
537 | * If the buffer didn't already have buffer ops set, it was read in by | |
538 | * the _find_buf function and could very well be /part/ of a multiblock | |
539 | * remote block. Mark it stale so that it doesn't hang around in | |
540 | * memory to cause problems. | |
541 | */ | |
542 | if (bp->b_ops == NULL) | |
543 | xfs_buf_stale(bp); | |
544 | ||
545 | xfs_buf_relse(bp); | |
546 | return error; | |
547 | } | |
548 | ||
549 | /* Insert one xattr key/value. */ | |
550 | STATIC int | |
551 | xrep_xattr_insert_rec( | |
552 | struct xrep_xattr *rx, | |
553 | const struct xrep_xattr_key *key) | |
554 | { | |
555 | struct xfs_da_args args = { | |
556 | .dp = rx->sc->tempip, | |
557 | .attr_filter = key->flags, | |
e47dcf11 DW |
558 | .namelen = key->namelen, |
559 | .valuelen = key->valuelen, | |
560 | .owner = rx->sc->ip->i_ino, | |
561 | }; | |
562 | struct xchk_xattr_buf *ab = rx->sc->buf; | |
563 | int error; | |
564 | ||
565 | /* | |
566 | * Grab pointers to the scrub buffer so that we can use them to insert | |
567 | * attrs into the temp file. | |
568 | */ | |
569 | args.name = ab->name; | |
570 | args.value = ab->value; | |
571 | ||
572 | /* | |
573 | * The attribute name is stored near the end of the in-core buffer, | |
574 | * though we reserve one more byte to ensure null termination. | |
575 | */ | |
576 | ab->name[XATTR_NAME_MAX] = 0; | |
577 | ||
578 | error = xfblob_load(rx->xattr_blobs, key->name_cookie, ab->name, | |
579 | key->namelen); | |
580 | if (error) | |
581 | return error; | |
582 | ||
583 | error = xfblob_free(rx->xattr_blobs, key->name_cookie); | |
584 | if (error) | |
585 | return error; | |
586 | ||
587 | error = xfblob_load(rx->xattr_blobs, key->value_cookie, args.value, | |
588 | key->valuelen); | |
589 | if (error) | |
590 | return error; | |
591 | ||
592 | error = xfblob_free(rx->xattr_blobs, key->value_cookie); | |
593 | if (error) | |
594 | return error; | |
595 | ||
596 | ab->name[key->namelen] = 0; | |
597 | ||
598 | trace_xrep_xattr_insert_rec(rx->sc->tempip, key->flags, ab->name, | |
599 | key->namelen, key->valuelen); | |
600 | ||
601 | /* | |
602 | * xfs_attr_set creates and commits its own transaction. If the attr | |
603 | * already exists, we'll just drop it during the rebuild. | |
604 | */ | |
54275d84 | 605 | error = xfs_attr_set(&args, XFS_ATTRUPDATE_CREATE); |
e47dcf11 DW |
606 | if (error == -EEXIST) |
607 | error = 0; | |
608 | ||
609 | return error; | |
610 | } | |
611 | ||
612 | /* | |
613 | * Periodically flush salvaged attributes to the temporary file. This is done | |
614 | * to reduce the memory requirements of the xattr rebuild because files can | |
615 | * contain millions of attributes. | |
616 | */ | |
617 | STATIC int | |
618 | xrep_xattr_flush_stashed( | |
619 | struct xrep_xattr *rx) | |
620 | { | |
621 | xfarray_idx_t array_cur; | |
622 | int error; | |
623 | ||
624 | /* | |
625 | * Entering this function, the scrub context has a reference to the | |
626 | * inode being repaired, the temporary file, and a scrub transaction | |
627 | * that we use during xattr salvaging to avoid livelocking if there | |
628 | * are cycles in the xattr structures. We hold ILOCK_EXCL on both | |
629 | * the inode being repaired, though it is not ijoined to the scrub | |
630 | * transaction. | |
631 | * | |
632 | * To constrain kernel memory use, we occasionally flush salvaged | |
633 | * xattrs from the xfarray and xfblob structures into the temporary | |
634 | * file in preparation for exchanging the xattr structures at the end. | |
635 | * Updating the temporary file requires a transaction, so we commit the | |
636 | * scrub transaction and drop the two ILOCKs so that xfs_attr_set can | |
637 | * allocate whatever transaction it wants. | |
638 | * | |
639 | * We still hold IOLOCK_EXCL on the inode being repaired, which | |
640 | * prevents anyone from modifying the damaged xattr data while we | |
641 | * repair it. | |
642 | */ | |
643 | error = xrep_trans_commit(rx->sc); | |
644 | if (error) | |
645 | return error; | |
646 | xchk_iunlock(rx->sc, XFS_ILOCK_EXCL); | |
647 | ||
648 | /* | |
649 | * Take the IOLOCK of the temporary file while we modify xattrs. This | |
650 | * isn't strictly required because the temporary file is never revealed | |
651 | * to userspace, but we follow the same locking rules. We still hold | |
652 | * sc->ip's IOLOCK. | |
653 | */ | |
654 | error = xrep_tempfile_iolock_polled(rx->sc); | |
655 | if (error) | |
656 | return error; | |
657 | ||
658 | /* Add all the salvaged attrs to the temporary file. */ | |
659 | foreach_xfarray_idx(rx->xattr_records, array_cur) { | |
660 | struct xrep_xattr_key key; | |
661 | ||
662 | error = xfarray_load(rx->xattr_records, array_cur, &key); | |
663 | if (error) | |
664 | return error; | |
665 | ||
666 | error = xrep_xattr_insert_rec(rx, &key); | |
667 | if (error) | |
668 | return error; | |
669 | } | |
670 | ||
671 | /* Empty out both arrays now that we've added the entries. */ | |
672 | xfarray_truncate(rx->xattr_records); | |
673 | xfblob_truncate(rx->xattr_blobs); | |
674 | ||
675 | xrep_tempfile_iounlock(rx->sc); | |
676 | ||
677 | /* Recreate the salvage transaction and relock the inode. */ | |
678 | error = xchk_trans_alloc(rx->sc, 0); | |
679 | if (error) | |
680 | return error; | |
681 | xchk_ilock(rx->sc, XFS_ILOCK_EXCL); | |
682 | return 0; | |
683 | } | |
684 | ||
685 | /* Decide if we've stashed too much xattr data in memory. */ | |
686 | static inline bool | |
687 | xrep_xattr_want_flush_stashed( | |
688 | struct xrep_xattr *rx) | |
689 | { | |
690 | unsigned long long bytes; | |
691 | ||
692 | bytes = xfarray_bytes(rx->xattr_records) + | |
693 | xfblob_bytes(rx->xattr_blobs); | |
694 | return bytes > XREP_XATTR_MAX_STASH_BYTES; | |
695 | } | |
696 | ||
697 | /* Extract as many attribute keys and values as we can. */ | |
698 | STATIC int | |
699 | xrep_xattr_recover( | |
700 | struct xrep_xattr *rx) | |
701 | { | |
702 | struct xfs_bmbt_irec got; | |
703 | struct xfs_scrub *sc = rx->sc; | |
704 | struct xfs_da_geometry *geo = sc->mp->m_attr_geo; | |
705 | xfs_fileoff_t offset; | |
706 | xfs_extlen_t len; | |
707 | xfs_dablk_t dabno; | |
708 | int nmap; | |
709 | int error; | |
710 | ||
711 | /* | |
712 | * Iterate each xattr leaf block in the attr fork to scan them for any | |
713 | * attributes that we might salvage. | |
714 | */ | |
715 | for (offset = 0; | |
716 | offset < XFS_MAX_FILEOFF; | |
717 | offset = got.br_startoff + got.br_blockcount) { | |
718 | nmap = 1; | |
719 | error = xfs_bmapi_read(sc->ip, offset, XFS_MAX_FILEOFF - offset, | |
720 | &got, &nmap, XFS_BMAPI_ATTRFORK); | |
721 | if (error) | |
722 | return error; | |
723 | if (nmap != 1) | |
724 | return -EFSCORRUPTED; | |
725 | if (!xfs_bmap_is_written_extent(&got)) | |
726 | continue; | |
727 | ||
728 | for (dabno = round_up(got.br_startoff, geo->fsbcount); | |
729 | dabno < got.br_startoff + got.br_blockcount; | |
730 | dabno += len) { | |
731 | xfs_fileoff_t curr_offset = dabno - got.br_startoff; | |
732 | xfs_extlen_t maxlen; | |
733 | ||
734 | if (xchk_should_terminate(rx->sc, &error)) | |
735 | return error; | |
736 | ||
737 | maxlen = min_t(xfs_filblks_t, INT_MAX, | |
738 | got.br_blockcount - curr_offset); | |
739 | error = xrep_xattr_recover_block(rx, dabno, | |
740 | curr_offset + got.br_startblock, | |
741 | maxlen, &len); | |
742 | if (error) | |
743 | return error; | |
744 | ||
745 | if (xrep_xattr_want_flush_stashed(rx)) { | |
746 | error = xrep_xattr_flush_stashed(rx); | |
747 | if (error) | |
748 | return error; | |
749 | } | |
750 | } | |
751 | } | |
752 | ||
753 | return 0; | |
754 | } | |
755 | ||
756 | /* | |
757 | * Reset the extended attribute fork to a state where we can start re-adding | |
758 | * the salvaged attributes. | |
759 | */ | |
760 | STATIC int | |
761 | xrep_xattr_fork_remove( | |
762 | struct xfs_scrub *sc, | |
763 | struct xfs_inode *ip) | |
764 | { | |
765 | struct xfs_attr_sf_hdr *hdr; | |
766 | struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_ATTR_FORK); | |
767 | ||
768 | /* | |
769 | * If the data fork is in btree format, we can't change di_forkoff | |
770 | * because we could run afoul of the rule that the data fork isn't | |
771 | * supposed to be in btree format if there's enough space in the fork | |
772 | * that it could have used extents format. Instead, reinitialize the | |
773 | * attr fork to have a shortform structure with zero attributes. | |
774 | */ | |
775 | if (ip->i_df.if_format == XFS_DINODE_FMT_BTREE) { | |
776 | ifp->if_format = XFS_DINODE_FMT_LOCAL; | |
777 | hdr = xfs_idata_realloc(ip, (int)sizeof(*hdr) - ifp->if_bytes, | |
778 | XFS_ATTR_FORK); | |
779 | hdr->count = 0; | |
780 | hdr->totsize = cpu_to_be16(sizeof(*hdr)); | |
781 | xfs_trans_log_inode(sc->tp, ip, | |
782 | XFS_ILOG_CORE | XFS_ILOG_ADATA); | |
783 | return 0; | |
784 | } | |
785 | ||
786 | /* If we still have attr fork extents, something's wrong. */ | |
787 | if (ifp->if_nextents != 0) { | |
788 | struct xfs_iext_cursor icur; | |
789 | struct xfs_bmbt_irec irec; | |
790 | unsigned int i = 0; | |
791 | ||
792 | xfs_emerg(sc->mp, | |
793 | "inode 0x%llx attr fork still has %llu attr extents, format %d?!", | |
794 | ip->i_ino, ifp->if_nextents, ifp->if_format); | |
795 | for_each_xfs_iext(ifp, &icur, &irec) { | |
796 | xfs_err(sc->mp, | |
797 | "[%u]: startoff %llu startblock %llu blockcount %llu state %u", | |
798 | i++, irec.br_startoff, | |
799 | irec.br_startblock, irec.br_blockcount, | |
800 | irec.br_state); | |
801 | } | |
802 | ASSERT(0); | |
803 | return -EFSCORRUPTED; | |
804 | } | |
805 | ||
806 | xfs_attr_fork_remove(ip, sc->tp); | |
807 | return 0; | |
808 | } | |
809 | ||
810 | /* | |
811 | * Free all the attribute fork blocks of the file being repaired and delete the | |
812 | * fork. The caller must ILOCK the scrub file and join it to the transaction. | |
813 | * This function returns with the inode joined to a clean transaction. | |
814 | */ | |
815 | int | |
816 | xrep_xattr_reset_fork( | |
817 | struct xfs_scrub *sc) | |
818 | { | |
819 | int error; | |
820 | ||
821 | trace_xrep_xattr_reset_fork(sc->ip, sc->ip); | |
822 | ||
823 | /* Unmap all the attr blocks. */ | |
824 | if (xfs_ifork_has_extents(&sc->ip->i_af)) { | |
825 | error = xrep_reap_ifork(sc, sc->ip, XFS_ATTR_FORK); | |
826 | if (error) | |
827 | return error; | |
828 | } | |
829 | ||
830 | error = xrep_xattr_fork_remove(sc, sc->ip); | |
831 | if (error) | |
832 | return error; | |
833 | ||
834 | return xfs_trans_roll_inode(&sc->tp, sc->ip); | |
835 | } | |
836 | ||
837 | /* | |
838 | * Free all the attribute fork blocks of the temporary file and delete the attr | |
839 | * fork. The caller must ILOCK the tempfile and join it to the transaction. | |
840 | * This function returns with the inode joined to a clean scrub transaction. | |
841 | */ | |
842 | STATIC int | |
843 | xrep_xattr_reset_tempfile_fork( | |
844 | struct xfs_scrub *sc) | |
845 | { | |
846 | int error; | |
847 | ||
848 | trace_xrep_xattr_reset_fork(sc->ip, sc->tempip); | |
849 | ||
850 | /* | |
851 | * Wipe out the attr fork of the temp file so that regular inode | |
852 | * inactivation won't trip over the corrupt attr fork. | |
853 | */ | |
854 | if (xfs_ifork_has_extents(&sc->tempip->i_af)) { | |
855 | error = xrep_reap_ifork(sc, sc->tempip, XFS_ATTR_FORK); | |
856 | if (error) | |
857 | return error; | |
858 | } | |
859 | ||
860 | return xrep_xattr_fork_remove(sc, sc->tempip); | |
861 | } | |
862 | ||
863 | /* | |
864 | * Find all the extended attributes for this inode by scraping them out of the | |
865 | * attribute key blocks by hand, and flushing them into the temp file. | |
866 | * When we're done, free the staging memory before exchanging the xattr | |
867 | * structures to reduce memory usage. | |
868 | */ | |
869 | STATIC int | |
870 | xrep_xattr_salvage_attributes( | |
871 | struct xrep_xattr *rx) | |
872 | { | |
873 | struct xfs_inode *ip = rx->sc->ip; | |
874 | int error; | |
875 | ||
876 | /* Short format xattrs are easy! */ | |
877 | if (rx->sc->ip->i_af.if_format == XFS_DINODE_FMT_LOCAL) { | |
878 | error = xrep_xattr_recover_sf(rx); | |
879 | if (error) | |
880 | return error; | |
881 | ||
882 | return xrep_xattr_flush_stashed(rx); | |
883 | } | |
884 | ||
885 | /* | |
886 | * For non-inline xattr structures, the salvage function scans the | |
887 | * buffer cache looking for potential attr leaf blocks. The scan | |
888 | * requires the ability to lock any buffer found and runs independently | |
889 | * of any transaction <-> buffer item <-> buffer linkage. Therefore, | |
890 | * roll the transaction to ensure there are no buffers joined. We hold | |
891 | * the ILOCK independently of the transaction. | |
892 | */ | |
893 | error = xfs_trans_roll(&rx->sc->tp); | |
894 | if (error) | |
895 | return error; | |
896 | ||
897 | error = xfs_iread_extents(rx->sc->tp, ip, XFS_ATTR_FORK); | |
898 | if (error) | |
899 | return error; | |
900 | ||
901 | error = xrep_xattr_recover(rx); | |
902 | if (error) | |
903 | return error; | |
904 | ||
905 | return xrep_xattr_flush_stashed(rx); | |
906 | } | |
907 | ||
908 | /* | |
909 | * Prepare both inodes' attribute forks for an exchange. Promote the tempfile | |
910 | * from short format to leaf format, and if the file being repaired has a short | |
911 | * format attr fork, turn it into an empty extent list. | |
912 | */ | |
913 | STATIC int | |
914 | xrep_xattr_swap_prep( | |
915 | struct xfs_scrub *sc, | |
916 | bool temp_local, | |
917 | bool ip_local) | |
918 | { | |
919 | int error; | |
920 | ||
921 | /* | |
922 | * If the tempfile's attributes are in shortform format, convert that | |
923 | * to a single leaf extent so that we can use the atomic mapping | |
924 | * exchange. | |
925 | */ | |
926 | if (temp_local) { | |
927 | struct xfs_da_args args = { | |
928 | .dp = sc->tempip, | |
929 | .geo = sc->mp->m_attr_geo, | |
930 | .whichfork = XFS_ATTR_FORK, | |
931 | .trans = sc->tp, | |
932 | .total = 1, | |
933 | .owner = sc->ip->i_ino, | |
934 | }; | |
935 | ||
936 | error = xfs_attr_shortform_to_leaf(&args); | |
937 | if (error) | |
938 | return error; | |
939 | ||
940 | /* | |
941 | * Roll the deferred log items to get us back to a clean | |
942 | * transaction. | |
943 | */ | |
944 | error = xfs_defer_finish(&sc->tp); | |
945 | if (error) | |
946 | return error; | |
947 | } | |
948 | ||
949 | /* | |
950 | * If the file being repaired had a shortform attribute fork, convert | |
951 | * that to an empty extent list in preparation for the atomic mapping | |
952 | * exchange. | |
953 | */ | |
954 | if (ip_local) { | |
955 | struct xfs_ifork *ifp; | |
956 | ||
957 | ifp = xfs_ifork_ptr(sc->ip, XFS_ATTR_FORK); | |
958 | ||
959 | xfs_idestroy_fork(ifp); | |
960 | ifp->if_format = XFS_DINODE_FMT_EXTENTS; | |
961 | ifp->if_nextents = 0; | |
962 | ifp->if_bytes = 0; | |
963 | ifp->if_data = NULL; | |
964 | ifp->if_height = 0; | |
965 | ||
966 | xfs_trans_log_inode(sc->tp, sc->ip, | |
967 | XFS_ILOG_CORE | XFS_ILOG_ADATA); | |
968 | } | |
969 | ||
970 | return 0; | |
971 | } | |
972 | ||
973 | /* Exchange the temporary file's attribute fork with the one being repaired. */ | |
974 | STATIC int | |
975 | xrep_xattr_swap( | |
976 | struct xfs_scrub *sc, | |
977 | struct xrep_tempexch *tx) | |
978 | { | |
979 | bool ip_local, temp_local; | |
980 | int error = 0; | |
981 | ||
982 | ip_local = sc->ip->i_af.if_format == XFS_DINODE_FMT_LOCAL; | |
983 | temp_local = sc->tempip->i_af.if_format == XFS_DINODE_FMT_LOCAL; | |
984 | ||
985 | /* | |
986 | * If the both files have a local format attr fork and the rebuilt | |
987 | * xattr data would fit in the repaired file's attr fork, just copy | |
988 | * the contents from the tempfile and declare ourselves done. | |
989 | */ | |
990 | if (ip_local && temp_local) { | |
991 | int forkoff; | |
992 | int newsize; | |
993 | ||
994 | newsize = xfs_attr_sf_totsize(sc->tempip); | |
995 | forkoff = xfs_attr_shortform_bytesfit(sc->ip, newsize); | |
996 | if (forkoff > 0) { | |
997 | sc->ip->i_forkoff = forkoff; | |
998 | xrep_tempfile_copyout_local(sc, XFS_ATTR_FORK); | |
999 | return 0; | |
1000 | } | |
1001 | } | |
1002 | ||
1003 | /* Otherwise, make sure both attr forks are in block-mapping mode. */ | |
1004 | error = xrep_xattr_swap_prep(sc, temp_local, ip_local); | |
1005 | if (error) | |
1006 | return error; | |
1007 | ||
1008 | return xrep_tempexch_contents(sc, tx); | |
1009 | } | |
1010 | ||
1011 | /* | |
1012 | * Exchange the new extended attribute data (which we created in the tempfile) | |
1013 | * with the file being repaired. | |
1014 | */ | |
1015 | STATIC int | |
1016 | xrep_xattr_rebuild_tree( | |
1017 | struct xrep_xattr *rx) | |
1018 | { | |
1019 | struct xfs_scrub *sc = rx->sc; | |
1020 | int error; | |
1021 | ||
1022 | /* | |
1023 | * If we didn't find any attributes to salvage, repair the file by | |
1024 | * zapping its attr fork. | |
1025 | */ | |
1026 | if (rx->attrs_found == 0) { | |
1027 | xfs_trans_ijoin(sc->tp, sc->ip, 0); | |
1028 | error = xrep_xattr_reset_fork(sc); | |
1029 | if (error) | |
1030 | return error; | |
1031 | ||
1032 | goto forget_acls; | |
1033 | } | |
1034 | ||
1035 | trace_xrep_xattr_rebuild_tree(sc->ip, sc->tempip); | |
1036 | ||
1037 | /* | |
1038 | * Commit the repair transaction and drop the ILOCKs so that we can use | |
1039 | * the atomic file content exchange helper functions to compute the | |
1040 | * correct resource reservations. | |
1041 | * | |
1042 | * We still hold IOLOCK_EXCL (aka i_rwsem) which will prevent xattr | |
1043 | * modifications, but there's nothing to prevent userspace from reading | |
1044 | * the attributes until we're ready for the exchange operation. Reads | |
1045 | * will return -EIO without shutting down the fs, so we're ok with | |
1046 | * that. | |
1047 | */ | |
1048 | error = xrep_trans_commit(sc); | |
1049 | if (error) | |
1050 | return error; | |
1051 | ||
1052 | xchk_iunlock(sc, XFS_ILOCK_EXCL); | |
1053 | ||
1054 | /* | |
1055 | * Take the IOLOCK on the temporary file so that we can run xattr | |
1056 | * operations with the same locks held as we would for a normal file. | |
1057 | * We still hold sc->ip's IOLOCK. | |
1058 | */ | |
1059 | error = xrep_tempfile_iolock_polled(rx->sc); | |
1060 | if (error) | |
1061 | return error; | |
1062 | ||
1063 | /* Allocate exchange transaction and lock both inodes. */ | |
1064 | error = xrep_tempexch_trans_alloc(rx->sc, XFS_ATTR_FORK, &rx->tx); | |
1065 | if (error) | |
1066 | return error; | |
1067 | ||
1068 | /* | |
1069 | * Exchange the blocks mapped by the tempfile's attr fork with the file | |
1070 | * being repaired. The old attr blocks will then be attached to the | |
1071 | * tempfile, so reap its attr fork. | |
1072 | */ | |
1073 | error = xrep_xattr_swap(sc, &rx->tx); | |
1074 | if (error) | |
1075 | return error; | |
1076 | ||
1077 | error = xrep_xattr_reset_tempfile_fork(sc); | |
1078 | if (error) | |
1079 | return error; | |
1080 | ||
1081 | /* | |
1082 | * Roll to get a transaction without any inodes joined to it. Then we | |
1083 | * can drop the tempfile's ILOCK and IOLOCK before doing more work on | |
1084 | * the scrub target file. | |
1085 | */ | |
1086 | error = xfs_trans_roll(&sc->tp); | |
1087 | if (error) | |
1088 | return error; | |
1089 | ||
1090 | xrep_tempfile_iunlock(sc); | |
1091 | xrep_tempfile_iounlock(sc); | |
1092 | ||
1093 | forget_acls: | |
1094 | /* Invalidate cached ACLs now that we've reloaded all the xattrs. */ | |
1095 | xfs_forget_acl(VFS_I(sc->ip), SGI_ACL_FILE); | |
1096 | xfs_forget_acl(VFS_I(sc->ip), SGI_ACL_DEFAULT); | |
1097 | return 0; | |
1098 | } | |
1099 | ||
1100 | /* Tear down all the incore scan stuff we created. */ | |
1101 | STATIC void | |
1102 | xrep_xattr_teardown( | |
1103 | struct xrep_xattr *rx) | |
1104 | { | |
1105 | xfblob_destroy(rx->xattr_blobs); | |
1106 | xfarray_destroy(rx->xattr_records); | |
1107 | kfree(rx); | |
1108 | } | |
1109 | ||
1110 | /* Set up the filesystem scan so we can regenerate extended attributes. */ | |
1111 | STATIC int | |
1112 | xrep_xattr_setup_scan( | |
1113 | struct xfs_scrub *sc, | |
1114 | struct xrep_xattr **rxp) | |
1115 | { | |
1116 | struct xrep_xattr *rx; | |
1117 | char *descr; | |
1118 | int max_len; | |
1119 | int error; | |
1120 | ||
1121 | rx = kzalloc(sizeof(struct xrep_xattr), XCHK_GFP_FLAGS); | |
1122 | if (!rx) | |
1123 | return -ENOMEM; | |
1124 | rx->sc = sc; | |
1125 | ||
1126 | /* | |
1127 | * Allocate enough memory to handle loading local attr values from the | |
1128 | * xfblob data while flushing stashed attrs to the temporary file. | |
1129 | * We only realloc the buffer when salvaging remote attr values. | |
1130 | */ | |
1131 | max_len = xfs_attr_leaf_entsize_local_max(sc->mp->m_attr_geo->blksize); | |
1132 | error = xchk_setup_xattr_buf(rx->sc, max_len); | |
1133 | if (error == -ENOMEM) | |
1134 | error = -EDEADLOCK; | |
1135 | if (error) | |
1136 | goto out_rx; | |
1137 | ||
1138 | /* Set up some staging for salvaged attribute keys and values */ | |
1139 | descr = xchk_xfile_ino_descr(sc, "xattr keys"); | |
1140 | error = xfarray_create(descr, 0, sizeof(struct xrep_xattr_key), | |
1141 | &rx->xattr_records); | |
1142 | kfree(descr); | |
1143 | if (error) | |
1144 | goto out_rx; | |
1145 | ||
1146 | descr = xchk_xfile_ino_descr(sc, "xattr names"); | |
1147 | error = xfblob_create(descr, &rx->xattr_blobs); | |
1148 | kfree(descr); | |
1149 | if (error) | |
1150 | goto out_keys; | |
1151 | ||
1152 | *rxp = rx; | |
1153 | return 0; | |
1154 | out_keys: | |
1155 | xfarray_destroy(rx->xattr_records); | |
1156 | out_rx: | |
1157 | kfree(rx); | |
1158 | return error; | |
1159 | } | |
1160 | ||
1161 | /* | |
1162 | * Repair the extended attribute metadata. | |
1163 | * | |
1164 | * XXX: Remote attribute value buffers encompass the entire (up to 64k) buffer. | |
1165 | * The buffer cache in XFS can't handle aliased multiblock buffers, so this | |
1166 | * might misbehave if the attr fork is crosslinked with other filesystem | |
1167 | * metadata. | |
1168 | */ | |
1169 | int | |
1170 | xrep_xattr( | |
1171 | struct xfs_scrub *sc) | |
1172 | { | |
1173 | struct xrep_xattr *rx = NULL; | |
1174 | int error; | |
1175 | ||
1176 | if (!xfs_inode_hasattr(sc->ip)) | |
1177 | return -ENOENT; | |
1178 | ||
1179 | /* The rmapbt is required to reap the old attr fork. */ | |
1180 | if (!xfs_has_rmapbt(sc->mp)) | |
1181 | return -EOPNOTSUPP; | |
1182 | ||
1183 | error = xrep_xattr_setup_scan(sc, &rx); | |
1184 | if (error) | |
1185 | return error; | |
1186 | ||
1187 | ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL); | |
1188 | ||
1189 | error = xrep_xattr_salvage_attributes(rx); | |
1190 | if (error) | |
1191 | goto out_scan; | |
1192 | ||
1193 | /* Last chance to abort before we start committing fixes. */ | |
1194 | if (xchk_should_terminate(sc, &error)) | |
1195 | goto out_scan; | |
1196 | ||
1197 | error = xrep_xattr_rebuild_tree(rx); | |
1198 | if (error) | |
1199 | goto out_scan; | |
1200 | ||
1201 | out_scan: | |
1202 | xrep_xattr_teardown(rx); | |
1203 | return error; | |
1204 | } |