[PATCH 2/3] ocfs2: reserve value tree metadata when moving an xattr into the inode

From: Joseph Qi

Date: Sat Oct 10 2026 - 07:35:09 EST


ocfs2_calc_xattr_set_need() charges no metadata for the case where an
xattr living in an xattr block or bucket moves back into the inode:

if (old_in_xb) {
if (ocfs2_xattr_can_be_in_inode(inode, xi, xis)) {
clusters_add += new_clusters;
credits += ...;
if (!ocfs2_xattr_is_local(xe))
credits += ocfs2_calc_extend_credits(...);
goto out;
}
}

The credits for extending a value tree are there but the matching blocks
are not, so meta_add stays 0, ocfs2_init_xattr_set_ctxt() skips
ocfs2_reserve_new_metadata_blocks() and ctxt->meta_ac is left NULL.

This is the shape commit 0cdc7dde00ec ("ocfs2: fix missing metadata
reservation for large xattrs") fixed for the xattr block case, and it is
reached the same way: namevalue_size() charges a value larger than
OCFS2_XATTR_INLINE_SIZE as only OCFS2_XATTR_ROOT_SIZE, so
ocfs2_xattr_can_be_in_inode() accepts a new value that will be stored
outside. ocfs2_xa_prepare_entry() installs a value root from def_xv and
grows it, and once the cluster allocator is fragmented enough to need a
second extent ocfs2_add_clusters_in_btree() finds the root full with no
meta_ac at all and answers RESTART_META.
ocfs2_xattr_extend_allocation() turns that into -ENOSPC, or into a
BUG_ON() without that commit, and ocfs2_xa_cleanup_value_truncate() takes
its !orig_clusters arm, dropping the entry and leaking the clusters
already handed out until fsck.ocfs2 reclaims them.

Reserve for the value tree here too. The entry moves back into the
inode rather than into a new xattr block, so unlike the create side of
meta_guess this needs no block of its own.

Fixes: 78f30c314a74 ("ocfs2/xattr: Reserve meta/data at the beginning of ocfs2_xattr_set.")
Cc: <stable@xxxxxxxxxxxxxxx>
Signed-off-by: Joseph Qi <joseph.qi@xxxxxxxxxxxxxxxxx>
---
fs/ocfs2/xattr.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 801d3d311115..53ba52381112 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -3480,6 +3480,14 @@ static int ocfs2_calc_xattr_set_need(struct inode *inode,
credits += ocfs2_calc_extend_credits(
inode->i_sb,
&def_xv.xv.xr_list);
+ /*
+ * A value too big to stay inline is only charged as
+ * OCFS2_XATTR_ROOT_SIZE above, so it can still need a
+ * value tree of its own. No xattr block is allocated
+ * on this path, so the tree is all we reserve for.
+ */
+ if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE)
+ meta_add += ocfs2_extend_meta_needed(&def_xv.xv.xr_list);
goto out;
}
}
--
2.39.3