From 83021b47c2870c0ba948cbcfe08f41bd7730f5fb Mon Sep 17 00:00:00 2001 From: Tim Chase Date: Sat, 10 May 2014 20:13:12 -0500 Subject: Calculate header size correctly in sa_find_sizes() In the case where a variable-sized SA overlaps the spill block pointer and a new variable-sized SA is being added, the header size was improperly calculated to include the to-be-moved SA. This problem could be reproduced when xattr=sa enabled as follows: ln -s $(perl -e 'print "x" x 120') blah setfattr -n security.selinux -v blahblah -h blah The symlink is large enough to interfere with the spill block pointer and has a typical SA registration as follows (shown in modified "zdb -dddd" format): [ ... ZPL_DACL_COUNT ZPL_DACL_ACES ZPL_SYMLINK ] Adding the SA xattr will attempt to extend the registration to: [ ... ZPL_DACL_COUNT ZPL_DACL_ACES ZPL_SYMLINK ZPL_DXATTR ] but since the ZPL_SYMLINK SA interferes with the spill block pointer, it must also be moved to the spill block which will have a registration of: [ ZPL_SYMLINK ZPL_DXATTR ] This commit updates extra_hdrsize when this condition occurs, allowing hdrsize to be subsequently decreased appropriately. Signed-off-by: Tim Chase Signed-off-by: Brian Behlendorf Signed-off-by: Ned Bass Issue #2214 Issue #2228 Issue #2316 Issue #2343 --- module/zfs/sa.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'module/zfs') diff --git a/module/zfs/sa.c b/module/zfs/sa.c index fcc5f3ba0..25153a839 100644 --- a/module/zfs/sa.c +++ b/module/zfs/sa.c @@ -594,7 +594,7 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count, ASSERT(IS_P2ALIGNED(full_space, 8)); for (i = 0; i != attr_count; i++) { - boolean_t is_var_sz; + boolean_t is_var_sz, might_spill_here; *total = P2ROUNDUP(*total, 8); *total += attr_desc[i].sa_length; @@ -606,6 +606,11 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count, var_size++; } + might_spill_here = + buftype == SA_BONUS && *index == -1 && + (*total + P2ROUNDUP(hdrsize, 8)) > + (full_space - sizeof (blkptr_t)); + if (is_var_sz && var_size > 1) { /* * Don't worry that the spill block might overflow. @@ -622,7 +627,7 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count, * spill-over. */ hdrsize += sizeof (uint16_t); - if (*index != -1) + if (*index != -1 || might_spill_here) extra_hdrsize += sizeof (uint16_t); } else { ASSERT(buftype == SA_BONUS); @@ -639,11 +644,8 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count, * space. The sum is used later for sizing bonus * and spill buffer. */ - if (buftype == SA_BONUS && *index == -1 && - (*total + P2ROUNDUP(hdrsize, 8)) > - (full_space - sizeof (blkptr_t))) { + if (might_spill_here) *index = i; - } if ((*total + P2ROUNDUP(hdrsize, 8)) > full_space && buftype == SA_BONUS) -- cgit v1.2.3