@@ -27,6 +27,7 @@
#include "xfs_attr_item.h"
#include "xfs_xattr.h"
#include "xfs_parent.h"
+#include "xfs_iomap.h"
struct kmem_cache *xfs_attr_intent_cache;
@@ -344,6 +345,175 @@ xfs_attr_set_resv(
return ret;
}
+/*
+ * Find attribute specified in args and return iomap pointing to the attribute
+ * data
+ */
+int
+xfs_attr_read_iomap(
+ struct xfs_da_args *args,
+ struct iomap *iomap)
+{
+ struct xfs_inode *ip = args->dp;
+ struct xfs_mount *mp = ip->i_mount;
+ int error;
+ struct xfs_bmbt_irec map[1];
+ int nmap = 1;
+ int seq;
+ unsigned int lockmode = XFS_ILOCK_SHARED;
+ int ret;
+ uint64_t pos = xfs_attr_get_position(args);
+
+ ASSERT(!args->region_offset);
+
+ if (xfs_is_shutdown(mp))
+ return -EIO;
+
+ /* We just need to find the attribute and block it's pointing
+ * to. The reading of data would be done by iomap */
+ args->valuelen = 0;
+ error = xfs_attr_get(args);
+ if (error)
+ return error;
+
+ if (xfs_need_iread_extents(&ip->i_af))
+ lockmode = XFS_ILOCK_EXCL;
+ xfs_ilock(ip, lockmode);
+ error = xfs_bmapi_read(ip, (xfs_fileoff_t)args->rmtblkno,
+ args->rmtblkcnt, map, &nmap,
+ XFS_BMAPI_ATTRFORK);
+ xfs_iunlock(ip, lockmode);
+ if (error)
+ return error;
+
+ map[0].br_startoff = XFS_B_TO_FSB(mp, pos | args->region_offset);
+
+ seq = xfs_iomap_inode_sequence(ip, IOMAP_F_XATTR);
+ trace_xfs_iomap_found(ip, pos, args->valuelen, XFS_ATTR_FORK, map);
+ ret = xfs_bmbt_to_iomap(ip, iomap, map, 0, IOMAP_F_XATTR, seq);
+ /* Attributes are at args->region_offset in cache, beyond EOF of the
+ * file */
+ iomap->flags |= IOMAP_F_BEYOND_EOF;
+
+ return ret;
+}
+
+int
+xfs_attr_read_end_io(
+ struct xfs_da_args *args)
+{
+ struct xfs_inode *ip = args->dp;
+ struct xfs_attr_leafblock *leaf;
+ struct xfs_attr_leaf_entry *entry;
+ struct xfs_attr_leaf_name_remote *name_rmt;
+ struct xfs_buf *bp;
+ struct xfs_mount *mp = args->dp->i_mount;
+ uint32_t crc;
+ int error;
+ unsigned int whichcrc;
+
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+ if (!xfs_inode_hasattr(args->dp)) {
+ error = -ENOATTR;
+ goto out_unlock;
+ }
+
+ error = xfs_iread_extents(args->trans, args->dp, XFS_ATTR_FORK);
+ if (error)
+ goto out_unlock;
+
+ error = xfs_attr3_leaf_read(args->trans, args->dp, args->owner,
+ args->blkno, &bp);
+ if (error)
+ goto out_unlock;
+
+ leaf = bp->b_addr;
+ entry = &xfs_attr3_leaf_entryp(leaf)[args->index];
+
+ whichcrc = (entry->flags & XFS_ATTR_RMCRC_SEL) != 0;
+ name_rmt = xfs_attr3_leaf_name_remote(&(mp->m_sb), leaf,
+ args->index);
+
+ xfs_calc_cksum(args->value, args->valuelen, &crc);
+ error = name_rmt->crc[whichcrc] != crc;
+ if (error) {
+ if (name_rmt->crc[~whichcrc & 1] != crc) {
+ error = -EFSCORRUPTED;
+ goto out_buf_relse;
+ } else {
+ error = -EFSBADCRC;
+ goto out_buf_relse;
+ }
+ }
+
+out_buf_relse:
+ xfs_buf_relse(bp);
+out_unlock:
+ xfs_iunlock(args->dp, XFS_ILOCK_SHARED);
+ return error;
+}
+
+/*
+ * Create an attribute described in args and return iomap pointing to the extent
+ * where attribute data has to be written.
+ *
+ * Created attribute has XFS_ATTR_INCOMPLETE set, and doesn't have any data CRC.
+ * Therefore, when IO is complete xfs_attr_write_end_ioend() need to be called.
+ */
+int
+xfs_attr_write_iomap(
+ struct xfs_da_args *args,
+ struct iomap *iomap)
+{
+ struct xfs_inode *ip = args->dp;
+ struct xfs_mount *mp = ip->i_mount;
+ int error;
+ int nmap = 1;
+ int seq;
+ struct xfs_bmbt_irec imap[1];
+ uint64_t pos = xfs_attr_get_position(args);
+ unsigned int blksize = mp->m_attr_geo->blksize;
+
+ ASSERT(!args->region_offset);
+
+ if (xfs_is_shutdown(mp))
+ return -EIO;
+
+ /* We just want to allocate blocks without copying any data there */
+ args->op_flags |= XFS_DA_OP_EMPTY;
+ args->valuelen = round_up(min_t(int, args->valuelen, blksize), blksize);
+
+ error = xfs_attr_set(args, XFS_ATTRUPDATE_UPSERT, false);
+ if (error)
+ return error;
+
+ ASSERT(args->dp->i_af.if_format != XFS_DINODE_FMT_LOCAL);
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ error = xfs_bmapi_read(ip, (xfs_fileoff_t)args->rmtblkno,
+ args->rmtblkcnt, imap, &nmap,
+ XFS_BMAPI_ATTRFORK);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ if (error)
+ return error;
+
+ /* Instead of xattr extent offset, which will be over data, we need
+ * merkle tree offset in page cache */
+ imap[0].br_startoff = XFS_B_TO_FSBT(mp, pos | args->region_offset);
+
+ seq = xfs_iomap_inode_sequence(ip, IOMAP_F_XATTR);
+ xfs_bmbt_to_iomap(ip, iomap, imap, 0, IOMAP_F_XATTR, seq);
+
+ return 0;
+}
+
+int
+xfs_attr_write_end_ioend(
+ struct xfs_da_args *args)
+{
+ return xfs_attr_set(args, XFS_ATTRUPDATE_FLAGS, false);
+}
+
/*
* Add an attr to a shortform fork. If there is no space,
* xfs_attr_shortform_addname() will convert to leaf format and return -ENOSPC.
@@ -642,11 +812,15 @@ xfs_attr_rmtval_alloc(
goto out;
}
- if (!(args->op_flags & XFS_DA_OP_EMPTY)) {
+ if (args->op_flags & XFS_DA_OP_EMPTY) {
+ /* Set XFS_ATTR_INCOMLETE flag as attribute doesn't have a value
+ * yet (which should be written by iomap). */
+ error = xfs_attr3_leaf_setflag(args);
+ } else {
error = xfs_attr_rmtval_set_value(args);
- if (error)
- return error;
}
+ if (error)
+ return error;
attr->xattri_dela_state = xfs_attr_complete_op(attr,
++attr->xattri_dela_state);
@@ -1613,3 +1787,12 @@ xfs_attr_intent_destroy_cache(void)
kmem_cache_destroy(xfs_attr_intent_cache);
xfs_attr_intent_cache = NULL;
}
+
+/* Retrieve attribute position from the attr data */
+uint64_t
+xfs_attr_get_position(
+ struct xfs_da_args *args)
+{
+ ASSERT(args->namelen == sizeof(uint64_t));
+ return be64_to_cpu(*(uint64_t*)args->name);
+}
@@ -6,6 +6,8 @@
#ifndef __XFS_ATTR_H__
#define __XFS_ATTR_H__
+#include <linux/iomap.h>
+
struct xfs_inode;
struct xfs_da_args;
struct xfs_attr_list_context;
@@ -569,6 +571,10 @@ bool xfs_attr_namecheck(unsigned int attr_flags, const void *name,
size_t length);
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
struct xfs_trans_res xfs_attr_set_resv(const struct xfs_da_args *args);
+int xfs_attr_read_iomap(struct xfs_da_args *args, struct iomap *iomap);
+int xfs_attr_read_end_io(struct xfs_da_args *args);
+int xfs_attr_write_iomap(struct xfs_da_args *args, struct iomap *iomap);
+int xfs_attr_write_end_ioend(struct xfs_da_args *args);
/*
* Check to see if the attr should be upgraded from non-existent or shortform to
@@ -652,4 +658,6 @@ void xfs_attr_intent_destroy_cache(void);
int xfs_attr_sf_totsize(struct xfs_inode *dp);
int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd);
+uint64_t xfs_attr_get_position(struct xfs_da_args *args);
+
#endif /* __XFS_ATTR_H__ */
@@ -62,13 +62,6 @@ xfs_attr3_rmt_blocks(
struct xfs_mount *mp,
unsigned int attrlen)
{
- /*
- * Each contiguous block has a header, so it is not just a simple
- * attribute length to FSB conversion.
- */
- if (xfs_has_crc(mp))
- return howmany(attrlen, xfs_attr3_rmt_buf_space(mp));
-
return XFS_B_TO_FSB(mp, attrlen);
}
@@ -467,11 +460,6 @@ xfs_attr_rmt_find_hole(
unsigned int blkcnt;
xfs_fileoff_t lfileoff = 0;
- /*
- * Because CRC enable attributes have headers, we can't just do a
- * straight byte to FSB conversion and have to take the header space
- * into account.
- */
blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen);
error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
XFS_ATTR_FORK);
@@ -85,6 +85,7 @@ typedef struct xfs_da_args {
int rmtblkcnt2; /* remote attr value block count */
int rmtvaluelen2; /* remote attr value length in bytes */
enum xfs_dacmp cmpresult; /* name compare result for lookups */
+ loff_t region_offset; /* offset of the iomapped attr region */
} xfs_da_args_t;
/*
@@ -645,6 +645,7 @@ typedef struct xfs_attr_leaf_name_local {
} xfs_attr_leaf_name_local_t;
typedef struct xfs_attr_leaf_name_remote {
+ __be32 crc[2]; /* CRC of the xattr data */
__be32 valueblk; /* block number of value bytes */
__be32 valuelen; /* number of bytes in value */
__u8 namelen; /* length of name bytes */
@@ -715,11 +716,13 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_ROOT_BIT 1 /* limit access to trusted attrs */
#define XFS_ATTR_SECURE_BIT 2 /* limit access to secure attrs */
#define XFS_ATTR_PARENT_BIT 3 /* parent pointer attrs */
+#define XFS_ATTR_RMCRC_SEL_BIT 4 /* which CRC field is primary */
#define XFS_ATTR_INCOMPLETE_BIT 7 /* attr in middle of create/delete */
#define XFS_ATTR_LOCAL (1u << XFS_ATTR_LOCAL_BIT)
#define XFS_ATTR_ROOT (1u << XFS_ATTR_ROOT_BIT)
#define XFS_ATTR_SECURE (1u << XFS_ATTR_SECURE_BIT)
#define XFS_ATTR_PARENT (1u << XFS_ATTR_PARENT_BIT)
+#define XFS_ATTR_RMCRC_SEL (1u << XFS_ATTR_RMCRC_SEL_BIT)
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | \
@@ -96,10 +96,11 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_local, valuelen, 0);
XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_local, namelen, 2);
XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_local, nameval, 3);
- XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, valueblk, 0);
- XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, valuelen, 4);
- XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, namelen, 8);
- XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, name, 9);
+ XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, crc, 0);
+ XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, valueblk, 8);
+ XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, valuelen, 12);
+ XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, namelen, 16);
+ XFS_CHECK_OFFSET(struct xfs_attr_leaf_name_remote, name, 17);
XFS_CHECK_STRUCT_SIZE(struct xfs_attr_leafblock, 32);
XFS_CHECK_STRUCT_SIZE(struct xfs_attr_sf_hdr, 4);
XFS_CHECK_OFFSET(struct xfs_attr_sf_hdr, totsize, 0);
@@ -183,6 +183,8 @@ xfs_sb_version_to_features(
features |= XFS_FEAT_PARENT;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)
features |= XFS_FEAT_METADIR;
+ if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_DXATTR)
+ features |= XFS_FEAT_DXATTR;
return features;
}
@@ -330,6 +330,7 @@ typedef struct xfs_mount {
#define XFS_FEAT_NREXT64 (1ULL << 26) /* large extent counters */
#define XFS_FEAT_EXCHANGE_RANGE (1ULL << 27) /* exchange range */
#define XFS_FEAT_METADIR (1ULL << 28) /* metadata directory tree */
+#define XFS_FEAT_DXATTR (1ULL << 29) /* directly mapped xattrs */
/* Mount features */
#define XFS_FEAT_NOATTR2 (1ULL << 48) /* disable attr2 creation */
@@ -386,6 +387,7 @@ __XFS_HAS_FEAT(needsrepair, NEEDSREPAIR)
__XFS_HAS_FEAT(large_extent_counts, NREXT64)
__XFS_HAS_FEAT(exchange_range, EXCHANGE_RANGE)
__XFS_HAS_FEAT(metadir, METADIR)
+__XFS_HAS_FEAT(dxattr, DXATTR)
static inline bool xfs_has_rtgroups(struct xfs_mount *mp)
{