diff options
-rw-r--r-- | fs/ocfs2/xattr.c | 241 |
1 files changed, 226 insertions, 15 deletions
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 8ae4e5d1f730..945ca697eb25 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -137,6 +137,51 @@ struct ocfs2_xattr_search { int not_found; }; +/* Operations on struct ocfs2_xa_entry */ +struct ocfs2_xa_loc; +struct ocfs2_xa_loc_operations { + /* + * Return a pointer to the appropriate buffer in loc->xl_storage + * at the given offset from loc->xl_header. + */ + void *(*xlo_offset_pointer)(struct ocfs2_xa_loc *loc, int offset); + + /* + * Remove the name+value at this location. Do whatever is + * appropriate with the remaining name+value pairs. + */ + void (*xlo_wipe_namevalue)(struct ocfs2_xa_loc *loc); +}; + +/* + * Describes an xattr entry location. This is a memory structure + * tracking the on-disk structure. + */ +struct ocfs2_xa_loc { + /* The ocfs2_xattr_header inside the on-disk storage. Not NULL. */ + struct ocfs2_xattr_header *xl_header; + + /* Bytes from xl_header to the end of the storage */ + int xl_size; + + /* + * The ocfs2_xattr_entry this location describes. If this is + * NULL, this location describes the on-disk structure where it + * would have been. + */ + struct ocfs2_xattr_entry *xl_entry; + + /* + * Internal housekeeping + */ + + /* Buffer(s) containing this entry */ + void *xl_storage; + + /* Operations on the storage backing this location */ + const struct ocfs2_xa_loc_operations *xl_ops; +}; + static int ocfs2_xattr_bucket_get_name_value(struct super_block *sb, struct ocfs2_xattr_header *xh, int index, @@ -1418,6 +1463,170 @@ static int ocfs2_xattr_set_value_outside(struct inode *inode, } /* + * Wipe the name+value pair and allow the storage to reclaim it. This + * must be followed by either removal of the entry or a call to + * ocfs2_xa_add_namevalue(). + */ +static void ocfs2_xa_wipe_namevalue(struct ocfs2_xa_loc *loc) +{ + loc->xl_ops->xlo_wipe_namevalue(loc); +} + +static void *ocfs2_xa_block_offset_pointer(struct ocfs2_xa_loc *loc, + int offset) +{ + BUG_ON(offset >= loc->xl_size); + return (char *)loc->xl_header + offset; +} + +/* + * Block storage for xattrs keeps the name+value pairs compacted. When + * we remove one, we have to shift any that preceded it towards the end. + */ +static void ocfs2_xa_block_wipe_namevalue(struct ocfs2_xa_loc *loc) +{ + int i, offset; + int namevalue_offset, first_namevalue_offset, namevalue_size; + struct ocfs2_xattr_entry *entry = loc->xl_entry; + struct ocfs2_xattr_header *xh = loc->xl_header; + u64 value_size = le64_to_cpu(entry->xe_value_size); + int count = le16_to_cpu(xh->xh_count); + + namevalue_offset = le16_to_cpu(entry->xe_name_offset); + namevalue_size = OCFS2_XATTR_SIZE(entry->xe_name_len); + if (value_size > OCFS2_XATTR_INLINE_SIZE) + namevalue_size += OCFS2_XATTR_ROOT_SIZE; + else + namevalue_size += OCFS2_XATTR_SIZE(value_size); + + for (i = 0, first_namevalue_offset = loc->xl_size; + i < count; i++) { + offset = le16_to_cpu(xh->xh_entries[i].xe_name_offset); + if (offset < first_namevalue_offset) + first_namevalue_offset = offset; + } + + /* Shift the name+value pairs */ + memmove((char *)xh + first_namevalue_offset + namevalue_size, + (char *)xh + first_namevalue_offset, + namevalue_offset - first_namevalue_offset); + memset((char *)xh + first_namevalue_offset, 0, namevalue_size); + + /* Now tell xh->xh_entries about it */ + for (i = 0; i < count; i++) { + offset = le16_to_cpu(xh->xh_entries[i].xe_name_offset); + if (offset < namevalue_offset) + le16_add_cpu(&xh->xh_entries[i].xe_name_offset, + namevalue_size); + } + + /* + * Note that we don't update xh_free_start or xh_name_value_len + * because they're not used in block-stored xattrs. + */ +} + +/* + * Operations for xattrs stored in blocks. This includes inline inode + * storage and unindexed ocfs2_xattr_blocks. + */ +static const struct ocfs2_xa_loc_operations ocfs2_xa_block_loc_ops = { + .xlo_offset_pointer = ocfs2_xa_block_offset_pointer, + .xlo_wipe_namevalue = ocfs2_xa_block_wipe_namevalue, +}; + +static void *ocfs2_xa_bucket_offset_pointer(struct ocfs2_xa_loc *loc, + int offset) +{ + struct ocfs2_xattr_bucket *bucket = loc->xl_storage; + int block, block_offset; + + BUG_ON(offset >= OCFS2_XATTR_BUCKET_SIZE); + + /* The header is at the front of the bucket */ + block = offset >> bucket->bu_inode->i_sb->s_blocksize_bits; + block_offset = offset % bucket->bu_inode->i_sb->s_blocksize; + + return bucket_block(bucket, block) + block_offset; +} + +static void ocfs2_xa_bucket_wipe_namevalue(struct ocfs2_xa_loc *loc) +{ + int namevalue_size; + struct ocfs2_xattr_entry *entry = loc->xl_entry; + u64 value_size = le64_to_cpu(entry->xe_value_size); + + namevalue_size = OCFS2_XATTR_SIZE(entry->xe_name_len); + if (value_size > OCFS2_XATTR_INLINE_SIZE) + namevalue_size += OCFS2_XATTR_ROOT_SIZE; + else + namevalue_size += OCFS2_XATTR_SIZE(value_size); + + le16_add_cpu(&loc->xl_header->xh_name_value_len, -namevalue_size); +} + +/* Operations for xattrs stored in buckets. */ +static const struct ocfs2_xa_loc_operations ocfs2_xa_bucket_loc_ops = { + .xlo_offset_pointer = ocfs2_xa_bucket_offset_pointer, + .xlo_wipe_namevalue = ocfs2_xa_bucket_wipe_namevalue, +}; + +static void ocfs2_xa_remove_entry(struct ocfs2_xa_loc *loc) +{ + ocfs2_xa_wipe_namevalue(loc); +} + +static void ocfs2_init_dinode_xa_loc(struct ocfs2_xa_loc *loc, + struct inode *inode, + struct buffer_head *bh, + struct ocfs2_xattr_entry *entry) +{ + struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; + + loc->xl_ops = &ocfs2_xa_block_loc_ops; + loc->xl_storage = bh; + loc->xl_entry = entry; + + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_XATTR_FL) + loc->xl_size = le16_to_cpu(di->i_xattr_inline_size); + else { + BUG_ON(entry); + loc->xl_size = OCFS2_SB(inode->i_sb)->s_xattr_inline_size; + } + loc->xl_header = + (struct ocfs2_xattr_header *)(bh->b_data + bh->b_size - + loc->xl_size); +} + +static void ocfs2_init_xattr_block_xa_loc(struct ocfs2_xa_loc *loc, + struct buffer_head *bh, + struct ocfs2_xattr_entry *entry) +{ + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)bh->b_data; + + BUG_ON(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED); + + loc->xl_ops = &ocfs2_xa_block_loc_ops; + loc->xl_storage = bh; + loc->xl_header = &(xb->xb_attrs.xb_header); + loc->xl_entry = entry; + loc->xl_size = bh->b_size - offsetof(struct ocfs2_xattr_block, + xb_attrs.xb_header); +} + +static void ocfs2_init_xattr_bucket_xa_loc(struct ocfs2_xa_loc *loc, + struct ocfs2_xattr_bucket *bucket, + struct ocfs2_xattr_entry *entry) +{ + loc->xl_ops = &ocfs2_xa_bucket_loc_ops; + loc->xl_storage = bucket; + loc->xl_header = bucket_xh(bucket); + loc->xl_entry = entry; + loc->xl_size = OCFS2_XATTR_BUCKET_SIZE; +} + +/* * ocfs2_xattr_set_entry_local() * * Set, replace or remove extended attribute in local. @@ -1430,7 +1639,14 @@ static void ocfs2_xattr_set_entry_local(struct inode *inode, { size_t name_len = strlen(xi->name); int i; + struct ocfs2_xa_loc loc; + if (xs->xattr_bh == xs->inode_bh) + ocfs2_init_dinode_xa_loc(&loc, inode, xs->inode_bh, + xs->not_found ? NULL : xs->here); + else + ocfs2_init_xattr_block_xa_loc(&loc, xs->xattr_bh, + xs->not_found ? NULL : xs->here); if (xi->value && xs->not_found) { /* Insert the new xattr entry. */ le16_add_cpu(&xs->header->xh_count, 1); @@ -1469,9 +1685,9 @@ static void ocfs2_xattr_set_entry_local(struct inode *inode, xi->value_len); return; } + /* Remove the old name+value. */ - memmove(first_val + size, first_val, val - first_val); - memset(first_val, 0, size); + ocfs2_xa_wipe_namevalue(&loc); xs->here->xe_name_hash = 0; xs->here->xe_name_offset = 0; ocfs2_xattr_set_local(xs->here, 1); @@ -1479,23 +1695,15 @@ static void ocfs2_xattr_set_entry_local(struct inode *inode, min_offs += size; - /* Adjust all value offsets. */ - last = xs->header->xh_entries; - for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) { - size_t o = le16_to_cpu(last->xe_name_offset); - - if (o < offs) - last->xe_name_offset = cpu_to_le16(o + size); - last += 1; - } - if (!xi->value) { /* Remove the old entry. */ - last -= 1; + i = le16_to_cpu(xs->header->xh_count) - 1; + last = &xs->header->xh_entries[i]; + xs->header->xh_count = cpu_to_le16(i); + memmove(xs->here, xs->here + 1, (void *)last - (void *)xs->here); memset(last, 0, sizeof(struct ocfs2_xattr_entry)); - le16_add_cpu(&xs->header->xh_count, -1); } } if (xi->value) { @@ -4769,7 +4977,10 @@ static void ocfs2_xattr_set_entry_normal(struct inode *inode, size_t blocksize = inode->i_sb->s_blocksize; char *val; size_t offs, size, new_size; + struct ocfs2_xa_loc loc; + ocfs2_init_xattr_bucket_xa_loc(&loc, xs->bucket, + xs->not_found ? NULL : xs->here); last = &xh->xh_entries[count]; if (!xs->not_found) { xe = xs->here; @@ -4790,7 +5001,7 @@ static void ocfs2_xattr_set_entry_normal(struct inode *inode, new_size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_SIZE(xi->value_len); - le16_add_cpu(&xh->xh_name_value_len, -size); + ocfs2_xa_wipe_namevalue(&loc); if (xi->value) { if (new_size > size) goto set_new_name_value; |