xref: /linux/fs/xfs/libxfs/xfs_attr_remote.c (revision 78c3925c048c752334873f56c3a3d1c9d53e0416)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
4  * Copyright (c) 2013 Red Hat, Inc.
5  * All Rights Reserved.
6  */
7 #include "xfs.h"
8 #include "xfs_fs.h"
9 #include "xfs_shared.h"
10 #include "xfs_format.h"
11 #include "xfs_log_format.h"
12 #include "xfs_trans_resv.h"
13 #include "xfs_bit.h"
14 #include "xfs_mount.h"
15 #include "xfs_defer.h"
16 #include "xfs_da_format.h"
17 #include "xfs_da_btree.h"
18 #include "xfs_inode.h"
19 #include "xfs_trans.h"
20 #include "xfs_bmap.h"
21 #include "xfs_attr.h"
22 #include "xfs_attr_remote.h"
23 #include "xfs_trace.h"
24 #include "xfs_error.h"
25 #include "xfs_health.h"
26 
27 #define ATTR_RMTVALUE_MAPSIZE	1	/* # of map entries at once */
28 
29 /*
30  * Remote Attribute Values
31  * =======================
32  *
33  * Remote extended attribute values are conceptually simple -- they're written
34  * to data blocks mapped by an inode's attribute fork, and they have an upper
35  * size limit of 64k.  Setting a value does not involve the XFS log.
36  *
37  * However, on a v5 filesystem, maximally sized remote attr values require one
38  * block more than 64k worth of space to hold both the remote attribute value
39  * header (64 bytes).  On a 4k block filesystem this results in a 68k buffer;
40  * on a 64k block filesystem, this would be a 128k buffer.  Note that the log
41  * format can only handle a dirty buffer of XFS_MAX_BLOCKSIZE length (64k).
42  * Therefore, we /must/ ensure that remote attribute value buffers never touch
43  * the logging system and therefore never have a log item.
44  */
45 
46 /*
47  * Each contiguous block has a header, so it is not just a simple attribute
48  * length to FSB conversion.
49  */
50 int
51 xfs_attr3_rmt_blocks(
52 	struct xfs_mount *mp,
53 	int		attrlen)
54 {
55 	if (xfs_has_crc(mp)) {
56 		int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
57 		return (attrlen + buflen - 1) / buflen;
58 	}
59 	return XFS_B_TO_FSB(mp, attrlen);
60 }
61 
62 /*
63  * Checking of the remote attribute header is split into two parts. The verifier
64  * does CRC, location and bounds checking, the unpacking function checks the
65  * attribute parameters and owner.
66  */
67 static xfs_failaddr_t
68 xfs_attr3_rmt_hdr_ok(
69 	void			*ptr,
70 	xfs_ino_t		ino,
71 	uint32_t		offset,
72 	uint32_t		size,
73 	xfs_daddr_t		bno)
74 {
75 	struct xfs_attr3_rmt_hdr *rmt = ptr;
76 
77 	if (bno != be64_to_cpu(rmt->rm_blkno))
78 		return __this_address;
79 	if (offset != be32_to_cpu(rmt->rm_offset))
80 		return __this_address;
81 	if (size != be32_to_cpu(rmt->rm_bytes))
82 		return __this_address;
83 	if (ino != be64_to_cpu(rmt->rm_owner))
84 		return __this_address;
85 
86 	/* ok */
87 	return NULL;
88 }
89 
90 static xfs_failaddr_t
91 xfs_attr3_rmt_verify(
92 	struct xfs_mount	*mp,
93 	struct xfs_buf		*bp,
94 	void			*ptr,
95 	int			fsbsize,
96 	xfs_daddr_t		bno)
97 {
98 	struct xfs_attr3_rmt_hdr *rmt = ptr;
99 
100 	if (!xfs_verify_magic(bp, rmt->rm_magic))
101 		return __this_address;
102 	if (!uuid_equal(&rmt->rm_uuid, &mp->m_sb.sb_meta_uuid))
103 		return __this_address;
104 	if (be64_to_cpu(rmt->rm_blkno) != bno)
105 		return __this_address;
106 	if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt))
107 		return __this_address;
108 	if (be32_to_cpu(rmt->rm_offset) +
109 				be32_to_cpu(rmt->rm_bytes) > XFS_XATTR_SIZE_MAX)
110 		return __this_address;
111 	if (rmt->rm_owner == 0)
112 		return __this_address;
113 
114 	return NULL;
115 }
116 
117 static int
118 __xfs_attr3_rmt_read_verify(
119 	struct xfs_buf	*bp,
120 	bool		check_crc,
121 	xfs_failaddr_t	*failaddr)
122 {
123 	struct xfs_mount *mp = bp->b_mount;
124 	char		*ptr;
125 	int		len;
126 	xfs_daddr_t	bno;
127 	int		blksize = mp->m_attr_geo->blksize;
128 
129 	/* no verification of non-crc buffers */
130 	if (!xfs_has_crc(mp))
131 		return 0;
132 
133 	ptr = bp->b_addr;
134 	bno = xfs_buf_daddr(bp);
135 	len = BBTOB(bp->b_length);
136 	ASSERT(len >= blksize);
137 
138 	while (len > 0) {
139 		if (check_crc &&
140 		    !xfs_verify_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF)) {
141 			*failaddr = __this_address;
142 			return -EFSBADCRC;
143 		}
144 		*failaddr = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno);
145 		if (*failaddr)
146 			return -EFSCORRUPTED;
147 		len -= blksize;
148 		ptr += blksize;
149 		bno += BTOBB(blksize);
150 	}
151 
152 	if (len != 0) {
153 		*failaddr = __this_address;
154 		return -EFSCORRUPTED;
155 	}
156 
157 	return 0;
158 }
159 
160 static void
161 xfs_attr3_rmt_read_verify(
162 	struct xfs_buf	*bp)
163 {
164 	xfs_failaddr_t	fa;
165 	int		error;
166 
167 	error = __xfs_attr3_rmt_read_verify(bp, true, &fa);
168 	if (error)
169 		xfs_verifier_error(bp, error, fa);
170 }
171 
172 static xfs_failaddr_t
173 xfs_attr3_rmt_verify_struct(
174 	struct xfs_buf	*bp)
175 {
176 	xfs_failaddr_t	fa;
177 	int		error;
178 
179 	error = __xfs_attr3_rmt_read_verify(bp, false, &fa);
180 	return error ? fa : NULL;
181 }
182 
183 static void
184 xfs_attr3_rmt_write_verify(
185 	struct xfs_buf	*bp)
186 {
187 	struct xfs_mount *mp = bp->b_mount;
188 	xfs_failaddr_t	fa;
189 	int		blksize = mp->m_attr_geo->blksize;
190 	char		*ptr;
191 	int		len;
192 	xfs_daddr_t	bno;
193 
194 	/* no verification of non-crc buffers */
195 	if (!xfs_has_crc(mp))
196 		return;
197 
198 	ptr = bp->b_addr;
199 	bno = xfs_buf_daddr(bp);
200 	len = BBTOB(bp->b_length);
201 	ASSERT(len >= blksize);
202 
203 	while (len > 0) {
204 		struct xfs_attr3_rmt_hdr *rmt = (struct xfs_attr3_rmt_hdr *)ptr;
205 
206 		fa = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno);
207 		if (fa) {
208 			xfs_verifier_error(bp, -EFSCORRUPTED, fa);
209 			return;
210 		}
211 
212 		/*
213 		 * Ensure we aren't writing bogus LSNs to disk. See
214 		 * xfs_attr3_rmt_hdr_set() for the explanation.
215 		 */
216 		if (rmt->rm_lsn != cpu_to_be64(NULLCOMMITLSN)) {
217 			xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
218 			return;
219 		}
220 		xfs_update_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF);
221 
222 		len -= blksize;
223 		ptr += blksize;
224 		bno += BTOBB(blksize);
225 	}
226 
227 	if (len != 0)
228 		xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
229 }
230 
231 const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
232 	.name = "xfs_attr3_rmt",
233 	.magic = { 0, cpu_to_be32(XFS_ATTR3_RMT_MAGIC) },
234 	.verify_read = xfs_attr3_rmt_read_verify,
235 	.verify_write = xfs_attr3_rmt_write_verify,
236 	.verify_struct = xfs_attr3_rmt_verify_struct,
237 };
238 
239 STATIC int
240 xfs_attr3_rmt_hdr_set(
241 	struct xfs_mount	*mp,
242 	void			*ptr,
243 	xfs_ino_t		ino,
244 	uint32_t		offset,
245 	uint32_t		size,
246 	xfs_daddr_t		bno)
247 {
248 	struct xfs_attr3_rmt_hdr *rmt = ptr;
249 
250 	if (!xfs_has_crc(mp))
251 		return 0;
252 
253 	rmt->rm_magic = cpu_to_be32(XFS_ATTR3_RMT_MAGIC);
254 	rmt->rm_offset = cpu_to_be32(offset);
255 	rmt->rm_bytes = cpu_to_be32(size);
256 	uuid_copy(&rmt->rm_uuid, &mp->m_sb.sb_meta_uuid);
257 	rmt->rm_owner = cpu_to_be64(ino);
258 	rmt->rm_blkno = cpu_to_be64(bno);
259 
260 	/*
261 	 * Remote attribute blocks are written synchronously, so we don't
262 	 * have an LSN that we can stamp in them that makes any sense to log
263 	 * recovery. To ensure that log recovery handles overwrites of these
264 	 * blocks sanely (i.e. once they've been freed and reallocated as some
265 	 * other type of metadata) we need to ensure that the LSN has a value
266 	 * that tells log recovery to ignore the LSN and overwrite the buffer
267 	 * with whatever is in it's log. To do this, we use the magic
268 	 * NULLCOMMITLSN to indicate that the LSN is invalid.
269 	 */
270 	rmt->rm_lsn = cpu_to_be64(NULLCOMMITLSN);
271 
272 	return sizeof(struct xfs_attr3_rmt_hdr);
273 }
274 
275 /*
276  * Helper functions to copy attribute data in and out of the one disk extents
277  */
278 STATIC int
279 xfs_attr_rmtval_copyout(
280 	struct xfs_mount	*mp,
281 	struct xfs_buf		*bp,
282 	struct xfs_inode	*dp,
283 	int			*offset,
284 	int			*valuelen,
285 	uint8_t			**dst)
286 {
287 	char			*src = bp->b_addr;
288 	xfs_ino_t		ino = dp->i_ino;
289 	xfs_daddr_t		bno = xfs_buf_daddr(bp);
290 	int			len = BBTOB(bp->b_length);
291 	int			blksize = mp->m_attr_geo->blksize;
292 
293 	ASSERT(len >= blksize);
294 
295 	while (len > 0 && *valuelen > 0) {
296 		int hdr_size = 0;
297 		int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
298 
299 		byte_cnt = min(*valuelen, byte_cnt);
300 
301 		if (xfs_has_crc(mp)) {
302 			if (xfs_attr3_rmt_hdr_ok(src, ino, *offset,
303 						  byte_cnt, bno)) {
304 				xfs_alert(mp,
305 "remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)",
306 					bno, *offset, byte_cnt, ino);
307 				xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
308 				return -EFSCORRUPTED;
309 			}
310 			hdr_size = sizeof(struct xfs_attr3_rmt_hdr);
311 		}
312 
313 		memcpy(*dst, src + hdr_size, byte_cnt);
314 
315 		/* roll buffer forwards */
316 		len -= blksize;
317 		src += blksize;
318 		bno += BTOBB(blksize);
319 
320 		/* roll attribute data forwards */
321 		*valuelen -= byte_cnt;
322 		*dst += byte_cnt;
323 		*offset += byte_cnt;
324 	}
325 	return 0;
326 }
327 
328 STATIC void
329 xfs_attr_rmtval_copyin(
330 	struct xfs_mount *mp,
331 	struct xfs_buf	*bp,
332 	xfs_ino_t	ino,
333 	int		*offset,
334 	int		*valuelen,
335 	uint8_t		**src)
336 {
337 	char		*dst = bp->b_addr;
338 	xfs_daddr_t	bno = xfs_buf_daddr(bp);
339 	int		len = BBTOB(bp->b_length);
340 	int		blksize = mp->m_attr_geo->blksize;
341 
342 	ASSERT(len >= blksize);
343 
344 	while (len > 0 && *valuelen > 0) {
345 		int hdr_size;
346 		int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
347 
348 		byte_cnt = min(*valuelen, byte_cnt);
349 		hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
350 						 byte_cnt, bno);
351 
352 		memcpy(dst + hdr_size, *src, byte_cnt);
353 
354 		/*
355 		 * If this is the last block, zero the remainder of it.
356 		 * Check that we are actually the last block, too.
357 		 */
358 		if (byte_cnt + hdr_size < blksize) {
359 			ASSERT(*valuelen - byte_cnt == 0);
360 			ASSERT(len == blksize);
361 			memset(dst + hdr_size + byte_cnt, 0,
362 					blksize - hdr_size - byte_cnt);
363 		}
364 
365 		/* roll buffer forwards */
366 		len -= blksize;
367 		dst += blksize;
368 		bno += BTOBB(blksize);
369 
370 		/* roll attribute data forwards */
371 		*valuelen -= byte_cnt;
372 		*src += byte_cnt;
373 		*offset += byte_cnt;
374 	}
375 }
376 
377 /*
378  * Read the value associated with an attribute from the out-of-line buffer
379  * that we stored it in.
380  *
381  * Returns 0 on successful retrieval, otherwise an error.
382  */
383 int
384 xfs_attr_rmtval_get(
385 	struct xfs_da_args	*args)
386 {
387 	struct xfs_bmbt_irec	map[ATTR_RMTVALUE_MAPSIZE];
388 	struct xfs_mount	*mp = args->dp->i_mount;
389 	struct xfs_buf		*bp;
390 	xfs_dablk_t		lblkno = args->rmtblkno;
391 	uint8_t			*dst = args->value;
392 	int			valuelen;
393 	int			nmap;
394 	int			error;
395 	int			blkcnt = args->rmtblkcnt;
396 	int			i;
397 	int			offset = 0;
398 
399 	trace_xfs_attr_rmtval_get(args);
400 
401 	ASSERT(args->valuelen != 0);
402 	ASSERT(args->rmtvaluelen == args->valuelen);
403 
404 	valuelen = args->rmtvaluelen;
405 	while (valuelen > 0) {
406 		nmap = ATTR_RMTVALUE_MAPSIZE;
407 		error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
408 				       blkcnt, map, &nmap,
409 				       XFS_BMAPI_ATTRFORK);
410 		if (error)
411 			return error;
412 		ASSERT(nmap >= 1);
413 
414 		for (i = 0; (i < nmap) && (valuelen > 0); i++) {
415 			xfs_daddr_t	dblkno;
416 			int		dblkcnt;
417 
418 			ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) &&
419 			       (map[i].br_startblock != HOLESTARTBLOCK));
420 			dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
421 			dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
422 			error = xfs_buf_read(mp->m_ddev_targp, dblkno, dblkcnt,
423 					0, &bp, &xfs_attr3_rmt_buf_ops);
424 			if (xfs_metadata_is_sick(error))
425 				xfs_dirattr_mark_sick(args->dp, XFS_ATTR_FORK);
426 			if (error)
427 				return error;
428 
429 			error = xfs_attr_rmtval_copyout(mp, bp, args->dp,
430 							&offset, &valuelen,
431 							&dst);
432 			xfs_buf_relse(bp);
433 			if (error)
434 				return error;
435 
436 			/* roll attribute extent map forwards */
437 			lblkno += map[i].br_blockcount;
438 			blkcnt -= map[i].br_blockcount;
439 		}
440 	}
441 	ASSERT(valuelen == 0);
442 	return 0;
443 }
444 
445 /*
446  * Find a "hole" in the attribute address space large enough for us to drop the
447  * new attributes value into
448  */
449 int
450 xfs_attr_rmt_find_hole(
451 	struct xfs_da_args	*args)
452 {
453 	struct xfs_inode	*dp = args->dp;
454 	struct xfs_mount	*mp = dp->i_mount;
455 	int			error;
456 	int			blkcnt;
457 	xfs_fileoff_t		lfileoff = 0;
458 
459 	/*
460 	 * Because CRC enable attributes have headers, we can't just do a
461 	 * straight byte to FSB conversion and have to take the header space
462 	 * into account.
463 	 */
464 	blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen);
465 	error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
466 						   XFS_ATTR_FORK);
467 	if (error)
468 		return error;
469 
470 	args->rmtblkno = (xfs_dablk_t)lfileoff;
471 	args->rmtblkcnt = blkcnt;
472 
473 	return 0;
474 }
475 
476 int
477 xfs_attr_rmtval_set_value(
478 	struct xfs_da_args	*args)
479 {
480 	struct xfs_inode	*dp = args->dp;
481 	struct xfs_mount	*mp = dp->i_mount;
482 	struct xfs_bmbt_irec	map;
483 	xfs_dablk_t		lblkno;
484 	uint8_t			*src = args->value;
485 	int			blkcnt;
486 	int			valuelen;
487 	int			nmap;
488 	int			error;
489 	int			offset = 0;
490 
491 	/*
492 	 * Roll through the "value", copying the attribute value to the
493 	 * already-allocated blocks.  Blocks are written synchronously
494 	 * so that we can know they are all on disk before we turn off
495 	 * the INCOMPLETE flag.
496 	 */
497 	lblkno = args->rmtblkno;
498 	blkcnt = args->rmtblkcnt;
499 	valuelen = args->rmtvaluelen;
500 	while (valuelen > 0) {
501 		struct xfs_buf	*bp;
502 		xfs_daddr_t	dblkno;
503 		int		dblkcnt;
504 
505 		ASSERT(blkcnt > 0);
506 
507 		nmap = 1;
508 		error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno,
509 				       blkcnt, &map, &nmap,
510 				       XFS_BMAPI_ATTRFORK);
511 		if (error)
512 			return error;
513 		ASSERT(nmap == 1);
514 		ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
515 		       (map.br_startblock != HOLESTARTBLOCK));
516 
517 		dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
518 		dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
519 
520 		error = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, &bp);
521 		if (error)
522 			return error;
523 		bp->b_ops = &xfs_attr3_rmt_buf_ops;
524 
525 		xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset,
526 				       &valuelen, &src);
527 
528 		error = xfs_bwrite(bp);	/* GROT: NOTE: synchronous write */
529 		xfs_buf_relse(bp);
530 		if (error)
531 			return error;
532 
533 
534 		/* roll attribute extent map forwards */
535 		lblkno += map.br_blockcount;
536 		blkcnt -= map.br_blockcount;
537 	}
538 	ASSERT(valuelen == 0);
539 	return 0;
540 }
541 
542 /* Mark stale any incore buffers for the remote value. */
543 int
544 xfs_attr_rmtval_stale(
545 	struct xfs_inode	*ip,
546 	struct xfs_bmbt_irec	*map,
547 	xfs_buf_flags_t		incore_flags)
548 {
549 	struct xfs_mount	*mp = ip->i_mount;
550 	struct xfs_buf		*bp;
551 	int			error;
552 
553 	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
554 
555 	if (XFS_IS_CORRUPT(mp, map->br_startblock == DELAYSTARTBLOCK) ||
556 	    XFS_IS_CORRUPT(mp, map->br_startblock == HOLESTARTBLOCK)) {
557 		xfs_bmap_mark_sick(ip, XFS_ATTR_FORK);
558 		return -EFSCORRUPTED;
559 	}
560 
561 	error = xfs_buf_incore(mp->m_ddev_targp,
562 			XFS_FSB_TO_DADDR(mp, map->br_startblock),
563 			XFS_FSB_TO_BB(mp, map->br_blockcount),
564 			incore_flags, &bp);
565 	if (error) {
566 		if (error == -ENOENT)
567 			return 0;
568 		return error;
569 	}
570 
571 	xfs_buf_stale(bp);
572 	xfs_buf_relse(bp);
573 	return 0;
574 }
575 
576 /*
577  * Find a hole for the attr and store it in the delayed attr context.  This
578  * initializes the context to roll through allocating an attr extent for a
579  * delayed attr operation
580  */
581 int
582 xfs_attr_rmtval_find_space(
583 	struct xfs_attr_intent		*attr)
584 {
585 	struct xfs_da_args		*args = attr->xattri_da_args;
586 	struct xfs_bmbt_irec		*map = &attr->xattri_map;
587 	int				error;
588 
589 	attr->xattri_lblkno = 0;
590 	attr->xattri_blkcnt = 0;
591 	args->rmtblkcnt = 0;
592 	args->rmtblkno = 0;
593 	memset(map, 0, sizeof(struct xfs_bmbt_irec));
594 
595 	error = xfs_attr_rmt_find_hole(args);
596 	if (error)
597 		return error;
598 
599 	attr->xattri_blkcnt = args->rmtblkcnt;
600 	attr->xattri_lblkno = args->rmtblkno;
601 
602 	return 0;
603 }
604 
605 /*
606  * Write one block of the value associated with an attribute into the
607  * out-of-line buffer that we have defined for it. This is similar to a subset
608  * of xfs_attr_rmtval_set, but records the current block to the delayed attr
609  * context, and leaves transaction handling to the caller.
610  */
611 int
612 xfs_attr_rmtval_set_blk(
613 	struct xfs_attr_intent		*attr)
614 {
615 	struct xfs_da_args		*args = attr->xattri_da_args;
616 	struct xfs_inode		*dp = args->dp;
617 	struct xfs_bmbt_irec		*map = &attr->xattri_map;
618 	int nmap;
619 	int error;
620 
621 	nmap = 1;
622 	error = xfs_bmapi_write(args->trans, dp,
623 			(xfs_fileoff_t)attr->xattri_lblkno,
624 			attr->xattri_blkcnt, XFS_BMAPI_ATTRFORK, args->total,
625 			map, &nmap);
626 	if (error)
627 		return error;
628 
629 	ASSERT(nmap == 1);
630 	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
631 	       (map->br_startblock != HOLESTARTBLOCK));
632 
633 	/* roll attribute extent map forwards */
634 	attr->xattri_lblkno += map->br_blockcount;
635 	attr->xattri_blkcnt -= map->br_blockcount;
636 
637 	return 0;
638 }
639 
640 /*
641  * Remove the value associated with an attribute by deleting the
642  * out-of-line buffer that it is stored on.
643  */
644 int
645 xfs_attr_rmtval_invalidate(
646 	struct xfs_da_args	*args)
647 {
648 	xfs_dablk_t		lblkno;
649 	int			blkcnt;
650 	int			error;
651 
652 	/*
653 	 * Roll through the "value", invalidating the attribute value's blocks.
654 	 */
655 	lblkno = args->rmtblkno;
656 	blkcnt = args->rmtblkcnt;
657 	while (blkcnt > 0) {
658 		struct xfs_bmbt_irec	map;
659 		int			nmap;
660 
661 		/*
662 		 * Try to remember where we decided to put the value.
663 		 */
664 		nmap = 1;
665 		error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
666 				       blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK);
667 		if (error)
668 			return error;
669 		if (XFS_IS_CORRUPT(args->dp->i_mount, nmap != 1)) {
670 			xfs_bmap_mark_sick(args->dp, XFS_ATTR_FORK);
671 			return -EFSCORRUPTED;
672 		}
673 		error = xfs_attr_rmtval_stale(args->dp, &map, XBF_TRYLOCK);
674 		if (error)
675 			return error;
676 
677 		lblkno += map.br_blockcount;
678 		blkcnt -= map.br_blockcount;
679 	}
680 	return 0;
681 }
682 
683 /*
684  * Remove the value associated with an attribute by deleting the out-of-line
685  * buffer that it is stored on. Returns -EAGAIN for the caller to refresh the
686  * transaction and re-call the function.  Callers should keep calling this
687  * routine until it returns something other than -EAGAIN.
688  */
689 int
690 xfs_attr_rmtval_remove(
691 	struct xfs_attr_intent		*attr)
692 {
693 	struct xfs_da_args		*args = attr->xattri_da_args;
694 	int				error, done;
695 
696 	/*
697 	 * Unmap value blocks for this attr.
698 	 */
699 	error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno,
700 			    args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done);
701 	if (error)
702 		return error;
703 
704 	/*
705 	 * We don't need an explicit state here to pick up where we left off. We
706 	 * can figure it out using the !done return code. The actual value of
707 	 * attr->xattri_dela_state may be some value reminiscent of the calling
708 	 * function, but it's value is irrelevant with in the context of this
709 	 * function. Once we are done here, the next state is set as needed by
710 	 * the parent
711 	 */
712 	if (!done) {
713 		trace_xfs_attr_rmtval_remove_return(attr->xattri_dela_state,
714 						    args->dp);
715 		return -EAGAIN;
716 	}
717 
718 	args->rmtblkno = 0;
719 	args->rmtblkcnt = 0;
720 	return 0;
721 }
722