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