@@ -251,6 +251,8 @@ xfs_attr_get_ilocked(
* If the attribute is found, but exceeds the size limit set by the caller in
* args->valuelen, return -ERANGE with the size of the attribute that was found
* in args->valuelen.
+ *
+ * Using XFS_DA_OP_BUFFER the caller have to release the buffer args->bp.
*/
int
xfs_attr_get(
@@ -269,7 +271,8 @@ xfs_attr_get(
args->hashval = xfs_da_hashname(args->name, args->namelen);
/* Entirely possible to look up a name which doesn't exist */
- args->op_flags = XFS_DA_OP_OKNOENT;
+ args->op_flags = XFS_DA_OP_OKNOENT |
+ (args->op_flags & XFS_DA_OP_BUFFER);
lock_mode = xfs_ilock_attr_map_shared(args->dp);
error = xfs_attr_get_ilocked(args);
@@ -2533,6 +2533,13 @@ xfs_attr3_leaf_getvalue(
name_loc = xfs_attr3_leaf_name_local(leaf, args->index);
ASSERT(name_loc->namelen == args->namelen);
ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0);
+
+ /* must be released by the caller */
+ if (args->op_flags & XFS_DA_OP_BUFFER) {
+ xfs_buf_hold(bp);
+ args->bp = bp;
+ }
+
return xfs_attr_copy_value(args,
&name_loc->nameval[args->namelen],
be16_to_cpu(name_loc->valuelen));
@@ -424,9 +424,18 @@ xfs_attr_rmtval_get(
error = xfs_attr_rmtval_copyout(mp, bp, args->dp->i_ino,
&offset, &valuelen,
&dst);
- xfs_buf_relse(bp);
- if (error)
+ xfs_buf_unlock(bp);
+ /* must be released by the caller */
+ if (args->op_flags & XFS_DA_OP_BUFFER)
+ args->bp = bp;
+ else
+ xfs_buf_rele(bp);
+
+ if (error) {
+ if (args->op_flags & XFS_DA_OP_BUFFER)
+ xfs_buf_rele(args->bp);
return error;
+ }
/* roll attribute extent map forwards */
lblkno += map[i].br_blockcount;
@@ -61,6 +61,7 @@ typedef struct xfs_da_args {
uint8_t filetype; /* filetype of inode for directories */
void *value; /* set of bytes (maybe contain NULLs) */
int valuelen; /* length of value */
+ struct xfs_buf *bp; /* OUT: xfs_buf which contains the attr */
unsigned int attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
unsigned int attr_flags; /* XATTR_{CREATE,REPLACE} */
xfs_dahash_t hashval; /* hash value of name */
@@ -95,6 +96,7 @@ typedef struct xfs_da_args {
#define XFS_DA_OP_REMOVE (1u << 6) /* this is a remove operation */
#define XFS_DA_OP_RECOVERY (1u << 7) /* Log recovery operation */
#define XFS_DA_OP_LOGGED (1u << 8) /* Use intent items to track op */
+#define XFS_DA_OP_BUFFER (1u << 9) /* Return underlying buffer */
#define XFS_DA_OP_FLAGS \
{ XFS_DA_OP_JUSTCHECK, "JUSTCHECK" }, \
@@ -105,7 +107,8 @@ typedef struct xfs_da_args {
{ XFS_DA_OP_NOTIME, "NOTIME" }, \
{ XFS_DA_OP_REMOVE, "REMOVE" }, \
{ XFS_DA_OP_RECOVERY, "RECOVERY" }, \
- { XFS_DA_OP_LOGGED, "LOGGED" }
+ { XFS_DA_OP_LOGGED, "LOGGED" }, \
+ { XFS_DA_OP_BUFFER, "BUFFER" }
/*
* Storage for holding state during Btree searches and split/join ops.
One of essential ideas of fs-verity is that pages which are already verified won't need to be re-verified if they still in page cache. The XFS stores Merkle tree blocks in extended attributes. Each attribute has one Merkle tree block. We can not directly mark underlying xfs_buf's pages as checked. The are not aligned with xattr value and we don't have a reference to that buffer which is immediately release when value is copied out. One way to track that this block was verified is to mark xattr's buffer as verified. If buffer is evicted the incore XBF_VERITY_CHECKED flag is lost. When the xattr is read again xfs_attr_get() returns new buffer without the flag. The flag is then used to tell fs-verity if it's new page or cached one. This patch adds XFS_DA_OP_BUFFER to tell xfs_attr_get() to xfs_buf_hold() underlying buffer and return it as xfs_da_args->bp. The caller must then xfs_buf_rele() the buffer. Signed-off-by: Andrey Albershteyn <aalbersh@redhat.com> --- fs/xfs/libxfs/xfs_attr.c | 5 ++++- fs/xfs/libxfs/xfs_attr_leaf.c | 7 +++++++ fs/xfs/libxfs/xfs_attr_remote.c | 13 +++++++++++-- fs/xfs/libxfs/xfs_da_btree.h | 5 ++++- 4 files changed, 26 insertions(+), 4 deletions(-)