diff options
author | Tim Chase <[email protected]> | 2014-05-10 20:13:12 -0500 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2014-05-19 11:55:50 -0700 |
commit | 83021b47c2870c0ba948cbcfe08f41bd7730f5fb (patch) | |
tree | 33b90baa091ae8c9e1f284bdfd6de6c553fe2020 | |
parent | 3937ab20f32fc7b79cacfd91c0891f4e1b4ab2de (diff) |
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"
<SA attr layout obj> 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 <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Signed-off-by: Ned Bass <[email protected]>
Issue #2214
Issue #2228
Issue #2316
Issue #2343
-rw-r--r-- | module/zfs/sa.c | 14 |
1 files changed, 8 insertions, 6 deletions
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) |