162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2000-2005 Silicon Graphics, Inc.
462306a36Sopenharmony_ci * Copyright (c) 2013 Red Hat, Inc.
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include "xfs.h"
862306a36Sopenharmony_ci#include "xfs_fs.h"
962306a36Sopenharmony_ci#include "xfs_shared.h"
1062306a36Sopenharmony_ci#include "xfs_format.h"
1162306a36Sopenharmony_ci#include "xfs_log_format.h"
1262306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1362306a36Sopenharmony_ci#include "xfs_bit.h"
1462306a36Sopenharmony_ci#include "xfs_mount.h"
1562306a36Sopenharmony_ci#include "xfs_defer.h"
1662306a36Sopenharmony_ci#include "xfs_da_format.h"
1762306a36Sopenharmony_ci#include "xfs_da_btree.h"
1862306a36Sopenharmony_ci#include "xfs_inode.h"
1962306a36Sopenharmony_ci#include "xfs_trans.h"
2062306a36Sopenharmony_ci#include "xfs_bmap.h"
2162306a36Sopenharmony_ci#include "xfs_attr.h"
2262306a36Sopenharmony_ci#include "xfs_attr_remote.h"
2362306a36Sopenharmony_ci#include "xfs_trace.h"
2462306a36Sopenharmony_ci#include "xfs_error.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define ATTR_RMTVALUE_MAPSIZE	1	/* # of map entries at once */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * Remote Attribute Values
3062306a36Sopenharmony_ci * =======================
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Remote extended attribute values are conceptually simple -- they're written
3362306a36Sopenharmony_ci * to data blocks mapped by an inode's attribute fork, and they have an upper
3462306a36Sopenharmony_ci * size limit of 64k.  Setting a value does not involve the XFS log.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * However, on a v5 filesystem, maximally sized remote attr values require one
3762306a36Sopenharmony_ci * block more than 64k worth of space to hold both the remote attribute value
3862306a36Sopenharmony_ci * header (64 bytes).  On a 4k block filesystem this results in a 68k buffer;
3962306a36Sopenharmony_ci * on a 64k block filesystem, this would be a 128k buffer.  Note that the log
4062306a36Sopenharmony_ci * format can only handle a dirty buffer of XFS_MAX_BLOCKSIZE length (64k).
4162306a36Sopenharmony_ci * Therefore, we /must/ ensure that remote attribute value buffers never touch
4262306a36Sopenharmony_ci * the logging system and therefore never have a log item.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * Each contiguous block has a header, so it is not just a simple attribute
4762306a36Sopenharmony_ci * length to FSB conversion.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ciint
5062306a36Sopenharmony_cixfs_attr3_rmt_blocks(
5162306a36Sopenharmony_ci	struct xfs_mount *mp,
5262306a36Sopenharmony_ci	int		attrlen)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	if (xfs_has_crc(mp)) {
5562306a36Sopenharmony_ci		int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
5662306a36Sopenharmony_ci		return (attrlen + buflen - 1) / buflen;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	return XFS_B_TO_FSB(mp, attrlen);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * Checking of the remote attribute header is split into two parts. The verifier
6362306a36Sopenharmony_ci * does CRC, location and bounds checking, the unpacking function checks the
6462306a36Sopenharmony_ci * attribute parameters and owner.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic xfs_failaddr_t
6762306a36Sopenharmony_cixfs_attr3_rmt_hdr_ok(
6862306a36Sopenharmony_ci	void			*ptr,
6962306a36Sopenharmony_ci	xfs_ino_t		ino,
7062306a36Sopenharmony_ci	uint32_t		offset,
7162306a36Sopenharmony_ci	uint32_t		size,
7262306a36Sopenharmony_ci	xfs_daddr_t		bno)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct xfs_attr3_rmt_hdr *rmt = ptr;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (bno != be64_to_cpu(rmt->rm_blkno))
7762306a36Sopenharmony_ci		return __this_address;
7862306a36Sopenharmony_ci	if (offset != be32_to_cpu(rmt->rm_offset))
7962306a36Sopenharmony_ci		return __this_address;
8062306a36Sopenharmony_ci	if (size != be32_to_cpu(rmt->rm_bytes))
8162306a36Sopenharmony_ci		return __this_address;
8262306a36Sopenharmony_ci	if (ino != be64_to_cpu(rmt->rm_owner))
8362306a36Sopenharmony_ci		return __this_address;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* ok */
8662306a36Sopenharmony_ci	return NULL;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic xfs_failaddr_t
9062306a36Sopenharmony_cixfs_attr3_rmt_verify(
9162306a36Sopenharmony_ci	struct xfs_mount	*mp,
9262306a36Sopenharmony_ci	struct xfs_buf		*bp,
9362306a36Sopenharmony_ci	void			*ptr,
9462306a36Sopenharmony_ci	int			fsbsize,
9562306a36Sopenharmony_ci	xfs_daddr_t		bno)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct xfs_attr3_rmt_hdr *rmt = ptr;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!xfs_verify_magic(bp, rmt->rm_magic))
10062306a36Sopenharmony_ci		return __this_address;
10162306a36Sopenharmony_ci	if (!uuid_equal(&rmt->rm_uuid, &mp->m_sb.sb_meta_uuid))
10262306a36Sopenharmony_ci		return __this_address;
10362306a36Sopenharmony_ci	if (be64_to_cpu(rmt->rm_blkno) != bno)
10462306a36Sopenharmony_ci		return __this_address;
10562306a36Sopenharmony_ci	if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt))
10662306a36Sopenharmony_ci		return __this_address;
10762306a36Sopenharmony_ci	if (be32_to_cpu(rmt->rm_offset) +
10862306a36Sopenharmony_ci				be32_to_cpu(rmt->rm_bytes) > XFS_XATTR_SIZE_MAX)
10962306a36Sopenharmony_ci		return __this_address;
11062306a36Sopenharmony_ci	if (rmt->rm_owner == 0)
11162306a36Sopenharmony_ci		return __this_address;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return NULL;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int
11762306a36Sopenharmony_ci__xfs_attr3_rmt_read_verify(
11862306a36Sopenharmony_ci	struct xfs_buf	*bp,
11962306a36Sopenharmony_ci	bool		check_crc,
12062306a36Sopenharmony_ci	xfs_failaddr_t	*failaddr)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct xfs_mount *mp = bp->b_mount;
12362306a36Sopenharmony_ci	char		*ptr;
12462306a36Sopenharmony_ci	int		len;
12562306a36Sopenharmony_ci	xfs_daddr_t	bno;
12662306a36Sopenharmony_ci	int		blksize = mp->m_attr_geo->blksize;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* no verification of non-crc buffers */
12962306a36Sopenharmony_ci	if (!xfs_has_crc(mp))
13062306a36Sopenharmony_ci		return 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ptr = bp->b_addr;
13362306a36Sopenharmony_ci	bno = xfs_buf_daddr(bp);
13462306a36Sopenharmony_ci	len = BBTOB(bp->b_length);
13562306a36Sopenharmony_ci	ASSERT(len >= blksize);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	while (len > 0) {
13862306a36Sopenharmony_ci		if (check_crc &&
13962306a36Sopenharmony_ci		    !xfs_verify_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF)) {
14062306a36Sopenharmony_ci			*failaddr = __this_address;
14162306a36Sopenharmony_ci			return -EFSBADCRC;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci		*failaddr = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno);
14462306a36Sopenharmony_ci		if (*failaddr)
14562306a36Sopenharmony_ci			return -EFSCORRUPTED;
14662306a36Sopenharmony_ci		len -= blksize;
14762306a36Sopenharmony_ci		ptr += blksize;
14862306a36Sopenharmony_ci		bno += BTOBB(blksize);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (len != 0) {
15262306a36Sopenharmony_ci		*failaddr = __this_address;
15362306a36Sopenharmony_ci		return -EFSCORRUPTED;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void
16062306a36Sopenharmony_cixfs_attr3_rmt_read_verify(
16162306a36Sopenharmony_ci	struct xfs_buf	*bp)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	xfs_failaddr_t	fa;
16462306a36Sopenharmony_ci	int		error;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	error = __xfs_attr3_rmt_read_verify(bp, true, &fa);
16762306a36Sopenharmony_ci	if (error)
16862306a36Sopenharmony_ci		xfs_verifier_error(bp, error, fa);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic xfs_failaddr_t
17262306a36Sopenharmony_cixfs_attr3_rmt_verify_struct(
17362306a36Sopenharmony_ci	struct xfs_buf	*bp)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	xfs_failaddr_t	fa;
17662306a36Sopenharmony_ci	int		error;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	error = __xfs_attr3_rmt_read_verify(bp, false, &fa);
17962306a36Sopenharmony_ci	return error ? fa : NULL;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void
18362306a36Sopenharmony_cixfs_attr3_rmt_write_verify(
18462306a36Sopenharmony_ci	struct xfs_buf	*bp)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct xfs_mount *mp = bp->b_mount;
18762306a36Sopenharmony_ci	xfs_failaddr_t	fa;
18862306a36Sopenharmony_ci	int		blksize = mp->m_attr_geo->blksize;
18962306a36Sopenharmony_ci	char		*ptr;
19062306a36Sopenharmony_ci	int		len;
19162306a36Sopenharmony_ci	xfs_daddr_t	bno;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* no verification of non-crc buffers */
19462306a36Sopenharmony_ci	if (!xfs_has_crc(mp))
19562306a36Sopenharmony_ci		return;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ptr = bp->b_addr;
19862306a36Sopenharmony_ci	bno = xfs_buf_daddr(bp);
19962306a36Sopenharmony_ci	len = BBTOB(bp->b_length);
20062306a36Sopenharmony_ci	ASSERT(len >= blksize);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	while (len > 0) {
20362306a36Sopenharmony_ci		struct xfs_attr3_rmt_hdr *rmt = (struct xfs_attr3_rmt_hdr *)ptr;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		fa = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno);
20662306a36Sopenharmony_ci		if (fa) {
20762306a36Sopenharmony_ci			xfs_verifier_error(bp, -EFSCORRUPTED, fa);
20862306a36Sopenharmony_ci			return;
20962306a36Sopenharmony_ci		}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		/*
21262306a36Sopenharmony_ci		 * Ensure we aren't writing bogus LSNs to disk. See
21362306a36Sopenharmony_ci		 * xfs_attr3_rmt_hdr_set() for the explanation.
21462306a36Sopenharmony_ci		 */
21562306a36Sopenharmony_ci		if (rmt->rm_lsn != cpu_to_be64(NULLCOMMITLSN)) {
21662306a36Sopenharmony_ci			xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
21762306a36Sopenharmony_ci			return;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci		xfs_update_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		len -= blksize;
22262306a36Sopenharmony_ci		ptr += blksize;
22362306a36Sopenharmony_ci		bno += BTOBB(blksize);
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (len != 0)
22762306a36Sopenharmony_ci		xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ciconst struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
23162306a36Sopenharmony_ci	.name = "xfs_attr3_rmt",
23262306a36Sopenharmony_ci	.magic = { 0, cpu_to_be32(XFS_ATTR3_RMT_MAGIC) },
23362306a36Sopenharmony_ci	.verify_read = xfs_attr3_rmt_read_verify,
23462306a36Sopenharmony_ci	.verify_write = xfs_attr3_rmt_write_verify,
23562306a36Sopenharmony_ci	.verify_struct = xfs_attr3_rmt_verify_struct,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciSTATIC int
23962306a36Sopenharmony_cixfs_attr3_rmt_hdr_set(
24062306a36Sopenharmony_ci	struct xfs_mount	*mp,
24162306a36Sopenharmony_ci	void			*ptr,
24262306a36Sopenharmony_ci	xfs_ino_t		ino,
24362306a36Sopenharmony_ci	uint32_t		offset,
24462306a36Sopenharmony_ci	uint32_t		size,
24562306a36Sopenharmony_ci	xfs_daddr_t		bno)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct xfs_attr3_rmt_hdr *rmt = ptr;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (!xfs_has_crc(mp))
25062306a36Sopenharmony_ci		return 0;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	rmt->rm_magic = cpu_to_be32(XFS_ATTR3_RMT_MAGIC);
25362306a36Sopenharmony_ci	rmt->rm_offset = cpu_to_be32(offset);
25462306a36Sopenharmony_ci	rmt->rm_bytes = cpu_to_be32(size);
25562306a36Sopenharmony_ci	uuid_copy(&rmt->rm_uuid, &mp->m_sb.sb_meta_uuid);
25662306a36Sopenharmony_ci	rmt->rm_owner = cpu_to_be64(ino);
25762306a36Sopenharmony_ci	rmt->rm_blkno = cpu_to_be64(bno);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/*
26062306a36Sopenharmony_ci	 * Remote attribute blocks are written synchronously, so we don't
26162306a36Sopenharmony_ci	 * have an LSN that we can stamp in them that makes any sense to log
26262306a36Sopenharmony_ci	 * recovery. To ensure that log recovery handles overwrites of these
26362306a36Sopenharmony_ci	 * blocks sanely (i.e. once they've been freed and reallocated as some
26462306a36Sopenharmony_ci	 * other type of metadata) we need to ensure that the LSN has a value
26562306a36Sopenharmony_ci	 * that tells log recovery to ignore the LSN and overwrite the buffer
26662306a36Sopenharmony_ci	 * with whatever is in it's log. To do this, we use the magic
26762306a36Sopenharmony_ci	 * NULLCOMMITLSN to indicate that the LSN is invalid.
26862306a36Sopenharmony_ci	 */
26962306a36Sopenharmony_ci	rmt->rm_lsn = cpu_to_be64(NULLCOMMITLSN);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return sizeof(struct xfs_attr3_rmt_hdr);
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/*
27562306a36Sopenharmony_ci * Helper functions to copy attribute data in and out of the one disk extents
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_ciSTATIC int
27862306a36Sopenharmony_cixfs_attr_rmtval_copyout(
27962306a36Sopenharmony_ci	struct xfs_mount *mp,
28062306a36Sopenharmony_ci	struct xfs_buf	*bp,
28162306a36Sopenharmony_ci	xfs_ino_t	ino,
28262306a36Sopenharmony_ci	int		*offset,
28362306a36Sopenharmony_ci	int		*valuelen,
28462306a36Sopenharmony_ci	uint8_t		**dst)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	char		*src = bp->b_addr;
28762306a36Sopenharmony_ci	xfs_daddr_t	bno = xfs_buf_daddr(bp);
28862306a36Sopenharmony_ci	int		len = BBTOB(bp->b_length);
28962306a36Sopenharmony_ci	int		blksize = mp->m_attr_geo->blksize;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	ASSERT(len >= blksize);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	while (len > 0 && *valuelen > 0) {
29462306a36Sopenharmony_ci		int hdr_size = 0;
29562306a36Sopenharmony_ci		int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		byte_cnt = min(*valuelen, byte_cnt);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		if (xfs_has_crc(mp)) {
30062306a36Sopenharmony_ci			if (xfs_attr3_rmt_hdr_ok(src, ino, *offset,
30162306a36Sopenharmony_ci						  byte_cnt, bno)) {
30262306a36Sopenharmony_ci				xfs_alert(mp,
30362306a36Sopenharmony_ci"remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)",
30462306a36Sopenharmony_ci					bno, *offset, byte_cnt, ino);
30562306a36Sopenharmony_ci				return -EFSCORRUPTED;
30662306a36Sopenharmony_ci			}
30762306a36Sopenharmony_ci			hdr_size = sizeof(struct xfs_attr3_rmt_hdr);
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		memcpy(*dst, src + hdr_size, byte_cnt);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		/* roll buffer forwards */
31362306a36Sopenharmony_ci		len -= blksize;
31462306a36Sopenharmony_ci		src += blksize;
31562306a36Sopenharmony_ci		bno += BTOBB(blksize);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		/* roll attribute data forwards */
31862306a36Sopenharmony_ci		*valuelen -= byte_cnt;
31962306a36Sopenharmony_ci		*dst += byte_cnt;
32062306a36Sopenharmony_ci		*offset += byte_cnt;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ciSTATIC void
32662306a36Sopenharmony_cixfs_attr_rmtval_copyin(
32762306a36Sopenharmony_ci	struct xfs_mount *mp,
32862306a36Sopenharmony_ci	struct xfs_buf	*bp,
32962306a36Sopenharmony_ci	xfs_ino_t	ino,
33062306a36Sopenharmony_ci	int		*offset,
33162306a36Sopenharmony_ci	int		*valuelen,
33262306a36Sopenharmony_ci	uint8_t		**src)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	char		*dst = bp->b_addr;
33562306a36Sopenharmony_ci	xfs_daddr_t	bno = xfs_buf_daddr(bp);
33662306a36Sopenharmony_ci	int		len = BBTOB(bp->b_length);
33762306a36Sopenharmony_ci	int		blksize = mp->m_attr_geo->blksize;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ASSERT(len >= blksize);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	while (len > 0 && *valuelen > 0) {
34262306a36Sopenharmony_ci		int hdr_size;
34362306a36Sopenharmony_ci		int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		byte_cnt = min(*valuelen, byte_cnt);
34662306a36Sopenharmony_ci		hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
34762306a36Sopenharmony_ci						 byte_cnt, bno);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		memcpy(dst + hdr_size, *src, byte_cnt);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		/*
35262306a36Sopenharmony_ci		 * If this is the last block, zero the remainder of it.
35362306a36Sopenharmony_ci		 * Check that we are actually the last block, too.
35462306a36Sopenharmony_ci		 */
35562306a36Sopenharmony_ci		if (byte_cnt + hdr_size < blksize) {
35662306a36Sopenharmony_ci			ASSERT(*valuelen - byte_cnt == 0);
35762306a36Sopenharmony_ci			ASSERT(len == blksize);
35862306a36Sopenharmony_ci			memset(dst + hdr_size + byte_cnt, 0,
35962306a36Sopenharmony_ci					blksize - hdr_size - byte_cnt);
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		/* roll buffer forwards */
36362306a36Sopenharmony_ci		len -= blksize;
36462306a36Sopenharmony_ci		dst += blksize;
36562306a36Sopenharmony_ci		bno += BTOBB(blksize);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		/* roll attribute data forwards */
36862306a36Sopenharmony_ci		*valuelen -= byte_cnt;
36962306a36Sopenharmony_ci		*src += byte_cnt;
37062306a36Sopenharmony_ci		*offset += byte_cnt;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/*
37562306a36Sopenharmony_ci * Read the value associated with an attribute from the out-of-line buffer
37662306a36Sopenharmony_ci * that we stored it in.
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * Returns 0 on successful retrieval, otherwise an error.
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_ciint
38162306a36Sopenharmony_cixfs_attr_rmtval_get(
38262306a36Sopenharmony_ci	struct xfs_da_args	*args)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct xfs_bmbt_irec	map[ATTR_RMTVALUE_MAPSIZE];
38562306a36Sopenharmony_ci	struct xfs_mount	*mp = args->dp->i_mount;
38662306a36Sopenharmony_ci	struct xfs_buf		*bp;
38762306a36Sopenharmony_ci	xfs_dablk_t		lblkno = args->rmtblkno;
38862306a36Sopenharmony_ci	uint8_t			*dst = args->value;
38962306a36Sopenharmony_ci	int			valuelen;
39062306a36Sopenharmony_ci	int			nmap;
39162306a36Sopenharmony_ci	int			error;
39262306a36Sopenharmony_ci	int			blkcnt = args->rmtblkcnt;
39362306a36Sopenharmony_ci	int			i;
39462306a36Sopenharmony_ci	int			offset = 0;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	trace_xfs_attr_rmtval_get(args);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ASSERT(args->valuelen != 0);
39962306a36Sopenharmony_ci	ASSERT(args->rmtvaluelen == args->valuelen);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	valuelen = args->rmtvaluelen;
40262306a36Sopenharmony_ci	while (valuelen > 0) {
40362306a36Sopenharmony_ci		nmap = ATTR_RMTVALUE_MAPSIZE;
40462306a36Sopenharmony_ci		error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
40562306a36Sopenharmony_ci				       blkcnt, map, &nmap,
40662306a36Sopenharmony_ci				       XFS_BMAPI_ATTRFORK);
40762306a36Sopenharmony_ci		if (error)
40862306a36Sopenharmony_ci			return error;
40962306a36Sopenharmony_ci		ASSERT(nmap >= 1);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		for (i = 0; (i < nmap) && (valuelen > 0); i++) {
41262306a36Sopenharmony_ci			xfs_daddr_t	dblkno;
41362306a36Sopenharmony_ci			int		dblkcnt;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci			ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) &&
41662306a36Sopenharmony_ci			       (map[i].br_startblock != HOLESTARTBLOCK));
41762306a36Sopenharmony_ci			dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
41862306a36Sopenharmony_ci			dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
41962306a36Sopenharmony_ci			error = xfs_buf_read(mp->m_ddev_targp, dblkno, dblkcnt,
42062306a36Sopenharmony_ci					0, &bp, &xfs_attr3_rmt_buf_ops);
42162306a36Sopenharmony_ci			if (error)
42262306a36Sopenharmony_ci				return error;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci			error = xfs_attr_rmtval_copyout(mp, bp, args->dp->i_ino,
42562306a36Sopenharmony_ci							&offset, &valuelen,
42662306a36Sopenharmony_ci							&dst);
42762306a36Sopenharmony_ci			xfs_buf_relse(bp);
42862306a36Sopenharmony_ci			if (error)
42962306a36Sopenharmony_ci				return error;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci			/* roll attribute extent map forwards */
43262306a36Sopenharmony_ci			lblkno += map[i].br_blockcount;
43362306a36Sopenharmony_ci			blkcnt -= map[i].br_blockcount;
43462306a36Sopenharmony_ci		}
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci	ASSERT(valuelen == 0);
43762306a36Sopenharmony_ci	return 0;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/*
44162306a36Sopenharmony_ci * Find a "hole" in the attribute address space large enough for us to drop the
44262306a36Sopenharmony_ci * new attributes value into
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_ciint
44562306a36Sopenharmony_cixfs_attr_rmt_find_hole(
44662306a36Sopenharmony_ci	struct xfs_da_args	*args)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct xfs_inode	*dp = args->dp;
44962306a36Sopenharmony_ci	struct xfs_mount	*mp = dp->i_mount;
45062306a36Sopenharmony_ci	int			error;
45162306a36Sopenharmony_ci	int			blkcnt;
45262306a36Sopenharmony_ci	xfs_fileoff_t		lfileoff = 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/*
45562306a36Sopenharmony_ci	 * Because CRC enable attributes have headers, we can't just do a
45662306a36Sopenharmony_ci	 * straight byte to FSB conversion and have to take the header space
45762306a36Sopenharmony_ci	 * into account.
45862306a36Sopenharmony_ci	 */
45962306a36Sopenharmony_ci	blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen);
46062306a36Sopenharmony_ci	error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
46162306a36Sopenharmony_ci						   XFS_ATTR_FORK);
46262306a36Sopenharmony_ci	if (error)
46362306a36Sopenharmony_ci		return error;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	args->rmtblkno = (xfs_dablk_t)lfileoff;
46662306a36Sopenharmony_ci	args->rmtblkcnt = blkcnt;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return 0;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ciint
47262306a36Sopenharmony_cixfs_attr_rmtval_set_value(
47362306a36Sopenharmony_ci	struct xfs_da_args	*args)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct xfs_inode	*dp = args->dp;
47662306a36Sopenharmony_ci	struct xfs_mount	*mp = dp->i_mount;
47762306a36Sopenharmony_ci	struct xfs_bmbt_irec	map;
47862306a36Sopenharmony_ci	xfs_dablk_t		lblkno;
47962306a36Sopenharmony_ci	uint8_t			*src = args->value;
48062306a36Sopenharmony_ci	int			blkcnt;
48162306a36Sopenharmony_ci	int			valuelen;
48262306a36Sopenharmony_ci	int			nmap;
48362306a36Sopenharmony_ci	int			error;
48462306a36Sopenharmony_ci	int			offset = 0;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/*
48762306a36Sopenharmony_ci	 * Roll through the "value", copying the attribute value to the
48862306a36Sopenharmony_ci	 * already-allocated blocks.  Blocks are written synchronously
48962306a36Sopenharmony_ci	 * so that we can know they are all on disk before we turn off
49062306a36Sopenharmony_ci	 * the INCOMPLETE flag.
49162306a36Sopenharmony_ci	 */
49262306a36Sopenharmony_ci	lblkno = args->rmtblkno;
49362306a36Sopenharmony_ci	blkcnt = args->rmtblkcnt;
49462306a36Sopenharmony_ci	valuelen = args->rmtvaluelen;
49562306a36Sopenharmony_ci	while (valuelen > 0) {
49662306a36Sopenharmony_ci		struct xfs_buf	*bp;
49762306a36Sopenharmony_ci		xfs_daddr_t	dblkno;
49862306a36Sopenharmony_ci		int		dblkcnt;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		ASSERT(blkcnt > 0);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		nmap = 1;
50362306a36Sopenharmony_ci		error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno,
50462306a36Sopenharmony_ci				       blkcnt, &map, &nmap,
50562306a36Sopenharmony_ci				       XFS_BMAPI_ATTRFORK);
50662306a36Sopenharmony_ci		if (error)
50762306a36Sopenharmony_ci			return error;
50862306a36Sopenharmony_ci		ASSERT(nmap == 1);
50962306a36Sopenharmony_ci		ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
51062306a36Sopenharmony_ci		       (map.br_startblock != HOLESTARTBLOCK));
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
51362306a36Sopenharmony_ci		dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		error = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, &bp);
51662306a36Sopenharmony_ci		if (error)
51762306a36Sopenharmony_ci			return error;
51862306a36Sopenharmony_ci		bp->b_ops = &xfs_attr3_rmt_buf_ops;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset,
52162306a36Sopenharmony_ci				       &valuelen, &src);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		error = xfs_bwrite(bp);	/* GROT: NOTE: synchronous write */
52462306a36Sopenharmony_ci		xfs_buf_relse(bp);
52562306a36Sopenharmony_ci		if (error)
52662306a36Sopenharmony_ci			return error;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		/* roll attribute extent map forwards */
53062306a36Sopenharmony_ci		lblkno += map.br_blockcount;
53162306a36Sopenharmony_ci		blkcnt -= map.br_blockcount;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci	ASSERT(valuelen == 0);
53462306a36Sopenharmony_ci	return 0;
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci/* Mark stale any incore buffers for the remote value. */
53862306a36Sopenharmony_ciint
53962306a36Sopenharmony_cixfs_attr_rmtval_stale(
54062306a36Sopenharmony_ci	struct xfs_inode	*ip,
54162306a36Sopenharmony_ci	struct xfs_bmbt_irec	*map,
54262306a36Sopenharmony_ci	xfs_buf_flags_t		incore_flags)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
54562306a36Sopenharmony_ci	struct xfs_buf		*bp;
54662306a36Sopenharmony_ci	int			error;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (XFS_IS_CORRUPT(mp, map->br_startblock == DELAYSTARTBLOCK) ||
55162306a36Sopenharmony_ci	    XFS_IS_CORRUPT(mp, map->br_startblock == HOLESTARTBLOCK))
55262306a36Sopenharmony_ci		return -EFSCORRUPTED;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	error = xfs_buf_incore(mp->m_ddev_targp,
55562306a36Sopenharmony_ci			XFS_FSB_TO_DADDR(mp, map->br_startblock),
55662306a36Sopenharmony_ci			XFS_FSB_TO_BB(mp, map->br_blockcount),
55762306a36Sopenharmony_ci			incore_flags, &bp);
55862306a36Sopenharmony_ci	if (error) {
55962306a36Sopenharmony_ci		if (error == -ENOENT)
56062306a36Sopenharmony_ci			return 0;
56162306a36Sopenharmony_ci		return error;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	xfs_buf_stale(bp);
56562306a36Sopenharmony_ci	xfs_buf_relse(bp);
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/*
57062306a36Sopenharmony_ci * Find a hole for the attr and store it in the delayed attr context.  This
57162306a36Sopenharmony_ci * initializes the context to roll through allocating an attr extent for a
57262306a36Sopenharmony_ci * delayed attr operation
57362306a36Sopenharmony_ci */
57462306a36Sopenharmony_ciint
57562306a36Sopenharmony_cixfs_attr_rmtval_find_space(
57662306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct xfs_da_args		*args = attr->xattri_da_args;
57962306a36Sopenharmony_ci	struct xfs_bmbt_irec		*map = &attr->xattri_map;
58062306a36Sopenharmony_ci	int				error;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	attr->xattri_lblkno = 0;
58362306a36Sopenharmony_ci	attr->xattri_blkcnt = 0;
58462306a36Sopenharmony_ci	args->rmtblkcnt = 0;
58562306a36Sopenharmony_ci	args->rmtblkno = 0;
58662306a36Sopenharmony_ci	memset(map, 0, sizeof(struct xfs_bmbt_irec));
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	error = xfs_attr_rmt_find_hole(args);
58962306a36Sopenharmony_ci	if (error)
59062306a36Sopenharmony_ci		return error;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	attr->xattri_blkcnt = args->rmtblkcnt;
59362306a36Sopenharmony_ci	attr->xattri_lblkno = args->rmtblkno;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	return 0;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/*
59962306a36Sopenharmony_ci * Write one block of the value associated with an attribute into the
60062306a36Sopenharmony_ci * out-of-line buffer that we have defined for it. This is similar to a subset
60162306a36Sopenharmony_ci * of xfs_attr_rmtval_set, but records the current block to the delayed attr
60262306a36Sopenharmony_ci * context, and leaves transaction handling to the caller.
60362306a36Sopenharmony_ci */
60462306a36Sopenharmony_ciint
60562306a36Sopenharmony_cixfs_attr_rmtval_set_blk(
60662306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct xfs_da_args		*args = attr->xattri_da_args;
60962306a36Sopenharmony_ci	struct xfs_inode		*dp = args->dp;
61062306a36Sopenharmony_ci	struct xfs_bmbt_irec		*map = &attr->xattri_map;
61162306a36Sopenharmony_ci	int nmap;
61262306a36Sopenharmony_ci	int error;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	nmap = 1;
61562306a36Sopenharmony_ci	error = xfs_bmapi_write(args->trans, dp,
61662306a36Sopenharmony_ci			(xfs_fileoff_t)attr->xattri_lblkno,
61762306a36Sopenharmony_ci			attr->xattri_blkcnt, XFS_BMAPI_ATTRFORK, args->total,
61862306a36Sopenharmony_ci			map, &nmap);
61962306a36Sopenharmony_ci	if (error)
62062306a36Sopenharmony_ci		return error;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	ASSERT(nmap == 1);
62362306a36Sopenharmony_ci	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
62462306a36Sopenharmony_ci	       (map->br_startblock != HOLESTARTBLOCK));
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* roll attribute extent map forwards */
62762306a36Sopenharmony_ci	attr->xattri_lblkno += map->br_blockcount;
62862306a36Sopenharmony_ci	attr->xattri_blkcnt -= map->br_blockcount;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return 0;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci/*
63462306a36Sopenharmony_ci * Remove the value associated with an attribute by deleting the
63562306a36Sopenharmony_ci * out-of-line buffer that it is stored on.
63662306a36Sopenharmony_ci */
63762306a36Sopenharmony_ciint
63862306a36Sopenharmony_cixfs_attr_rmtval_invalidate(
63962306a36Sopenharmony_ci	struct xfs_da_args	*args)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	xfs_dablk_t		lblkno;
64262306a36Sopenharmony_ci	int			blkcnt;
64362306a36Sopenharmony_ci	int			error;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/*
64662306a36Sopenharmony_ci	 * Roll through the "value", invalidating the attribute value's blocks.
64762306a36Sopenharmony_ci	 */
64862306a36Sopenharmony_ci	lblkno = args->rmtblkno;
64962306a36Sopenharmony_ci	blkcnt = args->rmtblkcnt;
65062306a36Sopenharmony_ci	while (blkcnt > 0) {
65162306a36Sopenharmony_ci		struct xfs_bmbt_irec	map;
65262306a36Sopenharmony_ci		int			nmap;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		/*
65562306a36Sopenharmony_ci		 * Try to remember where we decided to put the value.
65662306a36Sopenharmony_ci		 */
65762306a36Sopenharmony_ci		nmap = 1;
65862306a36Sopenharmony_ci		error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
65962306a36Sopenharmony_ci				       blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK);
66062306a36Sopenharmony_ci		if (error)
66162306a36Sopenharmony_ci			return error;
66262306a36Sopenharmony_ci		if (XFS_IS_CORRUPT(args->dp->i_mount, nmap != 1))
66362306a36Sopenharmony_ci			return -EFSCORRUPTED;
66462306a36Sopenharmony_ci		error = xfs_attr_rmtval_stale(args->dp, &map, XBF_TRYLOCK);
66562306a36Sopenharmony_ci		if (error)
66662306a36Sopenharmony_ci			return error;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		lblkno += map.br_blockcount;
66962306a36Sopenharmony_ci		blkcnt -= map.br_blockcount;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci	return 0;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci/*
67562306a36Sopenharmony_ci * Remove the value associated with an attribute by deleting the out-of-line
67662306a36Sopenharmony_ci * buffer that it is stored on. Returns -EAGAIN for the caller to refresh the
67762306a36Sopenharmony_ci * transaction and re-call the function.  Callers should keep calling this
67862306a36Sopenharmony_ci * routine until it returns something other than -EAGAIN.
67962306a36Sopenharmony_ci */
68062306a36Sopenharmony_ciint
68162306a36Sopenharmony_cixfs_attr_rmtval_remove(
68262306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	struct xfs_da_args		*args = attr->xattri_da_args;
68562306a36Sopenharmony_ci	int				error, done;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	/*
68862306a36Sopenharmony_ci	 * Unmap value blocks for this attr.
68962306a36Sopenharmony_ci	 */
69062306a36Sopenharmony_ci	error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno,
69162306a36Sopenharmony_ci			    args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done);
69262306a36Sopenharmony_ci	if (error)
69362306a36Sopenharmony_ci		return error;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	/*
69662306a36Sopenharmony_ci	 * We don't need an explicit state here to pick up where we left off. We
69762306a36Sopenharmony_ci	 * can figure it out using the !done return code. The actual value of
69862306a36Sopenharmony_ci	 * attr->xattri_dela_state may be some value reminiscent of the calling
69962306a36Sopenharmony_ci	 * function, but it's value is irrelevant with in the context of this
70062306a36Sopenharmony_ci	 * function. Once we are done here, the next state is set as needed by
70162306a36Sopenharmony_ci	 * the parent
70262306a36Sopenharmony_ci	 */
70362306a36Sopenharmony_ci	if (!done) {
70462306a36Sopenharmony_ci		trace_xfs_attr_rmtval_remove_return(attr->xattri_dela_state,
70562306a36Sopenharmony_ci						    args->dp);
70662306a36Sopenharmony_ci		return -EAGAIN;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	args->rmtblkno = 0;
71062306a36Sopenharmony_ci	args->rmtblkcnt = 0;
71162306a36Sopenharmony_ci	return 0;
71262306a36Sopenharmony_ci}
713