@@ -311,7 +311,8 @@ xfs_attr_calc_size(
* Out of line attribute, cannot double split, but
* make room for the attribute value itself.
*/
- uint dblocks = xfs_attr3_rmt_blocks(mp, args->valuelen);
+ uint dblocks = xfs_attr3_rmt_blocks(mp, args->attr_filter,
+ args->valuelen);
nblks += dblocks;
nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK);
}
@@ -1060,7 +1061,8 @@ xfs_attr_set(
return error;
if (!local)
- rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen);
+ rmt_blks = xfs_attr3_rmt_blocks(mp, args->attr_filter,
+ args->valuelen);
} else {
XFS_STATS_INC(mp, xs_attr_remove);
rmt_blks = xfs_attr3_max_rmt_blocks(mp);
@@ -1166,7 +1168,8 @@ xfs_attr_setname(
if (!local)
rmt_extents = XFS_IEXT_ATTR_MANIP_CNT(
- xfs_attr3_rmt_blocks(mp, args->valuelen));
+ xfs_attr3_rmt_blocks(mp, args->attr_filter,
+ args->valuelen));
xfs_init_attr_trans(args, &tres, &total);
error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
@@ -1561,7 +1561,8 @@ xfs_attr3_leaf_add_work(
name_rmt->valuelen = 0;
name_rmt->valueblk = 0;
args->rmtblkno = 1;
- args->rmtblkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen);
+ args->rmtblkcnt = xfs_attr3_rmt_blocks(mp, args->attr_filter,
+ args->valuelen);
args->rmtvaluelen = args->valuelen;
}
xfs_trans_log_buf(args->trans, bp,
@@ -2494,6 +2495,7 @@ xfs_attr3_leaf_lookup_int(
args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
args->rmtblkcnt = xfs_attr3_rmt_blocks(
args->dp->i_mount,
+ args->attr_filter,
args->rmtvaluelen);
return -EEXIST;
}
@@ -2542,6 +2544,7 @@ xfs_attr3_leaf_getvalue(
args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
args->rmtblkcnt = xfs_attr3_rmt_blocks(args->dp->i_mount,
+ args->attr_filter,
args->rmtvaluelen);
return xfs_attr_copy_value(args, NULL, args->rmtvaluelen);
}
@@ -43,14 +43,23 @@
* the logging system and therefore never have a log item.
*/
+static inline bool
+xfs_attr3_rmt_has_header(
+ struct xfs_mount *mp,
+ unsigned int attrns)
+{
+ return xfs_has_crc(mp) && !(attrns & XFS_ATTR_VERITY);
+}
+
/* How many bytes can be stored in a remote value buffer? */
inline unsigned int
xfs_attr3_rmt_buf_space(
- struct xfs_mount *mp)
+ struct xfs_mount *mp,
+ unsigned int attrns)
{
unsigned int blocksize = mp->m_attr_geo->blksize;
- if (xfs_has_crc(mp))
+ if (xfs_attr3_rmt_has_header(mp, attrns))
return blocksize - sizeof(struct xfs_attr3_rmt_hdr);
return blocksize;
@@ -60,14 +69,15 @@ xfs_attr3_rmt_buf_space(
unsigned int
xfs_attr3_rmt_blocks(
struct xfs_mount *mp,
+ unsigned int attrns,
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));
+ if (xfs_attr3_rmt_has_header(mp, attrns))
+ return howmany(attrlen, xfs_attr3_rmt_buf_space(mp, attrns));
return XFS_B_TO_FSB(mp, attrlen);
}
@@ -249,6 +259,42 @@ const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
.verify_struct = xfs_attr3_rmt_verify_struct,
};
+static void
+xfs_attr3_rmtverity_read_verify(
+ struct xfs_buf *bp)
+{
+}
+
+static xfs_failaddr_t
+xfs_attr3_rmtverity_verify_struct(
+ struct xfs_buf *bp)
+{
+ return NULL;
+}
+
+static void
+xfs_attr3_rmtverity_write_verify(
+ struct xfs_buf *bp)
+{
+}
+
+const struct xfs_buf_ops xfs_attr3_rmtverity_buf_ops = {
+ .name = "xfs_attr3_remote_verity",
+ .magic = { 0, 0 },
+ .verify_read = xfs_attr3_rmtverity_read_verify,
+ .verify_write = xfs_attr3_rmtverity_write_verify,
+ .verify_struct = xfs_attr3_rmtverity_verify_struct,
+};
+
+inline const struct xfs_buf_ops *
+xfs_attr3_remote_buf_ops(
+ unsigned int attrns)
+{
+ if (attrns & XFS_ATTR_VERITY)
+ return &xfs_attr3_rmtverity_buf_ops;
+ return &xfs_attr3_rmt_buf_ops;
+}
+
STATIC int
xfs_attr3_rmt_hdr_set(
struct xfs_mount *mp,
@@ -285,6 +331,40 @@ xfs_attr3_rmt_hdr_set(
return sizeof(struct xfs_attr3_rmt_hdr);
}
+static void
+xfs_attr_rmtverity_transform(
+ struct xfs_buf *bp,
+ xfs_ino_t ino,
+ void *buf,
+ unsigned int byte_cnt)
+{
+ struct xfs_mount *mp = bp->b_mount;
+ struct xfs_attr3_rmtverity_hdr *hdr = buf;
+ char *dst;
+ const char *src;
+ unsigned int i;
+
+ if (byte_cnt >= offsetofend(struct xfs_attr3_rmtverity_hdr, rmv_owner))
+ hdr->rmv_owner ^= cpu_to_be64(ino);
+
+ if (byte_cnt >= offsetofend(struct xfs_attr3_rmtverity_hdr, rmv_blkno))
+ hdr->rmv_blkno ^= cpu_to_be64(xfs_buf_daddr(bp));
+
+ if (byte_cnt >= offsetofend(struct xfs_attr3_rmtverity_hdr, rmv_magic))
+ hdr->rmv_magic ^= cpu_to_be32(XFS_ATTR3_RMTVERITY_MAGIC);
+
+ if (byte_cnt <= offsetof(struct xfs_attr3_rmtverity_hdr, rmv_uuid))
+ return;
+
+ byte_cnt -= offsetof(struct xfs_attr3_rmtverity_hdr, rmv_uuid);
+ byte_cnt = min(byte_cnt, sizeof(uuid_t));
+
+ dst = (void *)&hdr->rmv_uuid;
+ src = (void *)&mp->m_sb.sb_meta_uuid;
+ for (i = 0; i < byte_cnt; i++)
+ dst[i] ^= src[i];
+}
+
/*
* Helper functions to copy attribute data in and out of the one disk extents
*/
@@ -294,6 +374,7 @@ xfs_attr_rmtval_copyout(
struct xfs_buf *bp,
struct xfs_inode *dp,
xfs_ino_t owner,
+ unsigned int attrns,
unsigned int *offset,
unsigned int *valuelen,
uint8_t **dst)
@@ -307,11 +388,11 @@ xfs_attr_rmtval_copyout(
while (len > 0 && *valuelen > 0) {
unsigned int hdr_size = 0;
- unsigned int byte_cnt = xfs_attr3_rmt_buf_space(mp);
+ unsigned int byte_cnt = xfs_attr3_rmt_buf_space(mp, attrns);
byte_cnt = min(*valuelen, byte_cnt);
- if (xfs_has_crc(mp)) {
+ if (xfs_attr3_rmt_has_header(mp, attrns)) {
if (xfs_attr3_rmt_hdr_ok(src, owner, *offset,
byte_cnt, bno)) {
xfs_alert(mp,
@@ -325,6 +406,10 @@ xfs_attr_rmtval_copyout(
memcpy(*dst, src + hdr_size, byte_cnt);
+ if (attrns & XFS_ATTR_VERITY)
+ xfs_attr_rmtverity_transform(bp, dp->i_ino, *dst,
+ byte_cnt);
+
/* roll buffer forwards */
len -= blksize;
src += blksize;
@@ -343,6 +428,7 @@ xfs_attr_rmtval_copyin(
struct xfs_mount *mp,
struct xfs_buf *bp,
xfs_ino_t ino,
+ unsigned int attrns,
unsigned int *offset,
unsigned int *valuelen,
uint8_t **src)
@@ -355,15 +441,20 @@ xfs_attr_rmtval_copyin(
ASSERT(len >= blksize);
while (len > 0 && *valuelen > 0) {
- unsigned int hdr_size;
- unsigned int byte_cnt = xfs_attr3_rmt_buf_space(mp);
+ unsigned int hdr_size = 0;
+ unsigned int byte_cnt = xfs_attr3_rmt_buf_space(mp, attrns);
byte_cnt = min(*valuelen, byte_cnt);
- hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
- byte_cnt, bno);
+ if (xfs_attr3_rmt_has_header(mp, attrns))
+ hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
+ byte_cnt, bno);
memcpy(dst + hdr_size, *src, byte_cnt);
+ if (attrns & XFS_ATTR_VERITY)
+ xfs_attr_rmtverity_transform(bp, ino, dst + hdr_size,
+ byte_cnt);
+
/*
* If this is the last block, zero the remainder of it.
* Check that we are actually the last block, too.
@@ -408,6 +499,7 @@ xfs_attr_rmtval_get(
unsigned int blkcnt = args->rmtblkcnt;
int i;
unsigned int offset = 0;
+ const struct xfs_buf_ops *ops = xfs_attr3_remote_buf_ops(args->attr_filter);
trace_xfs_attr_rmtval_get(args);
@@ -433,14 +525,15 @@ xfs_attr_rmtval_get(
dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
error = xfs_buf_read(mp->m_ddev_targp, dblkno, dblkcnt,
- 0, &bp, &xfs_attr3_rmt_buf_ops);
+ 0, &bp, ops);
if (xfs_metadata_is_sick(error))
xfs_dirattr_mark_sick(args->dp, XFS_ATTR_FORK);
if (error)
return error;
error = xfs_attr_rmtval_copyout(mp, bp, args->dp,
- args->owner, &offset, &valuelen, &dst);
+ args->owner, args->attr_filter,
+ &offset, &valuelen, &dst);
xfs_buf_relse(bp);
if (error)
return error;
@@ -473,7 +566,7 @@ xfs_attr_rmt_find_hole(
* straight byte to FSB conversion and have to take the header space
* into account.
*/
- blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen);
+ blkcnt = xfs_attr3_rmt_blocks(mp, args->attr_filter, args->rmtvaluelen);
error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
XFS_ATTR_FORK);
if (error)
@@ -532,10 +625,10 @@ xfs_attr_rmtval_set_value(
error = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, &bp);
if (error)
return error;
- bp->b_ops = &xfs_attr3_rmt_buf_ops;
+ bp->b_ops = xfs_attr3_remote_buf_ops(args->attr_filter);
- xfs_attr_rmtval_copyin(mp, bp, args->owner, &offset, &valuelen,
- &src);
+ xfs_attr_rmtval_copyin(mp, bp, args->owner, args->attr_filter,
+ &offset, &valuelen, &src);
error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */
xfs_buf_relse(bp);
@@ -6,12 +6,13 @@
#ifndef __XFS_ATTR_REMOTE_H__
#define __XFS_ATTR_REMOTE_H__
-unsigned int xfs_attr3_rmt_blocks(struct xfs_mount *mp, unsigned int attrlen);
+unsigned int xfs_attr3_rmt_blocks(struct xfs_mount *mp, unsigned int attrns,
+ unsigned int attrlen);
/* Number of rmt blocks needed to store the maximally sized attr value */
static inline unsigned int xfs_attr3_max_rmt_blocks(struct xfs_mount *mp)
{
- return xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX);
+ return xfs_attr3_rmt_blocks(mp, 0, XFS_XATTR_SIZE_MAX);
}
int xfs_attr_rmtval_get(struct xfs_da_args *args);
@@ -23,4 +24,7 @@ int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
int xfs_attr_rmtval_set_blk(struct xfs_attr_intent *attr);
int xfs_attr_rmtval_find_space(struct xfs_attr_intent *attr);
+
+const struct xfs_buf_ops *xfs_attr3_remote_buf_ops(unsigned int attrns);
+
#endif /* __XFS_ATTR_REMOTE_H__ */
@@ -878,7 +878,27 @@ struct xfs_attr3_rmt_hdr {
#define XFS_ATTR3_RMT_CRC_OFF offsetof(struct xfs_attr3_rmt_hdr, rm_crc)
-unsigned int xfs_attr3_rmt_buf_space(struct xfs_mount *mp);
+unsigned int xfs_attr3_rmt_buf_space(struct xfs_mount *mp, unsigned int attrns);
+
+/*
+ * XFS_ATTR_VERITY remote attribute block format definition
+ *
+ * fsverity stores blocks of a merkle tree in the extended attributes. The
+ * size of these blocks are a power of two, so we'd like to reduce overhead by
+ * not storing a remote header at the start of each ondisk block. Because
+ * merkle tree blocks are themselves hashes of other merkle tree or data
+ * blocks, we can detect bitflips without needing our own checksum. Settle for
+ * XORing the owner, blkno, magic, and metauuid into the start of each ondisk
+ * merkle tree block.
+ */
+#define XFS_ATTR3_RMTVERITY_MAGIC 0x5955434B /* YUCK */
+
+struct xfs_attr3_rmtverity_hdr {
+ __be64 rmv_owner;
+ __be64 rmv_blkno;
+ __be32 rmv_magic;
+ uuid_t rmv_uuid;
+} __packed;
/* Number of bytes in a directory block. */
static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
@@ -59,6 +59,7 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_STRUCT_SIZE(struct xfs_attr3_leaf_hdr, 80);
XFS_CHECK_STRUCT_SIZE(struct xfs_attr3_leafblock, 80);
XFS_CHECK_STRUCT_SIZE(struct xfs_attr3_rmt_hdr, 56);
+ XFS_CHECK_STRUCT_SIZE(struct xfs_attr3_rmtverity_hdr, 36);
XFS_CHECK_STRUCT_SIZE(struct xfs_da3_blkinfo, 56);
XFS_CHECK_STRUCT_SIZE(struct xfs_da3_intnode, 64);
XFS_CHECK_STRUCT_SIZE(struct xfs_da3_node_hdr, 64);
@@ -206,6 +207,7 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_VALUE(XFS_DQ_BIGTIME_EXPIRY_MIN << XFS_DQ_BIGTIME_SHIFT, 4);
XFS_CHECK_VALUE(XFS_DQ_BIGTIME_EXPIRY_MAX << XFS_DQ_BIGTIME_SHIFT,
16299260424LL);
+
}
#endif /* __XFS_ONDISK_H */
@@ -26,6 +26,7 @@ extern const struct xfs_buf_ops xfs_agfl_buf_ops;
extern const struct xfs_buf_ops xfs_agi_buf_ops;
extern const struct xfs_buf_ops xfs_attr3_leaf_buf_ops;
extern const struct xfs_buf_ops xfs_attr3_rmt_buf_ops;
+extern const struct xfs_buf_ops xfs_attr3_rmtverity_buf_ops;
extern const struct xfs_buf_ops xfs_bmbt_buf_ops;
extern const struct xfs_buf_ops xfs_bnobt_buf_ops;
extern const struct xfs_buf_ops xfs_cntbt_buf_ops;
@@ -110,7 +110,7 @@ xfs_attr3_leaf_inactive(
if (!name_rmt->valueblk)
continue;
- blkcnt = xfs_attr3_rmt_blocks(dp->i_mount,
+ blkcnt = xfs_attr3_rmt_blocks(dp->i_mount, entry->flags,
be32_to_cpu(name_rmt->valuelen));
error = xfs_attr3_rmt_stale(dp,
be32_to_cpu(name_rmt->valueblk), blkcnt);