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