Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * Copyright (C) 2022-2023 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_log_format.h"
15 : #include "xfs_trans.h"
16 : #include "xfs_inode.h"
17 : #include "xfs_inode_fork.h"
18 : #include "xfs_alloc.h"
19 : #include "xfs_bmap.h"
20 : #include "xfs_rmap.h"
21 : #include "xfs_refcount.h"
22 : #include "xfs_quota.h"
23 : #include "xfs_ialloc.h"
24 : #include "xfs_ag.h"
25 : #include "xfs_error.h"
26 : #include "xfs_errortag.h"
27 : #include "xfs_icache.h"
28 : #include "xfs_refcount_btree.h"
29 : #include "scrub/xfs_scrub.h"
30 : #include "scrub/scrub.h"
31 : #include "scrub/common.h"
32 : #include "scrub/trace.h"
33 : #include "scrub/repair.h"
34 : #include "scrub/bitmap.h"
35 : #include "scrub/reap.h"
36 :
37 : /*
38 : * CoW Fork Mapping Repair
39 : * =======================
40 : *
41 : * Although CoW staging extents are owned by incore CoW inode forks, on disk
42 : * they are owned by the refcount btree. The ondisk metadata does not record
43 : * any ownership information, which limits what we can do to repair the
44 : * mappings in the CoW fork. At most, we can replace ifork mappings that lack
45 : * an entry in the refcount btree or are described by a reverse mapping record
46 : * whose owner is not OWN_COW.
47 : *
48 : * Replacing extents is also tricky -- we can't touch written CoW fork extents
49 : * since they are undergoing writeback, and delalloc extents do not require
50 : * repair since they only exist incore. Hence the most we can do is find the
51 : * bad parts of unwritten mappings, allocate a replacement set of blocks, and
52 : * replace the incore mapping. We use the regular reaping process to unmap
53 : * or free the discarded blocks, as appropriate.
54 : */
55 : struct xrep_cow {
56 : struct xfs_scrub *sc;
57 :
58 : /* Bitmap of file offset ranges that need replacing. */
59 : struct xbitmap bad_fileoffs;
60 :
61 : /* Bitmap of fsblocks that were removed from the CoW fork. */
62 : struct xfsb_bitmap old_cowfork_fsblocks;
63 :
64 : /* CoW fork mappings used to scan for bad CoW staging extents. */
65 : struct xfs_bmbt_irec irec;
66 :
67 : /* refcount btree block number of irec.br_startblock */
68 : unsigned int irec_startbno;
69 :
70 : /* refcount btree block number of the next refcount record we expect */
71 : unsigned int next_bno;
72 : };
73 :
74 : /* CoW staging extent. */
75 : struct xrep_cow_extent {
76 : xfs_fsblock_t fsbno;
77 : xfs_extlen_t len;
78 : };
79 :
80 : /*
81 : * Mark the part of the file range that corresponds to the given physical
82 : * space. Caller must ensure that the physical range is within xc->irec.
83 : */
84 : STATIC int
85 1410 : xrep_cow_mark_file_range(
86 : struct xrep_cow *xc,
87 : xfs_fsblock_t startblock,
88 : xfs_filblks_t blockcount)
89 : {
90 1410 : xfs_fileoff_t startoff;
91 :
92 1410 : startoff = xc->irec.br_startoff +
93 1410 : (startblock - xc->irec.br_startblock);
94 :
95 1410 : trace_xrep_cow_mark_file_range(xc->sc->ip, startblock, startoff,
96 : blockcount);
97 :
98 1410 : return xbitmap_set(&xc->bad_fileoffs, startoff, blockcount);
99 : }
100 :
101 : /*
102 : * Trim @src to fit within the CoW fork mapping being examined, and put the
103 : * result in @dst.
104 : */
105 : static inline void
106 1410 : xrep_cow_trim_refcount(
107 : struct xrep_cow *xc,
108 : struct xfs_refcount_irec *dst,
109 : const struct xfs_refcount_irec *src)
110 : {
111 1410 : unsigned int adj;
112 :
113 2820 : memcpy(dst, src, sizeof(*dst));
114 :
115 1410 : if (dst->rc_startblock < xc->irec_startbno) {
116 577 : adj = xc->irec_startbno - dst->rc_startblock;
117 577 : dst->rc_blockcount -= adj;
118 577 : dst->rc_startblock += adj;
119 : }
120 :
121 1410 : if (dst->rc_startblock + dst->rc_blockcount >
122 1410 : xc->irec_startbno + xc->irec.br_blockcount) {
123 578 : adj = (dst->rc_startblock + dst->rc_blockcount) -
124 578 : (xc->irec_startbno + xc->irec.br_blockcount);
125 578 : dst->rc_blockcount -= adj;
126 : }
127 1410 : }
128 :
129 : /* Mark any shared CoW staging extents. */
130 : STATIC int
131 0 : xrep_cow_mark_shared_staging(
132 : struct xfs_btree_cur *cur,
133 : const struct xfs_refcount_irec *rec,
134 : void *priv)
135 : {
136 0 : struct xrep_cow *xc = priv;
137 0 : struct xfs_refcount_irec rrec;
138 0 : xfs_fsblock_t fsbno;
139 :
140 0 : if (!xfs_refcount_check_domain(rec) ||
141 0 : rec->rc_domain != XFS_REFC_DOMAIN_SHARED)
142 : return -EFSCORRUPTED;
143 :
144 0 : xrep_cow_trim_refcount(xc, &rrec, rec);
145 :
146 0 : fsbno = XFS_AGB_TO_FSB(xc->sc->mp, cur->bc_ag.pag->pag_agno,
147 : rrec.rc_startblock);
148 0 : return xrep_cow_mark_file_range(xc, fsbno, rrec.rc_blockcount);
149 : }
150 :
151 : /*
152 : * Mark any portion of the CoW fork file offset range where there is not a CoW
153 : * staging extent record in the refcountbt, and keep a record of where we did
154 : * find correct refcountbt records. Staging records are always cleaned out at
155 : * mount time, so any two inodes trying to map the same staging area would have
156 : * already taken the fs down due to refcount btree verifier errors. Hence this
157 : * inode should be the sole creator of the staging extent records ondisk.
158 : */
159 : STATIC int
160 1410 : xrep_cow_mark_missing_staging(
161 : struct xfs_btree_cur *cur,
162 : const struct xfs_refcount_irec *rec,
163 : void *priv)
164 : {
165 1410 : struct xrep_cow *xc = priv;
166 1410 : struct xfs_refcount_irec rrec;
167 1410 : int error;
168 :
169 1410 : if (!xfs_refcount_check_domain(rec) ||
170 1410 : rec->rc_domain != XFS_REFC_DOMAIN_COW)
171 : return -EFSCORRUPTED;
172 :
173 1410 : xrep_cow_trim_refcount(xc, &rrec, rec);
174 :
175 1410 : if (xc->next_bno >= rrec.rc_startblock)
176 1410 : goto next;
177 :
178 0 : error = xrep_cow_mark_file_range(xc,
179 0 : XFS_AGB_TO_FSB(xc->sc->mp, cur->bc_ag.pag->pag_agno,
180 : xc->next_bno),
181 0 : rrec.rc_startblock - xc->next_bno);
182 0 : if (error)
183 : return error;
184 :
185 0 : next:
186 1410 : xc->next_bno = rrec.rc_startblock + rrec.rc_blockcount;
187 1410 : return 0;
188 : }
189 :
190 : /*
191 : * Mark any area that does not correspond to a CoW staging rmap. These are
192 : * cross-linked areas that must be avoided.
193 : */
194 : STATIC int
195 1410 : xrep_cow_mark_missing_staging_rmap(
196 : struct xfs_btree_cur *cur,
197 : const struct xfs_rmap_irec *rec,
198 : void *priv)
199 : {
200 1410 : struct xrep_cow *xc = priv;
201 1410 : xfs_fsblock_t fsbno;
202 1410 : xfs_agblock_t rec_bno;
203 1410 : xfs_extlen_t rec_len;
204 1410 : unsigned int adj;
205 :
206 1410 : if (rec->rm_owner == XFS_RMAP_OWN_COW)
207 : return 0;
208 :
209 0 : rec_bno = rec->rm_startblock;
210 0 : rec_len = rec->rm_blockcount;
211 0 : if (rec_bno < xc->irec_startbno) {
212 0 : adj = xc->irec_startbno - rec_bno;
213 0 : rec_len -= adj;
214 0 : rec_bno += adj;
215 : }
216 :
217 0 : if (rec_bno + rec_len > xc->irec_startbno + xc->irec.br_blockcount) {
218 0 : adj = (rec_bno + rec_len) -
219 0 : (xc->irec_startbno + xc->irec.br_blockcount);
220 0 : rec_len -= adj;
221 : }
222 :
223 0 : fsbno = XFS_AGB_TO_FSB(xc->sc->mp, cur->bc_ag.pag->pag_agno, rec_bno);
224 0 : return xrep_cow_mark_file_range(xc, fsbno, rec_len);
225 : }
226 :
227 : /*
228 : * Find any part of the CoW fork mapping that isn't a single-owner CoW staging
229 : * extent and mark the corresponding part of the file range in the bitmap.
230 : */
231 : STATIC int
232 1410 : xrep_cow_find_bad(
233 : struct xrep_cow *xc)
234 : {
235 1410 : struct xfs_refcount_irec rc_low = { 0 };
236 1410 : struct xfs_refcount_irec rc_high = { 0 };
237 1410 : struct xfs_rmap_irec rm_low = { 0 };
238 1410 : struct xfs_rmap_irec rm_high = { 0 };
239 1410 : struct xfs_perag *pag;
240 1410 : struct xfs_scrub *sc = xc->sc;
241 1410 : xfs_agnumber_t agno;
242 1410 : int error;
243 :
244 1410 : agno = XFS_FSB_TO_AGNO(sc->mp, xc->irec.br_startblock);
245 1410 : xc->irec_startbno = XFS_FSB_TO_AGBNO(sc->mp, xc->irec.br_startblock);
246 :
247 1410 : pag = xfs_perag_get(sc->mp, agno);
248 1410 : if (!pag)
249 : return -EFSCORRUPTED;
250 :
251 1410 : error = xrep_ag_init(sc, pag, &sc->sa);
252 1410 : if (error)
253 0 : goto out_pag;
254 :
255 : /* Mark any CoW fork extents that are shared. */
256 1410 : rc_low.rc_startblock = xc->irec_startbno;
257 1410 : rc_high.rc_startblock = xc->irec_startbno + xc->irec.br_blockcount - 1;
258 1410 : rc_low.rc_domain = rc_high.rc_domain = XFS_REFC_DOMAIN_SHARED;
259 1410 : error = xfs_refcount_query_range(sc->sa.refc_cur, &rc_low, &rc_high,
260 : xrep_cow_mark_shared_staging, xc);
261 1410 : if (error)
262 0 : goto out_sa;
263 :
264 : /* Make sure there are CoW staging extents for the whole mapping. */
265 1410 : rc_low.rc_startblock = xc->irec_startbno;
266 1410 : rc_high.rc_startblock = xc->irec_startbno + xc->irec.br_blockcount - 1;
267 1410 : rc_low.rc_domain = rc_high.rc_domain = XFS_REFC_DOMAIN_COW;
268 1410 : xc->next_bno = xc->irec_startbno;
269 1410 : error = xfs_refcount_query_range(sc->sa.refc_cur, &rc_low, &rc_high,
270 : xrep_cow_mark_missing_staging, xc);
271 1410 : if (error)
272 0 : goto out_sa;
273 :
274 1410 : if (xc->next_bno < xc->irec_startbno + xc->irec.br_blockcount) {
275 0 : error = xrep_cow_mark_file_range(xc,
276 0 : XFS_AGB_TO_FSB(sc->mp, pag->pag_agno,
277 : xc->next_bno),
278 : xc->irec_startbno + xc->irec.br_blockcount -
279 : xc->next_bno);
280 0 : if (error)
281 0 : goto out_sa;
282 : }
283 :
284 : /* Mark any area has an rmap that isn't a COW staging extent. */
285 1410 : rm_low.rm_startblock = xc->irec_startbno;
286 1410 : memset(&rm_high, 0xFF, sizeof(rm_high));
287 1410 : rm_high.rm_startblock = xc->irec_startbno + xc->irec.br_blockcount - 1;
288 1410 : error = xfs_rmap_query_range(sc->sa.rmap_cur, &rm_low, &rm_high,
289 : xrep_cow_mark_missing_staging_rmap, xc);
290 1410 : if (error)
291 0 : goto out_sa;
292 :
293 : /*
294 : * If userspace is forcing us to rebuild the CoW fork or someone turned
295 : * on the debugging knob, replace everything in the CoW fork.
296 : */
297 1410 : if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) ||
298 0 : XFS_TEST_ERROR(false, sc->mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) {
299 1410 : error = xrep_cow_mark_file_range(xc, xc->irec.br_startblock,
300 : xc->irec.br_blockcount);
301 1410 : if (error)
302 : return error;
303 : }
304 :
305 1410 : out_sa:
306 1410 : xchk_ag_free(sc, &sc->sa);
307 1410 : out_pag:
308 1410 : xfs_perag_put(pag);
309 1410 : return 0;
310 : }
311 :
312 : /*
313 : * Allocate a replacement CoW staging extent of up to the given number of
314 : * blocks, and fill out the mapping.
315 : */
316 : STATIC int
317 1724 : xrep_cow_alloc(
318 : struct xfs_scrub *sc,
319 : xfs_filblks_t maxlen,
320 : struct xrep_cow_extent *repl)
321 : {
322 1724 : struct xfs_alloc_arg args = {
323 1724 : .tp = sc->tp,
324 1724 : .mp = sc->mp,
325 : .oinfo = XFS_RMAP_OINFO_SKIP_UPDATE,
326 : .minlen = 1,
327 : .maxlen = maxlen,
328 : .prod = 1,
329 : .resv = XFS_AG_RESV_NONE,
330 : .datatype = XFS_ALLOC_USERDATA,
331 : };
332 1724 : int error;
333 :
334 1724 : error = xfs_trans_reserve_more(sc->tp, maxlen, 0);
335 1724 : if (error)
336 : return error;
337 :
338 1724 : error = xfs_alloc_vextent_start_ag(&args,
339 1724 : XFS_INO_TO_FSB(sc->mp, sc->ip->i_ino));
340 1724 : if (error)
341 : return error;
342 1724 : if (args.fsbno == NULLFSBLOCK)
343 : return -ENOSPC;
344 :
345 1724 : xfs_refcount_alloc_cow_extent(sc->tp, args.fsbno, args.len);
346 :
347 1724 : repl->fsbno = args.fsbno;
348 1724 : repl->len = args.len;
349 1724 : return 0;
350 : }
351 :
352 : /*
353 : * Look up the current CoW fork mapping so that we only allocate enough to
354 : * replace a single mapping. If we don't find a mapping that covers the start
355 : * of the file range, or we find a delalloc or written extent, something is
356 : * seriously wrong, since we didn't drop the ILOCK.
357 : */
358 : static inline int
359 1724 : xrep_cow_find_mapping(
360 : struct xrep_cow *xc,
361 : struct xfs_iext_cursor *icur,
362 : xfs_fileoff_t startoff,
363 : struct xfs_bmbt_irec *got)
364 : {
365 1724 : struct xfs_inode *ip = xc->sc->ip;
366 1724 : struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
367 :
368 1724 : if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got))
369 0 : goto bad;
370 :
371 1724 : if (got->br_startoff > startoff)
372 0 : goto bad;
373 :
374 1724 : if (got->br_blockcount == 0)
375 0 : goto bad;
376 :
377 1724 : if (isnullstartblock(got->br_startblock))
378 0 : goto bad;
379 :
380 1724 : if (xfs_bmap_is_written_extent(got))
381 0 : goto bad;
382 :
383 : return 0;
384 0 : bad:
385 0 : ASSERT(0);
386 0 : return -EFSCORRUPTED;
387 : }
388 :
389 : #define REPLACE_LEFT_SIDE (1U << 0)
390 : #define REPLACE_RIGHT_SIDE (1U << 1)
391 :
392 : /*
393 : * Given a CoW fork mapping @got and a replacement mapping @repl, remap the
394 : * beginning of @got with the space described by @rep.
395 : */
396 : static inline void
397 1724 : xrep_cow_replace_mapping(
398 : struct xfs_inode *ip,
399 : struct xfs_iext_cursor *icur,
400 : const struct xfs_bmbt_irec *got,
401 : const struct xrep_cow_extent *repl)
402 : {
403 1724 : struct xfs_bmbt_irec new = *got; /* struct copy */
404 :
405 1724 : ASSERT(repl->len > 0);
406 1724 : ASSERT(!isnullstartblock(got->br_startblock));
407 :
408 1724 : trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len);
409 :
410 1724 : if (got->br_blockcount == repl->len) {
411 : /*
412 : * The new extent is a complete replacement for the existing
413 : * extent. Update the COW fork record.
414 : */
415 1410 : new.br_startblock = repl->fsbno;
416 1410 : xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
417 1410 : return;
418 : }
419 :
420 : /*
421 : * The new extent can replace the beginning of the COW fork record.
422 : * Move the left side of @got upwards, then insert the new record.
423 : */
424 314 : new.br_startoff += repl->len;
425 314 : new.br_startblock += repl->len;
426 314 : new.br_blockcount -= repl->len;
427 314 : xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
428 :
429 314 : new.br_startoff = got->br_startoff;
430 314 : new.br_startblock = repl->fsbno;
431 314 : new.br_blockcount = repl->len;
432 314 : xfs_iext_insert(ip, icur, &new, BMAP_COWFORK);
433 : }
434 :
435 : /*
436 : * Replace the unwritten CoW staging extent backing the given file range with a
437 : * new space extent that isn't as problematic.
438 : */
439 : STATIC int
440 1724 : xrep_cow_replace_range(
441 : struct xrep_cow *xc,
442 : xfs_fileoff_t startoff,
443 : xfs_extlen_t *blockcount)
444 : {
445 1724 : struct xfs_iext_cursor icur;
446 1724 : struct xrep_cow_extent repl;
447 1724 : struct xfs_bmbt_irec got;
448 1724 : struct xfs_scrub *sc = xc->sc;
449 1724 : xfs_fileoff_t nextoff;
450 1724 : int error;
451 :
452 : /*
453 : * Put the existing CoW fork mapping in @got. If @got ends before
454 : * @rep, truncate @rep so we only replace one extent mapping at a time.
455 : */
456 1724 : error = xrep_cow_find_mapping(xc, &icur, startoff, &got);
457 1724 : if (error)
458 : return error;
459 1724 : nextoff = min(startoff + *blockcount,
460 : got.br_startoff + got.br_blockcount);
461 :
462 : /*
463 : * Allocate a replacement extent. If we don't fill all the blocks,
464 : * shorten the quantity that will be deleted in this step.
465 : */
466 1724 : error = xrep_cow_alloc(sc, nextoff - startoff, &repl);
467 1724 : if (error)
468 : return error;
469 :
470 : /*
471 : * Replace the old mapping with the new one, and commit the metadata
472 : * changes made so far.
473 : */
474 1724 : xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl);
475 :
476 1724 : xfs_inode_set_cowblocks_tag(sc->ip);
477 1724 : error = xfs_defer_finish(&sc->tp);
478 1724 : if (error)
479 : return error;
480 :
481 : /* Note the old CoW staging extents; we'll reap them all later. */
482 1724 : error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, got.br_startblock,
483 1724 : repl.len);
484 1724 : if (error)
485 : return error;
486 :
487 1724 : *blockcount = repl.len;
488 1724 : return 0;
489 : }
490 :
491 : /*
492 : * Replace a bad part of an unwritten CoW staging extent with a fresh delalloc
493 : * reservation.
494 : */
495 : STATIC int
496 341 : xrep_cow_replace(
497 : uint64_t startoff,
498 : uint64_t blockcount,
499 : void *priv)
500 : {
501 341 : struct xrep_cow *xc = priv;
502 341 : int error = 0;
503 :
504 2065 : while (blockcount > 0) {
505 1724 : xfs_extlen_t len = min_t(xfs_filblks_t, blockcount,
506 : XFS_MAX_BMBT_EXTLEN);
507 :
508 1724 : error = xrep_cow_replace_range(xc, startoff, &len);
509 1724 : if (error)
510 : break;
511 :
512 1724 : blockcount -= len;
513 1724 : startoff += len;
514 : }
515 :
516 341 : return error;
517 : }
518 :
519 : /*
520 : * Repair an inode's CoW fork. The CoW fork is an in-core structure, so
521 : * there's no btree to rebuid. Instead, we replace any mappings that are
522 : * cross-linked or lack ondisk CoW fork records in the refcount btree.
523 : */
524 : int
525 30821 : xrep_bmap_cow(
526 : struct xfs_scrub *sc)
527 : {
528 30821 : struct xrep_cow *xc;
529 30821 : struct xfs_iext_cursor icur;
530 30821 : struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, XFS_COW_FORK);
531 30821 : int error;
532 :
533 30821 : if (!xfs_has_rmapbt(sc->mp) || !xfs_has_reflink(sc->mp))
534 : return -EOPNOTSUPP;
535 :
536 30821 : if (!ifp)
537 : return 0;
538 :
539 : /* realtime files aren't supported yet */
540 30821 : if (XFS_IS_REALTIME_INODE(sc->ip))
541 : return -EOPNOTSUPP;
542 :
543 : /*
544 : * If we're somehow not in extents format, then reinitialize it to
545 : * an empty extent mapping fork and exit.
546 : */
547 30821 : if (ifp->if_format != XFS_DINODE_FMT_EXTENTS) {
548 0 : ifp->if_format = XFS_DINODE_FMT_EXTENTS;
549 0 : ifp->if_nextents = 0;
550 0 : return 0;
551 : }
552 :
553 30821 : xc = kzalloc(sizeof(struct xrep_cow), XCHK_GFP_FLAGS);
554 30821 : if (!xc)
555 : return -ENOMEM;
556 :
557 30821 : xfs_trans_ijoin(sc->tp, sc->ip, 0);
558 :
559 30821 : xc->sc = sc;
560 30821 : xbitmap_init(&xc->bad_fileoffs);
561 30821 : xfsb_bitmap_init(&xc->old_cowfork_fsblocks);
562 :
563 32231 : for_each_xfs_iext(ifp, &icur, &xc->irec) {
564 1410 : if (xchk_should_terminate(sc, &error))
565 0 : goto out_bitmap;
566 :
567 : /*
568 : * delalloc reservations only exist incore, so there is no
569 : * ondisk metadata that we can examine. Hence we leave them
570 : * alone.
571 : */
572 1410 : if (isnullstartblock(xc->irec.br_startblock))
573 0 : continue;
574 :
575 : /*
576 : * COW fork extents are only in the written state if writeback
577 : * is actively writing to disk. We cannot restart the write
578 : * at a different disk address since we've already issued the
579 : * IO, so we leave these alone and hope for the best.
580 : */
581 1410 : if (xfs_bmap_is_written_extent(&xc->irec))
582 0 : continue;
583 :
584 1410 : error = xrep_cow_find_bad(xc);
585 1410 : if (error)
586 0 : goto out_bitmap;
587 : }
588 :
589 : /* Replace any bad unwritten mappings with fresh reservations. */
590 30821 : error = xbitmap_walk(&xc->bad_fileoffs, xrep_cow_replace, xc);
591 30821 : if (error)
592 0 : goto out_bitmap;
593 :
594 : /*
595 : * Reap as many of the old CoW blocks as we can. They are owned ondisk
596 : * by the refcount btree, not the inode, so it is correct to treat them
597 : * like inode metadata.
598 : */
599 30821 : error = xrep_reap_fsblocks(sc, &xc->old_cowfork_fsblocks,
600 : &XFS_RMAP_OINFO_COW);
601 30821 : if (error)
602 : goto out_bitmap;
603 :
604 30821 : out_bitmap:
605 30821 : xfsb_bitmap_destroy(&xc->old_cowfork_fsblocks);
606 30821 : xbitmap_destroy(&xc->bad_fileoffs);
607 30821 : kmem_free(xc);
608 30821 : return error;
609 : }
|