| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * xattr.c |
| * |
| * Copyright (C) 2004, 2008 Oracle. All rights reserved. |
| * |
| * CREDITS: |
| * Lots of code in this file is copy from linux/fs/ext3/xattr.c. |
| * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> |
| */ |
| |
| #include <linux/capability.h> |
| #include <linux/fs.h> |
| #include <linux/types.h> |
| #include <linux/slab.h> |
| #include <linux/highmem.h> |
| #include <linux/pagemap.h> |
| #include <linux/uio.h> |
| #include <linux/sched.h> |
| #include <linux/splice.h> |
| #include <linux/mount.h> |
| #include <linux/writeback.h> |
| #include <linux/falloc.h> |
| #include <linux/sort.h> |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/string.h> |
| #include <linux/security.h> |
| |
| #include <cluster/masklog.h> |
| |
| #include "ocfs2.h" |
| #include "alloc.h" |
| #include "blockcheck.h" |
| #include "dlmglue.h" |
| #include "file.h" |
| #include "symlink.h" |
| #include "sysfile.h" |
| #include "inode.h" |
| #include "journal.h" |
| #include "ocfs2_fs.h" |
| #include "suballoc.h" |
| #include "uptodate.h" |
| #include "buffer_head_io.h" |
| #include "super.h" |
| #include "xattr.h" |
| #include "refcounttree.h" |
| #include "acl.h" |
| #include "ocfs2_trace.h" |
| |
| struct ocfs2_xattr_def_value_root { |
| struct ocfs2_xattr_value_root xv; |
| struct ocfs2_extent_rec er; |
| }; |
| |
| struct ocfs2_xattr_bucket { |
| /* The inode these xattrs are associated with */ |
| struct inode *bu_inode; |
| |
| /* The actual buffers that make up the bucket */ |
| struct buffer_head *bu_bhs[OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET]; |
| |
| /* How many blocks make up one bucket for this filesystem */ |
| int bu_blocks; |
| }; |
| |
| struct ocfs2_xattr_set_ctxt { |
| handle_t *handle; |
| struct ocfs2_alloc_context *meta_ac; |
| struct ocfs2_alloc_context *data_ac; |
| struct ocfs2_cached_dealloc_ctxt dealloc; |
| int set_abort; |
| }; |
| |
| #define OCFS2_XATTR_ROOT_SIZE (sizeof(struct ocfs2_xattr_def_value_root)) |
| #define OCFS2_XATTR_INLINE_SIZE 80 |
| #define OCFS2_XATTR_HEADER_GAP 4 |
| #define OCFS2_XATTR_FREE_IN_IBODY (OCFS2_MIN_XATTR_INLINE_SIZE \ |
| - sizeof(struct ocfs2_xattr_header) \ |
| - OCFS2_XATTR_HEADER_GAP) |
| #define OCFS2_XATTR_FREE_IN_BLOCK(ptr) ((ptr)->i_sb->s_blocksize \ |
| - sizeof(struct ocfs2_xattr_block) \ |
| - sizeof(struct ocfs2_xattr_header) \ |
| - OCFS2_XATTR_HEADER_GAP) |
| |
| static struct ocfs2_xattr_def_value_root def_xv = { |
| .xv.xr_list.l_count = cpu_to_le16(1), |
| }; |
| |
| const struct xattr_handler * const ocfs2_xattr_handlers[] = { |
| &ocfs2_xattr_user_handler, |
| &ocfs2_xattr_trusted_handler, |
| &ocfs2_xattr_security_handler, |
| NULL |
| }; |
| |
| static const struct xattr_handler * const ocfs2_xattr_handler_map[OCFS2_XATTR_MAX] = { |
| [OCFS2_XATTR_INDEX_USER] = &ocfs2_xattr_user_handler, |
| [OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS] = &nop_posix_acl_access, |
| [OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT] = &nop_posix_acl_default, |
| [OCFS2_XATTR_INDEX_TRUSTED] = &ocfs2_xattr_trusted_handler, |
| [OCFS2_XATTR_INDEX_SECURITY] = &ocfs2_xattr_security_handler, |
| }; |
| |
| struct ocfs2_xattr_info { |
| int xi_name_index; |
| const char *xi_name; |
| int xi_name_len; |
| const void *xi_value; |
| size_t xi_value_len; |
| }; |
| |
| struct ocfs2_xattr_search { |
| struct buffer_head *inode_bh; |
| /* |
| * xattr_bh point to the block buffer head which has extended attribute |
| * when extended attribute in inode, xattr_bh is equal to inode_bh. |
| */ |
| struct buffer_head *xattr_bh; |
| struct ocfs2_xattr_header *header; |
| struct ocfs2_xattr_bucket *bucket; |
| void *base; |
| void *end; |
| struct ocfs2_xattr_entry *here; |
| int not_found; |
| }; |
| |
| /* Operations on struct ocfs2_xa_entry */ |
| struct ocfs2_xa_loc; |
| struct ocfs2_xa_loc_operations { |
| /* |
| * Journal functions |
| */ |
| int (*xlo_journal_access)(handle_t *handle, struct ocfs2_xa_loc *loc, |
| int type); |
| void (*xlo_journal_dirty)(handle_t *handle, struct ocfs2_xa_loc *loc); |
| |
| /* |
| * 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); |
| |
| /* Can we reuse the existing entry for the new value? */ |
| int (*xlo_can_reuse)(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi); |
| |
| /* How much space is needed for the new value? */ |
| int (*xlo_check_space)(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi); |
| |
| /* |
| * Return the offset of the first name+value pair. This is |
| * the start of our downward-filling free space. |
| */ |
| int (*xlo_get_free_start)(struct ocfs2_xa_loc *loc); |
| |
| /* |
| * 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); |
| |
| /* Fill xl_entry with a new entry */ |
| void (*xlo_add_entry)(struct ocfs2_xa_loc *loc, u32 name_hash); |
| |
| /* Add name+value storage to an entry */ |
| void (*xlo_add_namevalue)(struct ocfs2_xa_loc *loc, int size); |
| |
| /* |
| * Initialize the value buf's access and bh fields for this entry. |
| * ocfs2_xa_fill_value_buf() will handle the xv pointer. |
| */ |
| void (*xlo_fill_value_buf)(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_value_buf *vb); |
| }; |
| |
| /* |
| * Describes an xattr entry location. This is a memory structure |
| * tracking the on-disk structure. |
| */ |
| struct ocfs2_xa_loc { |
| /* This xattr belongs to this inode */ |
| struct inode *xl_inode; |
| |
| /* 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; |
| }; |
| |
| /* |
| * Convenience functions to calculate how much space is needed for a |
| * given name+value pair |
| */ |
| static int namevalue_size(int name_len, uint64_t value_len) |
| { |
| if (value_len > OCFS2_XATTR_INLINE_SIZE) |
| return OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; |
| else |
| return OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_SIZE(value_len); |
| } |
| |
| static int namevalue_size_xi(struct ocfs2_xattr_info *xi) |
| { |
| return namevalue_size(xi->xi_name_len, xi->xi_value_len); |
| } |
| |
| static int namevalue_size_xe(struct ocfs2_xattr_entry *xe) |
| { |
| u64 value_len = le64_to_cpu(xe->xe_value_size); |
| |
| BUG_ON((value_len > OCFS2_XATTR_INLINE_SIZE) && |
| ocfs2_xattr_is_local(xe)); |
| return namevalue_size(xe->xe_name_len, value_len); |
| } |
| |
| |
| static int ocfs2_xattr_bucket_get_name_value(struct super_block *sb, |
| struct ocfs2_xattr_header *xh, |
| int index, |
| int *block_off, |
| int *new_offset); |
| |
| static int ocfs2_xattr_block_find(struct inode *inode, |
| int name_index, |
| const char *name, |
| struct ocfs2_xattr_search *xs); |
| static int ocfs2_xattr_index_block_find(struct inode *inode, |
| struct buffer_head *root_bh, |
| int name_index, |
| const char *name, |
| struct ocfs2_xattr_search *xs); |
| |
| static int ocfs2_xattr_tree_list_index_block(struct inode *inode, |
| struct buffer_head *blk_bh, |
| char *buffer, |
| size_t buffer_size); |
| |
| static int ocfs2_xattr_create_index_block(struct inode *inode, |
| struct ocfs2_xattr_search *xs, |
| struct ocfs2_xattr_set_ctxt *ctxt); |
| |
| static int ocfs2_xattr_set_entry_index_block(struct inode *inode, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_search *xs, |
| struct ocfs2_xattr_set_ctxt *ctxt); |
| |
| typedef int (xattr_tree_rec_func)(struct inode *inode, |
| struct buffer_head *root_bh, |
| u64 blkno, u32 cpos, u32 len, void *para); |
| static int ocfs2_iterate_xattr_index_block(struct inode *inode, |
| struct buffer_head *root_bh, |
| xattr_tree_rec_func *rec_func, |
| void *para); |
| static int ocfs2_delete_xattr_in_bucket(struct inode *inode, |
| struct ocfs2_xattr_bucket *bucket, |
| void *para); |
| static int ocfs2_rm_xattr_cluster(struct inode *inode, |
| struct buffer_head *root_bh, |
| u64 blkno, |
| u32 cpos, |
| u32 len, |
| void *para); |
| |
| static int ocfs2_mv_xattr_buckets(struct inode *inode, handle_t *handle, |
| u64 src_blk, u64 last_blk, u64 to_blk, |
| unsigned int start_bucket, |
| u32 *first_hash); |
| static int ocfs2_prepare_refcount_xattr(struct inode *inode, |
| struct ocfs2_dinode *di, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_search *xis, |
| struct ocfs2_xattr_search *xbs, |
| struct ocfs2_refcount_tree **ref_tree, |
| int *meta_need, |
| int *credits); |
| static int ocfs2_get_xattr_tree_value_root(struct super_block *sb, |
| struct ocfs2_xattr_bucket *bucket, |
| int offset, |
| struct ocfs2_xattr_value_root **xv, |
| struct buffer_head **bh); |
| |
| static inline u16 ocfs2_xattr_buckets_per_cluster(struct ocfs2_super *osb) |
| { |
| return (1 << osb->s_clustersize_bits) / OCFS2_XATTR_BUCKET_SIZE; |
| } |
| |
| static inline u16 ocfs2_blocks_per_xattr_bucket(struct super_block *sb) |
| { |
| return OCFS2_XATTR_BUCKET_SIZE / (1 << sb->s_blocksize_bits); |
| } |
| |
| #define bucket_blkno(_b) ((_b)->bu_bhs[0]->b_blocknr) |
| #define bucket_block(_b, _n) ((_b)->bu_bhs[(_n)]->b_data) |
| #define bucket_xh(_b) ((struct ocfs2_xattr_header *)bucket_block((_b), 0)) |
| |
| static struct ocfs2_xattr_bucket *ocfs2_xattr_bucket_new(struct inode *inode) |
| { |
| struct ocfs2_xattr_bucket *bucket; |
| int blks = ocfs2_blocks_per_xattr_bucket(inode->i_sb); |
| |
| BUG_ON(blks > OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET); |
| |
| bucket = kzalloc(sizeof(struct ocfs2_xattr_bucket), GFP_NOFS); |
| if (bucket) { |
| bucket->bu_inode = inode; |
| bucket->bu_blocks = blks; |
| } |
| |
| return bucket; |
| } |
| |
| static void ocfs2_xattr_bucket_relse(struct ocfs2_xattr_bucket *bucket) |
| { |
| int i; |
| |
| for (i = 0; i < bucket->bu_blocks; i++) { |
| brelse(bucket->bu_bhs[i]); |
| bucket->bu_bhs[i] = NULL; |
| } |
| } |
| |
| static void ocfs2_xattr_bucket_free(struct ocfs2_xattr_bucket *bucket) |
| { |
| if (bucket) { |
| ocfs2_xattr_bucket_relse(bucket); |
| bucket->bu_inode = NULL; |
| kfree(bucket); |
| } |
| } |
| |
| /* |
| * A bucket that has never been written to disk doesn't need to be |
| * read. We just need the buffer_heads. Don't call this for |
| * buckets that are already on disk. ocfs2_read_xattr_bucket() initializes |
| * them fully. |
| */ |
| static int ocfs2_init_xattr_bucket(struct ocfs2_xattr_bucket *bucket, |
| u64 xb_blkno, int new) |
| { |
| int i, rc = 0; |
| |
| for (i = 0; i < bucket->bu_blocks; i++) { |
| bucket->bu_bhs[i] = sb_getblk(bucket->bu_inode->i_sb, |
| xb_blkno + i); |
| if (!bucket->bu_bhs[i]) { |
| rc = -ENOMEM; |
| mlog_errno(rc); |
| break; |
| } |
| |
| if (!ocfs2_buffer_uptodate(INODE_CACHE(bucket->bu_inode), |
| bucket->bu_bhs[i])) { |
| if (new) |
| ocfs2_set_new_buffer_uptodate(INODE_CACHE(bucket->bu_inode), |
| bucket->bu_bhs[i]); |
| else { |
| set_buffer_uptodate(bucket->bu_bhs[i]); |
| ocfs2_set_buffer_uptodate(INODE_CACHE(bucket->bu_inode), |
| bucket->bu_bhs[i]); |
| } |
| } |
| } |
| |
| if (rc) |
| ocfs2_xattr_bucket_relse(bucket); |
| return rc; |
| } |
| |
| /* Read the xattr bucket at xb_blkno */ |
| static int ocfs2_read_xattr_bucket(struct ocfs2_xattr_bucket *bucket, |
| u64 xb_blkno) |
| { |
| int rc; |
| |
| rc = ocfs2_read_blocks(INODE_CACHE(bucket->bu_inode), xb_blkno, |
| bucket->bu_blocks, bucket->bu_bhs, 0, |
| NULL); |
| if (!rc) { |
| spin_lock(&OCFS2_SB(bucket->bu_inode->i_sb)->osb_xattr_lock); |
| rc = ocfs2_validate_meta_ecc_bhs(bucket->bu_inode->i_sb, |
| bucket->bu_bhs, |
| bucket->bu_blocks, |
| &bucket_xh(bucket)->xh_check); |
| spin_unlock(&OCFS2_SB(bucket->bu_inode->i_sb)->osb_xattr_lock); |
| if (rc) |
| mlog_errno(rc); |
| } |
| |
| if (rc) |
| ocfs2_xattr_bucket_relse(bucket); |
| return rc; |
| } |
| |
| static int ocfs2_xattr_bucket_journal_access(handle_t *handle, |
| struct ocfs2_xattr_bucket *bucket, |
| int type) |
| { |
| int i, rc = 0; |
| |
| for (i = 0; i < bucket->bu_blocks; i++) { |
| rc = ocfs2_journal_access(handle, |
| INODE_CACHE(bucket->bu_inode), |
| bucket->bu_bhs[i], type); |
| if (rc) { |
| mlog_errno(rc); |
| break; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static void ocfs2_xattr_bucket_journal_dirty(handle_t *handle, |
| struct ocfs2_xattr_bucket *bucket) |
| { |
| int i; |
| |
| spin_lock(&OCFS2_SB(bucket->bu_inode->i_sb)->osb_xattr_lock); |
| ocfs2_compute_meta_ecc_bhs(bucket->bu_inode->i_sb, |
| bucket->bu_bhs, bucket->bu_blocks, |
| &bucket_xh(bucket)->xh_check); |
| spin_unlock(&OCFS2_SB(bucket->bu_inode->i_sb)->osb_xattr_lock); |
| |
| for (i = 0; i < bucket->bu_blocks; i++) |
| ocfs2_journal_dirty(handle, bucket->bu_bhs[i]); |
| } |
| |
| static void ocfs2_xattr_bucket_copy_data(struct ocfs2_xattr_bucket *dest, |
| struct ocfs2_xattr_bucket *src) |
| { |
| int i; |
| int blocksize = src->bu_inode->i_sb->s_blocksize; |
| |
| BUG_ON(dest->bu_blocks != src->bu_blocks); |
| BUG_ON(dest->bu_inode != src->bu_inode); |
| |
| for (i = 0; i < src->bu_blocks; i++) { |
| memcpy(bucket_block(dest, i), bucket_block(src, i), |
| blocksize); |
| } |
| } |
| |
| static int ocfs2_validate_xattr_block(struct super_block *sb, |
| struct buffer_head *bh) |
| { |
| int rc; |
| struct ocfs2_xattr_block *xb = |
| (struct ocfs2_xattr_block *)bh->b_data; |
| |
| trace_ocfs2_validate_xattr_block((unsigned long long)bh->b_blocknr); |
| |
| BUG_ON(!buffer_uptodate(bh)); |
| |
| /* |
| * If the ecc fails, we return the error but otherwise |
| * leave the filesystem running. We know any error is |
| * local to this block. |
| */ |
| rc = ocfs2_validate_meta_ecc(sb, bh->b_data, &xb->xb_check); |
| if (rc) |
| return rc; |
| |
| /* |
| * Errors after here are fatal |
| */ |
| |
| if (!OCFS2_IS_VALID_XATTR_BLOCK(xb)) { |
| return ocfs2_error(sb, |
| "Extended attribute block #%llu has bad signature %.*s\n", |
| (unsigned long long)bh->b_blocknr, 7, |
| xb->xb_signature); |
| } |
| |
| if (le64_to_cpu(xb->xb_blkno) != bh->b_blocknr) { |
| return ocfs2_error(sb, |
| "Extended attribute block #%llu has an invalid xb_blkno of %llu\n", |
| (unsigned long long)bh->b_blocknr, |
| (unsigned long long)le64_to_cpu(xb->xb_blkno)); |
| } |
| |
| if (le32_to_cpu(xb->xb_fs_generation) != OCFS2_SB(sb)->fs_generation) { |
| return ocfs2_error(sb, |
| "Extended attribute block #%llu has an invalid xb_fs_generation of #%u\n", |
| (unsigned long long)bh->b_blocknr, |
| le32_to_cpu(xb->xb_fs_generation)); |
| } |
| |
| return 0; |
| } |
| |
| static int ocfs2_read_xattr_block(struct inode *inode, u64 xb_blkno, |
| struct buffer_head **bh) |
| { |
| int rc; |
| struct buffer_head *tmp = *bh; |
| |
| rc = ocfs2_read_block(INODE_CACHE(inode), xb_blkno, &tmp, |
| ocfs2_validate_xattr_block); |
| |
| /* If ocfs2_read_block() got us a new bh, pass it up. */ |
| if (!rc && !*bh) |
| *bh = tmp; |
| |
| return rc; |
| } |
| |
| static inline const char *ocfs2_xattr_prefix(int name_index) |
| { |
| const struct xattr_handler *handler = NULL; |
| |
| if (name_index > 0 && name_index < OCFS2_XATTR_MAX) |
| handler = ocfs2_xattr_handler_map[name_index]; |
| return handler ? xattr_prefix(handler) : NULL; |
| } |
| |
| static u32 ocfs2_xattr_name_hash(struct inode *inode, |
| const char *name, |
| int name_len) |
| { |
| /* Get hash value of uuid from super block */ |
| u32 hash = OCFS2_SB(inode->i_sb)->uuid_hash; |
| int i; |
| |
| /* hash extended attribute name */ |
| for (i = 0; i < name_len; i++) { |
| hash = (hash << OCFS2_HASH_SHIFT) ^ |
| (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ |
| *name++; |
| } |
| |
| return hash; |
| } |
| |
| static int ocfs2_xattr_entry_real_size(int name_len, size_t value_len) |
| { |
| return namevalue_size(name_len, value_len) + |
| sizeof(struct ocfs2_xattr_entry); |
| } |
| |
| static int ocfs2_xi_entry_usage(struct ocfs2_xattr_info *xi) |
| { |
| return namevalue_size_xi(xi) + |
| sizeof(struct ocfs2_xattr_entry); |
| } |
| |
| static int ocfs2_xe_entry_usage(struct ocfs2_xattr_entry *xe) |
| { |
| return namevalue_size_xe(xe) + |
| sizeof(struct ocfs2_xattr_entry); |
| } |
| |
| int ocfs2_calc_security_init(struct inode *dir, |
| struct ocfs2_security_xattr_info *si, |
| int *want_clusters, |
| int *xattr_credits, |
| struct ocfs2_alloc_context **xattr_ac) |
| { |
| int ret = 0; |
| struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); |
| int s_size = ocfs2_xattr_entry_real_size(strlen(si->name), |
| si->value_len); |
| |
| /* |
| * The max space of security xattr taken inline is |
| * 256(name) + 80(value) + 16(entry) = 352 bytes, |
| * So reserve one metadata block for it is ok. |
| */ |
| if (dir->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE || |
| s_size > OCFS2_XATTR_FREE_IN_IBODY) { |
| ret = ocfs2_reserve_new_metadata_blocks(osb, 1, xattr_ac); |
| if (ret) { |
| mlog_errno(ret); |
| return ret; |
| } |
| *xattr_credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; |
| } |
| |
| /* reserve clusters for xattr value which will be set in B tree*/ |
| if (si->value_len > OCFS2_XATTR_INLINE_SIZE) { |
| int new_clusters = ocfs2_clusters_for_bytes(dir->i_sb, |
| si->value_len); |
| |
| *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb, |
| new_clusters); |
| *want_clusters += new_clusters; |
| } |
| return ret; |
| } |
| |
| int ocfs2_calc_xattr_init(struct inode *dir, |
| struct buffer_head *dir_bh, |
| umode_t mode, |
| struct ocfs2_security_xattr_info *si, |
| int *want_clusters, |
| int *xattr_credits, |
| int *want_meta) |
| { |
| int ret = 0; |
| struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); |
| int s_size = 0, a_size = 0, acl_len = 0, new_clusters; |
| |
| if (si->enable) |
| s_size = ocfs2_xattr_entry_real_size(strlen(si->name), |
| si->value_len); |
| |
| if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) { |
| down_read(&OCFS2_I(dir)->ip_xattr_sem); |
| acl_len = ocfs2_xattr_get_nolock(dir, dir_bh, |
| OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT, |
| "", NULL, 0); |
| up_read(&OCFS2_I(dir)->ip_xattr_sem); |
| if (acl_len > 0) { |
| a_size = ocfs2_xattr_entry_real_size(0, acl_len); |
| if (S_ISDIR(mode)) |
| a_size <<= 1; |
| } else if (acl_len != 0 && acl_len != -ENODATA) { |
| ret = acl_len; |
| mlog_errno(ret); |
| return ret; |
| } |
| } |
| |
| if (!(s_size + a_size)) |
| return ret; |
| |
| /* |
| * The max space of security xattr taken inline is |
| * 256(name) + 80(value) + 16(entry) = 352 bytes, |
| * The max space of acl xattr taken inline is |
| * 80(value) + 16(entry) * 2(if directory) = 192 bytes, |
| * when blocksize = 512, may reserve one more cluser for |
| * xattr bucket, otherwise reserve one metadata block |
| * for them is ok. |
| * If this is a new directory with inline data, |
| * we choose to reserve the entire inline area for |
| * directory contents and force an external xattr block. |
| */ |
| if (dir->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE || |
| (S_ISDIR(mode) && ocfs2_supports_inline_data(osb)) || |
| (s_size + a_size) > OCFS2_XATTR_FREE_IN_IBODY) { |
| *want_meta = *want_meta + 1; |
| *xattr_credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; |
| } |
| |
| if (dir->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE && |
| (s_size + a_size) > OCFS2_XATTR_FREE_IN_BLOCK(dir)) { |
| *want_clusters += 1; |
| *xattr_credits += ocfs2_blocks_per_xattr_bucket(dir->i_sb); |
| } |
| |
| /* |
| * reserve credits and clusters for xattrs which has large value |
| * and have to be set outside |
| */ |
| if (si->enable && si->value_len > OCFS2_XATTR_INLINE_SIZE) { |
| new_clusters = ocfs2_clusters_for_bytes(dir->i_sb, |
| si->value_len); |
| *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb, |
| new_clusters); |
| *want_clusters += new_clusters; |
| } |
| if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL && |
| acl_len > OCFS2_XATTR_INLINE_SIZE) { |
| /* for directory, it has DEFAULT and ACCESS two types of acls */ |
| new_clusters = (S_ISDIR(mode) ? 2 : 1) * |
| ocfs2_clusters_for_bytes(dir->i_sb, acl_len); |
| *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb, |
| new_clusters); |
| *want_clusters += new_clusters; |
| } |
| |
| return ret; |
| } |
| |
| static int ocfs2_xattr_extend_allocation(struct inode *inode, |
| u32 clusters_to_add, |
| struct ocfs2_xattr_value_buf *vb, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int status = 0, credits; |
| handle_t *handle = ctxt->handle; |
| enum ocfs2_alloc_restarted why; |
| u32 prev_clusters, logical_start = le32_to_cpu(vb->vb_xv->xr_clusters); |
| struct ocfs2_extent_tree et; |
| |
| ocfs2_init_xattr_value_extent_tree(&et, INODE_CACHE(inode), vb); |
| |
| while (clusters_to_add) { |
| trace_ocfs2_xattr_extend_allocation(clusters_to_add); |
| |
| status = vb->vb_access(handle, INODE_CACHE(inode), vb->vb_bh, |
| OCFS2_JOURNAL_ACCESS_WRITE); |
| if (status < 0) { |
| mlog_errno(status); |
| break; |
| } |
| |
| prev_clusters = le32_to_cpu(vb->vb_xv->xr_clusters); |
| status = ocfs2_add_clusters_in_btree(handle, |
| &et, |
| &logical_start, |
| clusters_to_add, |
| 0, |
| ctxt->data_ac, |
| ctxt->meta_ac, |
| &why); |
| if ((status < 0) && (status != -EAGAIN)) { |
| if (status != -ENOSPC) |
| mlog_errno(status); |
| break; |
| } |
| |
| ocfs2_journal_dirty(handle, vb->vb_bh); |
| |
| clusters_to_add -= le32_to_cpu(vb->vb_xv->xr_clusters) - |
| prev_clusters; |
| |
| if (why != RESTART_NONE && clusters_to_add) { |
| /* |
| * We can only fail in case the alloc file doesn't give |
| * up enough clusters. |
| */ |
| BUG_ON(why == RESTART_META); |
| |
| credits = ocfs2_calc_extend_credits(inode->i_sb, |
| &vb->vb_xv->xr_list); |
| status = ocfs2_extend_trans(handle, credits); |
| if (status < 0) { |
| status = -ENOMEM; |
| mlog_errno(status); |
| break; |
| } |
| } |
| } |
| |
| return status; |
| } |
| |
| static int __ocfs2_remove_xattr_range(struct inode *inode, |
| struct ocfs2_xattr_value_buf *vb, |
| u32 cpos, u32 phys_cpos, u32 len, |
| unsigned int ext_flags, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int ret; |
| u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos); |
| handle_t *handle = ctxt->handle; |
| struct ocfs2_extent_tree et; |
| |
| ocfs2_init_xattr_value_extent_tree(&et, INODE_CACHE(inode), vb); |
| |
| ret = vb->vb_access(handle, INODE_CACHE(inode), vb->vb_bh, |
| OCFS2_JOURNAL_ACCESS_WRITE); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| ret = ocfs2_remove_extent(handle, &et, cpos, len, ctxt->meta_ac, |
| &ctxt->dealloc); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| le32_add_cpu(&vb->vb_xv->xr_clusters, -len); |
| ocfs2_journal_dirty(handle, vb->vb_bh); |
| |
| if (ext_flags & OCFS2_EXT_REFCOUNTED) |
| ret = ocfs2_decrease_refcount(inode, handle, |
| ocfs2_blocks_to_clusters(inode->i_sb, |
| phys_blkno), |
| len, ctxt->meta_ac, &ctxt->dealloc, 1); |
| else |
| ret = ocfs2_cache_cluster_dealloc(&ctxt->dealloc, |
| phys_blkno, len); |
| if (ret) |
| mlog_errno(ret); |
| |
| out: |
| return ret; |
| } |
| |
| static int ocfs2_xattr_shrink_size(struct inode *inode, |
| u32 old_clusters, |
| u32 new_clusters, |
| struct ocfs2_xattr_value_buf *vb, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int ret = 0; |
| unsigned int ext_flags; |
| u32 trunc_len, cpos, phys_cpos, alloc_size; |
| u64 block; |
| |
| if (old_clusters <= new_clusters) |
| return 0; |
| |
| cpos = new_clusters; |
| trunc_len = old_clusters - new_clusters; |
| while (trunc_len) { |
| ret = ocfs2_xattr_get_clusters(inode, cpos, &phys_cpos, |
| &alloc_size, |
| &vb->vb_xv->xr_list, &ext_flags); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| if (alloc_size > trunc_len) |
| alloc_size = trunc_len; |
| |
| ret = __ocfs2_remove_xattr_range(inode, vb, cpos, |
| phys_cpos, alloc_size, |
| ext_flags, ctxt); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| block = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos); |
| ocfs2_remove_xattr_clusters_from_cache(INODE_CACHE(inode), |
| block, alloc_size); |
| cpos += alloc_size; |
| trunc_len -= alloc_size; |
| } |
| |
| out: |
| return ret; |
| } |
| |
| static int ocfs2_xattr_value_truncate(struct inode *inode, |
| struct ocfs2_xattr_value_buf *vb, |
| int len, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int ret; |
| u32 new_clusters = ocfs2_clusters_for_bytes(inode->i_sb, len); |
| u32 old_clusters = le32_to_cpu(vb->vb_xv->xr_clusters); |
| |
| if (new_clusters == old_clusters) |
| return 0; |
| |
| if (new_clusters > old_clusters) |
| ret = ocfs2_xattr_extend_allocation(inode, |
| new_clusters - old_clusters, |
| vb, ctxt); |
| else |
| ret = ocfs2_xattr_shrink_size(inode, |
| old_clusters, new_clusters, |
| vb, ctxt); |
| |
| return ret; |
| } |
| |
| static int ocfs2_xattr_list_entry(struct super_block *sb, |
| char *buffer, size_t size, |
| size_t *result, int type, |
| const char *name, int name_len) |
| { |
| char *p = buffer + *result; |
| const char *prefix; |
| int prefix_len; |
| int total_len; |
| |
| switch(type) { |
| case OCFS2_XATTR_INDEX_USER: |
| if (OCFS2_SB(sb)->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR) |
| return 0; |
| break; |
| |
| case OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS: |
| case OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT: |
| if (!(sb->s_flags & SB_POSIXACL)) |
| return 0; |
| break; |
| |
| case OCFS2_XATTR_INDEX_TRUSTED: |
| if (!capable(CAP_SYS_ADMIN)) |
| return 0; |
| break; |
| } |
| |
| prefix = ocfs2_xattr_prefix(type); |
| if (!prefix) |
| return 0; |
| prefix_len = strlen(prefix); |
| total_len = prefix_len + name_len + 1; |
| *result += total_len; |
| |
| /* we are just looking for how big our buffer needs to be */ |
| if (!size) |
| return 0; |
| |
| if (*result > size) |
| return -ERANGE; |
| |
| memcpy(p, prefix, prefix_len); |
| memcpy(p + prefix_len, name, name_len); |
| p[prefix_len + name_len] = '\0'; |
| |
| return 0; |
| } |
| |
| static int ocfs2_xattr_list_entries(struct inode *inode, |
| struct ocfs2_xattr_header *header, |
| char *buffer, size_t buffer_size) |
| { |
| size_t result = 0; |
| int i, type, ret; |
| const char *name; |
| |
| for (i = 0 ; i < le16_to_cpu(header->xh_count); i++) { |
| struct ocfs2_xattr_entry *entry = &header->xh_entries[i]; |
| type = ocfs2_xattr_get_type(entry); |
| name = (const char *)header + |
| le16_to_cpu(entry->xe_name_offset); |
| |
| ret = ocfs2_xattr_list_entry(inode->i_sb, |
| buffer, buffer_size, |
| &result, type, name, |
| entry->xe_name_len); |
| if (ret) |
| return ret; |
| } |
| |
| return result; |
| } |
| |
| int ocfs2_has_inline_xattr_value_outside(struct inode *inode, |
| struct ocfs2_dinode *di) |
| { |
| struct ocfs2_xattr_header *xh; |
| int i; |
| |
| xh = (struct ocfs2_xattr_header *) |
| ((void *)di + inode->i_sb->s_blocksize - |
| le16_to_cpu(di->i_xattr_inline_size)); |
| |
| for (i = 0; i < le16_to_cpu(xh->xh_count); i++) |
| if (!ocfs2_xattr_is_local(&xh->xh_entries[i])) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int ocfs2_xattr_ibody_list(struct inode *inode, |
| struct ocfs2_dinode *di, |
| char *buffer, |
| size_t buffer_size) |
| { |
| struct ocfs2_xattr_header *header = NULL; |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| int ret = 0; |
| |
| if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) |
| return ret; |
| |
| header = (struct ocfs2_xattr_header *) |
| ((void *)di + inode->i_sb->s_blocksize - |
| le16_to_cpu(di->i_xattr_inline_size)); |
| |
| ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size); |
| |
| return ret; |
| } |
| |
| static int ocfs2_xattr_block_list(struct inode *inode, |
| struct ocfs2_dinode *di, |
| char *buffer, |
| size_t buffer_size) |
| { |
| struct buffer_head *blk_bh = NULL; |
| struct ocfs2_xattr_block *xb; |
| int ret = 0; |
| |
| if (!di->i_xattr_loc) |
| return ret; |
| |
| ret = ocfs2_read_xattr_block(inode, le64_to_cpu(di->i_xattr_loc), |
| &blk_bh); |
| if (ret < 0) { |
| mlog_errno(ret); |
| return ret; |
| } |
| |
| xb = (struct ocfs2_xattr_block *)blk_bh->b_data; |
| if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { |
| struct ocfs2_xattr_header *header = &xb->xb_attrs.xb_header; |
| ret = ocfs2_xattr_list_entries(inode, header, |
| buffer, buffer_size); |
| } else |
| ret = ocfs2_xattr_tree_list_index_block(inode, blk_bh, |
| buffer, buffer_size); |
| |
| brelse(blk_bh); |
| |
| return ret; |
| } |
| |
| ssize_t ocfs2_listxattr(struct dentry *dentry, |
| char *buffer, |
| size_t size) |
| { |
| int ret = 0, i_ret = 0, b_ret = 0; |
| struct buffer_head *di_bh = NULL; |
| struct ocfs2_dinode *di = NULL; |
| struct ocfs2_inode_info *oi = OCFS2_I(d_inode(dentry)); |
| |
| if (!ocfs2_supports_xattr(OCFS2_SB(dentry->d_sb))) |
| return -EOPNOTSUPP; |
| |
| if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) |
| return ret; |
| |
| ret = ocfs2_inode_lock(d_inode(dentry), &di_bh, 0); |
| if (ret < 0) { |
| mlog_errno(ret); |
| return ret; |
| } |
| |
| di = (struct ocfs2_dinode *)di_bh->b_data; |
| |
| down_read(&oi->ip_xattr_sem); |
| i_ret = ocfs2_xattr_ibody_list(d_inode(dentry), di, buffer, size); |
| if (i_ret < 0) |
| b_ret = 0; |
| else { |
| if (buffer) { |
| buffer += i_ret; |
| size -= i_ret; |
| } |
| b_ret = ocfs2_xattr_block_list(d_inode(dentry), di, |
| buffer, size); |
| if (b_ret < 0) |
| i_ret = 0; |
| } |
| up_read(&oi->ip_xattr_sem); |
| ocfs2_inode_unlock(d_inode(dentry), 0); |
| |
| brelse(di_bh); |
| |
| return i_ret + b_ret; |
| } |
| |
| static int ocfs2_xattr_find_entry(int name_index, |
| const char *name, |
| struct ocfs2_xattr_search *xs) |
| { |
| struct ocfs2_xattr_entry *entry; |
| size_t name_len; |
| int i, cmp = 1; |
| |
| if (name == NULL) |
| return -EINVAL; |
| |
| name_len = strlen(name); |
| entry = xs->here; |
| for (i = 0; i < le16_to_cpu(xs->header->xh_count); i++) { |
| cmp = name_index - ocfs2_xattr_get_type(entry); |
| if (!cmp) |
| cmp = name_len - entry->xe_name_len; |
| if (!cmp) |
| cmp = memcmp(name, (xs->base + |
| le16_to_cpu(entry->xe_name_offset)), |
| name_len); |
| if (cmp == 0) |
| break; |
| entry += 1; |
| } |
| xs->here = entry; |
| |
| return cmp ? -ENODATA : 0; |
| } |
| |
| static int ocfs2_xattr_get_value_outside(struct inode *inode, |
| struct ocfs2_xattr_value_root *xv, |
| void *buffer, |
| size_t len) |
| { |
| u32 cpos, p_cluster, num_clusters, bpc, clusters; |
| u64 blkno; |
| int i, ret = 0; |
| size_t cplen, blocksize; |
| struct buffer_head *bh = NULL; |
| struct ocfs2_extent_list *el; |
| |
| el = &xv->xr_list; |
| clusters = le32_to_cpu(xv->xr_clusters); |
| bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); |
| blocksize = inode->i_sb->s_blocksize; |
| |
| cpos = 0; |
| while (cpos < clusters) { |
| ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster, |
| &num_clusters, el, NULL); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); |
| /* Copy ocfs2_xattr_value */ |
| for (i = 0; i < num_clusters * bpc; i++, blkno++) { |
| ret = ocfs2_read_block(INODE_CACHE(inode), blkno, |
| &bh, NULL); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| cplen = len >= blocksize ? blocksize : len; |
| memcpy(buffer, bh->b_data, cplen); |
| len -= cplen; |
| buffer += cplen; |
| |
| brelse(bh); |
| bh = NULL; |
| if (len == 0) |
| break; |
| } |
| cpos += num_clusters; |
| } |
| out: |
| return ret; |
| } |
| |
| static int ocfs2_xattr_ibody_get(struct inode *inode, |
| int name_index, |
| const char *name, |
| void *buffer, |
| size_t buffer_size, |
| struct ocfs2_xattr_search *xs) |
| { |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; |
| struct ocfs2_xattr_value_root *xv; |
| size_t size; |
| int ret = 0; |
| |
| if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) |
| return -ENODATA; |
| |
| xs->end = (void *)di + inode->i_sb->s_blocksize; |
| xs->header = (struct ocfs2_xattr_header *) |
| (xs->end - le16_to_cpu(di->i_xattr_inline_size)); |
| xs->base = (void *)xs->header; |
| xs->here = xs->header->xh_entries; |
| |
| ret = ocfs2_xattr_find_entry(name_index, name, xs); |
| if (ret) |
| return ret; |
| size = le64_to_cpu(xs->here->xe_value_size); |
| if (buffer) { |
| if (size > buffer_size) |
| return -ERANGE; |
| if (ocfs2_xattr_is_local(xs->here)) { |
| memcpy(buffer, (void *)xs->base + |
| le16_to_cpu(xs->here->xe_name_offset) + |
| OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); |
| } else { |
| xv = (struct ocfs2_xattr_value_root *) |
| (xs->base + le16_to_cpu( |
| xs->here->xe_name_offset) + |
| OCFS2_XATTR_SIZE(xs->here->xe_name_len)); |
| ret = ocfs2_xattr_get_value_outside(inode, xv, |
| buffer, size); |
| if (ret < 0) { |
| mlog_errno(ret); |
| return ret; |
| } |
| } |
| } |
| |
| return size; |
| } |
| |
| static int ocfs2_xattr_block_get(struct inode *inode, |
| int name_index, |
| const char *name, |
| void *buffer, |
| size_t buffer_size, |
| struct ocfs2_xattr_search *xs) |
| { |
| struct ocfs2_xattr_block *xb; |
| struct ocfs2_xattr_value_root *xv; |
| size_t size; |
| int ret = -ENODATA, name_offset, name_len, i; |
| int block_off; |
| |
| xs->bucket = ocfs2_xattr_bucket_new(inode); |
| if (!xs->bucket) { |
| ret = -ENOMEM; |
| mlog_errno(ret); |
| goto cleanup; |
| } |
| |
| ret = ocfs2_xattr_block_find(inode, name_index, name, xs); |
| if (ret) { |
| mlog_errno(ret); |
| goto cleanup; |
| } |
| |
| if (xs->not_found) { |
| ret = -ENODATA; |
| goto cleanup; |
| } |
| |
| xb = (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; |
| size = le64_to_cpu(xs->here->xe_value_size); |
| if (buffer) { |
| ret = -ERANGE; |
| if (size > buffer_size) |
| goto cleanup; |
| |
| name_offset = le16_to_cpu(xs->here->xe_name_offset); |
| name_len = OCFS2_XATTR_SIZE(xs->here->xe_name_len); |
| i = xs->here - xs->header->xh_entries; |
| |
| if (le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED) { |
| ret = ocfs2_xattr_bucket_get_name_value(inode->i_sb, |
| bucket_xh(xs->bucket), |
| i, |
| &block_off, |
| &name_offset); |
| if (ret) { |
| mlog_errno(ret); |
| goto cleanup; |
| } |
| xs->base = bucket_block(xs->bucket, block_off); |
| } |
| if (ocfs2_xattr_is_local(xs->here)) { |
| memcpy(buffer, (void *)xs->base + |
| name_offset + name_len, size); |
| } else { |
| xv = (struct ocfs2_xattr_value_root *) |
| (xs->base + name_offset + name_len); |
| ret = ocfs2_xattr_get_value_outside(inode, xv, |
| buffer, size); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto cleanup; |
| } |
| } |
| } |
| ret = size; |
| cleanup: |
| ocfs2_xattr_bucket_free(xs->bucket); |
| |
| brelse(xs->xattr_bh); |
| xs->xattr_bh = NULL; |
| return ret; |
| } |
| |
| int ocfs2_xattr_get_nolock(struct inode *inode, |
| struct buffer_head *di_bh, |
| int name_index, |
| const char *name, |
| void *buffer, |
| size_t buffer_size) |
| { |
| int ret; |
| struct ocfs2_dinode *di = NULL; |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| struct ocfs2_xattr_search xis = { |
| .not_found = -ENODATA, |
| }; |
| struct ocfs2_xattr_search xbs = { |
| .not_found = -ENODATA, |
| }; |
| |
| if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb))) |
| return -EOPNOTSUPP; |
| |
| if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) |
| return -ENODATA; |
| |
| xis.inode_bh = xbs.inode_bh = di_bh; |
| di = (struct ocfs2_dinode *)di_bh->b_data; |
| |
| ret = ocfs2_xattr_ibody_get(inode, name_index, name, buffer, |
| buffer_size, &xis); |
| if (ret == -ENODATA && di->i_xattr_loc) |
| ret = ocfs2_xattr_block_get(inode, name_index, name, buffer, |
| buffer_size, &xbs); |
| |
| return ret; |
| } |
| |
| /* ocfs2_xattr_get() |
| * |
| * Copy an extended attribute into the buffer provided. |
| * Buffer is NULL to compute the size of buffer required. |
| */ |
| static int ocfs2_xattr_get(struct inode *inode, |
| int name_index, |
| const char *name, |
| void *buffer, |
| size_t buffer_size) |
| { |
| int ret, had_lock; |
| struct buffer_head *di_bh = NULL; |
| struct ocfs2_lock_holder oh; |
| |
| had_lock = ocfs2_inode_lock_tracker(inode, &di_bh, 0, &oh); |
| if (had_lock < 0) { |
| mlog_errno(had_lock); |
| return had_lock; |
| } |
| down_read(&OCFS2_I(inode)->ip_xattr_sem); |
| ret = ocfs2_xattr_get_nolock(inode, di_bh, name_index, |
| name, buffer, buffer_size); |
| up_read(&OCFS2_I(inode)->ip_xattr_sem); |
| |
| ocfs2_inode_unlock_tracker(inode, 0, &oh, had_lock); |
| |
| brelse(di_bh); |
| |
| return ret; |
| } |
| |
| static int __ocfs2_xattr_set_value_outside(struct inode *inode, |
| handle_t *handle, |
| struct ocfs2_xattr_value_buf *vb, |
| const void *value, |
| int value_len) |
| { |
| int ret = 0, i, cp_len; |
| u16 blocksize = inode->i_sb->s_blocksize; |
| u32 p_cluster, num_clusters; |
| u32 cpos = 0, bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); |
| u32 clusters = ocfs2_clusters_for_bytes(inode->i_sb, value_len); |
| u64 blkno; |
| struct buffer_head *bh = NULL; |
| unsigned int ext_flags; |
| struct ocfs2_xattr_value_root *xv = vb->vb_xv; |
| |
| BUG_ON(clusters > le32_to_cpu(xv->xr_clusters)); |
| |
| while (cpos < clusters) { |
| ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster, |
| &num_clusters, &xv->xr_list, |
| &ext_flags); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| BUG_ON(ext_flags & OCFS2_EXT_REFCOUNTED); |
| |
| blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); |
| |
| for (i = 0; i < num_clusters * bpc; i++, blkno++) { |
| ret = ocfs2_read_block(INODE_CACHE(inode), blkno, |
| &bh, NULL); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| ret = ocfs2_journal_access(handle, |
| INODE_CACHE(inode), |
| bh, |
| OCFS2_JOURNAL_ACCESS_WRITE); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| cp_len = value_len > blocksize ? blocksize : value_len; |
| memcpy(bh->b_data, value, cp_len); |
| value_len -= cp_len; |
| value += cp_len; |
| if (cp_len < blocksize) |
| memset(bh->b_data + cp_len, 0, |
| blocksize - cp_len); |
| |
| ocfs2_journal_dirty(handle, bh); |
| brelse(bh); |
| bh = NULL; |
| |
| /* |
| * XXX: do we need to empty all the following |
| * blocks in this cluster? |
| */ |
| if (!value_len) |
| break; |
| } |
| cpos += num_clusters; |
| } |
| out: |
| brelse(bh); |
| |
| return ret; |
| } |
| |
| static int ocfs2_xa_check_space_helper(int needed_space, int free_start, |
| int num_entries) |
| { |
| int free_space; |
| |
| if (!needed_space) |
| return 0; |
| |
| free_space = free_start - |
| sizeof(struct ocfs2_xattr_header) - |
| (num_entries * sizeof(struct ocfs2_xattr_entry)) - |
| OCFS2_XATTR_HEADER_GAP; |
| if (free_space < 0) |
| return -EIO; |
| if (free_space < needed_space) |
| return -ENOSPC; |
| |
| return 0; |
| } |
| |
| static int ocfs2_xa_journal_access(handle_t *handle, struct ocfs2_xa_loc *loc, |
| int type) |
| { |
| return loc->xl_ops->xlo_journal_access(handle, loc, type); |
| } |
| |
| static void ocfs2_xa_journal_dirty(handle_t *handle, struct ocfs2_xa_loc *loc) |
| { |
| loc->xl_ops->xlo_journal_dirty(handle, loc); |
| } |
| |
| /* Give a pointer into the storage for the given offset */ |
| static void *ocfs2_xa_offset_pointer(struct ocfs2_xa_loc *loc, int offset) |
| { |
| BUG_ON(offset >= loc->xl_size); |
| return loc->xl_ops->xlo_offset_pointer(loc, offset); |
| } |
| |
| /* |
| * 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); |
| } |
| |
| /* |
| * Find lowest offset to a name+value pair. This is the start of our |
| * downward-growing free space. |
| */ |
| static int ocfs2_xa_get_free_start(struct ocfs2_xa_loc *loc) |
| { |
| return loc->xl_ops->xlo_get_free_start(loc); |
| } |
| |
| /* Can we reuse loc->xl_entry for xi? */ |
| static int ocfs2_xa_can_reuse_entry(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi) |
| { |
| return loc->xl_ops->xlo_can_reuse(loc, xi); |
| } |
| |
| /* How much free space is needed to set the new value */ |
| static int ocfs2_xa_check_space(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi) |
| { |
| return loc->xl_ops->xlo_check_space(loc, xi); |
| } |
| |
| static void ocfs2_xa_add_entry(struct ocfs2_xa_loc *loc, u32 name_hash) |
| { |
| loc->xl_ops->xlo_add_entry(loc, name_hash); |
| loc->xl_entry->xe_name_hash = cpu_to_le32(name_hash); |
| /* |
| * We can't leave the new entry's xe_name_offset at zero or |
| * add_namevalue() will go nuts. We set it to the size of our |
| * storage so that it can never be less than any other entry. |
| */ |
| loc->xl_entry->xe_name_offset = cpu_to_le16(loc->xl_size); |
| } |
| |
| static void ocfs2_xa_add_namevalue(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi) |
| { |
| int size = namevalue_size_xi(xi); |
| int nameval_offset; |
| char *nameval_buf; |
| |
| loc->xl_ops->xlo_add_namevalue(loc, size); |
| loc->xl_entry->xe_value_size = cpu_to_le64(xi->xi_value_len); |
| loc->xl_entry->xe_name_len = xi->xi_name_len; |
| ocfs2_xattr_set_type(loc->xl_entry, xi->xi_name_index); |
| ocfs2_xattr_set_local(loc->xl_entry, |
| xi->xi_value_len <= OCFS2_XATTR_INLINE_SIZE); |
| |
| nameval_offset = le16_to_cpu(loc->xl_entry->xe_name_offset); |
| nameval_buf = ocfs2_xa_offset_pointer(loc, nameval_offset); |
| memset(nameval_buf, 0, size); |
| memcpy(nameval_buf, xi->xi_name, xi->xi_name_len); |
| } |
| |
| static void ocfs2_xa_fill_value_buf(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_value_buf *vb) |
| { |
| int nameval_offset = le16_to_cpu(loc->xl_entry->xe_name_offset); |
| int name_size = OCFS2_XATTR_SIZE(loc->xl_entry->xe_name_len); |
| |
| /* Value bufs are for value trees */ |
| BUG_ON(ocfs2_xattr_is_local(loc->xl_entry)); |
| BUG_ON(namevalue_size_xe(loc->xl_entry) != |
| (name_size + OCFS2_XATTR_ROOT_SIZE)); |
| |
| loc->xl_ops->xlo_fill_value_buf(loc, vb); |
| vb->vb_xv = |
| (struct ocfs2_xattr_value_root *)ocfs2_xa_offset_pointer(loc, |
| nameval_offset + |
| name_size); |
| } |
| |
| static int ocfs2_xa_block_journal_access(handle_t *handle, |
| struct ocfs2_xa_loc *loc, int type) |
| { |
| struct buffer_head *bh = loc->xl_storage; |
| ocfs2_journal_access_func access; |
| |
| if (loc->xl_size == (bh->b_size - |
| offsetof(struct ocfs2_xattr_block, |
| xb_attrs.xb_header))) |
| access = ocfs2_journal_access_xb; |
| else |
| access = ocfs2_journal_access_di; |
| return access(handle, INODE_CACHE(loc->xl_inode), bh, type); |
| } |
| |
| static void ocfs2_xa_block_journal_dirty(handle_t *handle, |
| struct ocfs2_xa_loc *loc) |
| { |
| struct buffer_head *bh = loc->xl_storage; |
| |
| ocfs2_journal_dirty(handle, bh); |
| } |
| |
| static void *ocfs2_xa_block_offset_pointer(struct ocfs2_xa_loc *loc, |
| int offset) |
| { |
| return (char *)loc->xl_header + offset; |
| } |
| |
| static int ocfs2_xa_block_can_reuse(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi) |
| { |
| /* |
| * Block storage is strict. If the sizes aren't exact, we will |
| * remove the old one and reinsert the new. |
| */ |
| return namevalue_size_xe(loc->xl_entry) == |
| namevalue_size_xi(xi); |
| } |
| |
| static int ocfs2_xa_block_get_free_start(struct ocfs2_xa_loc *loc) |
| { |
| struct ocfs2_xattr_header *xh = loc->xl_header; |
| int i, count = le16_to_cpu(xh->xh_count); |
| int offset, free_start = loc->xl_size; |
| |
| for (i = 0; i < count; i++) { |
| offset = le16_to_cpu(xh->xh_entries[i].xe_name_offset); |
| if (offset < free_start) |
| free_start = offset; |
| } |
| |
| return free_start; |
| } |
| |
| static int ocfs2_xa_block_check_space(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi) |
| { |
| int count = le16_to_cpu(loc->xl_header->xh_count); |
| int free_start = ocfs2_xa_get_free_start(loc); |
| int needed_space = ocfs2_xi_entry_usage(xi); |
| |
| /* |
| * Block storage will reclaim the original entry before inserting |
| * the new value, so we only need the difference. If the new |
| * entry is smaller than the old one, we don't need anything. |
| */ |
| if (loc->xl_entry) { |
| /* Don't need space if we're reusing! */ |
| if (ocfs2_xa_can_reuse_entry(loc, xi)) |
| needed_space = 0; |
| else |
| needed_space -= ocfs2_xe_entry_usage(loc->xl_entry); |
| } |
| if (needed_space < 0) |
| needed_space = 0; |
| return ocfs2_xa_check_space_helper(needed_space, free_start, count); |
| } |
| |
| /* |
| * 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; |
| int count = le16_to_cpu(xh->xh_count); |
| |
| namevalue_offset = le16_to_cpu(entry->xe_name_offset); |
| namevalue_size = namevalue_size_xe(entry); |
| first_namevalue_offset = ocfs2_xa_get_free_start(loc); |
| |
| /* 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. |
| */ |
| } |
| |
| static void ocfs2_xa_block_add_entry(struct ocfs2_xa_loc *loc, u32 name_hash) |
| { |
| int count = le16_to_cpu(loc->xl_header->xh_count); |
| loc->xl_entry = &(loc->xl_header->xh_entries[count]); |
| le16_add_cpu(&loc->xl_header->xh_count, 1); |
| memset(loc->xl_entry, 0, sizeof(struct ocfs2_xattr_entry)); |
| } |
| |
| static void ocfs2_xa_block_add_namevalue(struct ocfs2_xa_loc *loc, int size) |
| { |
| int free_start = ocfs2_xa_get_free_start(loc); |
| |
| loc->xl_entry->xe_name_offset = cpu_to_le16(free_start - size); |
| } |
| |
| static void ocfs2_xa_block_fill_value_buf(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_value_buf *vb) |
| { |
| struct buffer_head *bh = loc->xl_storage; |
| |
| if (loc->xl_size == (bh->b_size - |
| offsetof(struct ocfs2_xattr_block, |
| xb_attrs.xb_header))) |
| vb->vb_access = ocfs2_journal_access_xb; |
| else |
| vb->vb_access = ocfs2_journal_access_di; |
| vb->vb_bh = bh; |
| } |
| |
| /* |
| * 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_journal_access = ocfs2_xa_block_journal_access, |
| .xlo_journal_dirty = ocfs2_xa_block_journal_dirty, |
| .xlo_offset_pointer = ocfs2_xa_block_offset_pointer, |
| .xlo_check_space = ocfs2_xa_block_check_space, |
| .xlo_can_reuse = ocfs2_xa_block_can_reuse, |
| .xlo_get_free_start = ocfs2_xa_block_get_free_start, |
| .xlo_wipe_namevalue = ocfs2_xa_block_wipe_namevalue, |
| .xlo_add_entry = ocfs2_xa_block_add_entry, |
| .xlo_add_namevalue = ocfs2_xa_block_add_namevalue, |
| .xlo_fill_value_buf = ocfs2_xa_block_fill_value_buf, |
| }; |
| |
| static int ocfs2_xa_bucket_journal_access(handle_t *handle, |
| struct ocfs2_xa_loc *loc, int type) |
| { |
| struct ocfs2_xattr_bucket *bucket = loc->xl_storage; |
| |
| return ocfs2_xattr_bucket_journal_access(handle, bucket, type); |
| } |
| |
| static void ocfs2_xa_bucket_journal_dirty(handle_t *handle, |
| struct ocfs2_xa_loc *loc) |
| { |
| struct ocfs2_xattr_bucket *bucket = loc->xl_storage; |
| |
| ocfs2_xattr_bucket_journal_dirty(handle, bucket); |
| } |
| |
| 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; |
| |
| /* The header is at the front of the bucket */ |
| block = offset >> loc->xl_inode->i_sb->s_blocksize_bits; |
| block_offset = offset % loc->xl_inode->i_sb->s_blocksize; |
| |
| return bucket_block(bucket, block) + block_offset; |
| } |
| |
| static int ocfs2_xa_bucket_can_reuse(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi) |
| { |
| return namevalue_size_xe(loc->xl_entry) >= |
| namevalue_size_xi(xi); |
| } |
| |
| static int ocfs2_xa_bucket_get_free_start(struct ocfs2_xa_loc *loc) |
| { |
| struct ocfs2_xattr_bucket *bucket = loc->xl_storage; |
| return le16_to_cpu(bucket_xh(bucket)->xh_free_start); |
| } |
| |
| static int ocfs2_bucket_align_free_start(struct super_block *sb, |
| int free_start, int size) |
| { |
| /* |
| * We need to make sure that the name+value pair fits within |
| * one block. |
| */ |
| if (((free_start - size) >> sb->s_blocksize_bits) != |
| ((free_start - 1) >> sb->s_blocksize_bits)) |
| free_start -= free_start % sb->s_blocksize; |
| |
| return free_start; |
| } |
| |
| static int ocfs2_xa_bucket_check_space(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi) |
| { |
| int rc; |
| int count = le16_to_cpu(loc->xl_header->xh_count); |
| int free_start = ocfs2_xa_get_free_start(loc); |
| int needed_space = ocfs2_xi_entry_usage(xi); |
| int size = namevalue_size_xi(xi); |
| struct super_block *sb = loc->xl_inode->i_sb; |
| |
| /* |
| * Bucket storage does not reclaim name+value pairs it cannot |
| * reuse. They live as holes until the bucket fills, and then |
| * the bucket is defragmented. However, the bucket can reclaim |
| * the ocfs2_xattr_entry. |
| */ |
| if (loc->xl_entry) { |
| /* Don't need space if we're reusing! */ |
| if (ocfs2_xa_can_reuse_entry(loc, xi)) |
| needed_space = 0; |
| else |
| needed_space -= sizeof(struct ocfs2_xattr_entry); |
| } |
| BUG_ON(needed_space < 0); |
| |
| if (free_start < size) { |
| if (needed_space) |
| return -ENOSPC; |
| } else { |
| /* |
| * First we check if it would fit in the first place. |
| * Below, we align the free start to a block. This may |
| * slide us below the minimum gap. By checking unaligned |
| * first, we avoid that error. |
| */ |
| rc = ocfs2_xa_check_space_helper(needed_space, free_start, |
| count); |
| if (rc) |
| return rc; |
| free_start = ocfs2_bucket_align_free_start(sb, free_start, |
| size); |
| } |
| return ocfs2_xa_check_space_helper(needed_space, free_start, count); |
| } |
| |
| static void ocfs2_xa_bucket_wipe_namevalue(struct ocfs2_xa_loc *loc) |
| { |
| le16_add_cpu(&loc->xl_header->xh_name_value_len, |
| -namevalue_size_xe(loc->xl_entry)); |
| } |
| |
| static void ocfs2_xa_bucket_add_entry(struct ocfs2_xa_loc *loc, u32 name_hash) |
| { |
| struct ocfs2_xattr_header *xh = loc->xl_header; |
| int count = le16_to_cpu(xh->xh_count); |
| int low = 0, high = count - 1, tmp; |
| struct ocfs2_xattr_entry *tmp_xe; |
| |
| /* |
| * We keep buckets sorted by name_hash, so we need to find |
| * our insert place. |
| */ |
| while (low <= high && count) { |
| tmp = (low + high) / 2; |
| tmp_xe = &xh->xh_entries[tmp]; |
| |
| if (name_hash > le32_to_cpu(tmp_xe->xe_name_hash)) |
| low = tmp + 1; |
| else if (name_hash < le32_to_cpu(tmp_xe->xe_name_hash)) |
| high = tmp - 1; |
| else { |
| low = tmp; |
| break; |
| } |
| } |
| |
| if (low != count) |
| memmove(&xh->xh_entries[low + 1], |
| &xh->xh_entries[low], |
| ((count - low) * sizeof(struct ocfs2_xattr_entry))); |
| |
| le16_add_cpu(&xh->xh_count, 1); |
| loc->xl_entry = &xh->xh_entries[low]; |
| memset(loc->xl_entry, 0, sizeof(struct ocfs2_xattr_entry)); |
| } |
| |
| static void ocfs2_xa_bucket_add_namevalue(struct ocfs2_xa_loc *loc, int size) |
| { |
| int free_start = ocfs2_xa_get_free_start(loc); |
| struct ocfs2_xattr_header *xh = loc->xl_header; |
| struct super_block *sb = loc->xl_inode->i_sb; |
| int nameval_offset; |
| |
| free_start = ocfs2_bucket_align_free_start(sb, free_start, size); |
| nameval_offset = free_start - size; |
| loc->xl_entry->xe_name_offset = cpu_to_le16(nameval_offset); |
| xh->xh_free_start = cpu_to_le16(nameval_offset); |
| le16_add_cpu(&xh->xh_name_value_len, size); |
| |
| } |
| |
| static void ocfs2_xa_bucket_fill_value_buf(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_value_buf *vb) |
| { |
| struct ocfs2_xattr_bucket *bucket = loc->xl_storage; |
| struct super_block *sb = loc->xl_inode->i_sb; |
| int nameval_offset = le16_to_cpu(loc->xl_entry->xe_name_offset); |
| int size = namevalue_size_xe(loc->xl_entry); |
| int block_offset = nameval_offset >> sb->s_blocksize_bits; |
| |
| /* Values are not allowed to straddle block boundaries */ |
| BUG_ON(block_offset != |
| ((nameval_offset + size - 1) >> sb->s_blocksize_bits)); |
| /* We expect the bucket to be filled in */ |
| BUG_ON(!bucket->bu_bhs[block_offset]); |
| |
| vb->vb_access = ocfs2_journal_access; |
| vb->vb_bh = bucket->bu_bhs[block_offset]; |
| } |
| |
| /* Operations for xattrs stored in buckets. */ |
| static const struct ocfs2_xa_loc_operations ocfs2_xa_bucket_loc_ops = { |
| .xlo_journal_access = ocfs2_xa_bucket_journal_access, |
| .xlo_journal_dirty = ocfs2_xa_bucket_journal_dirty, |
| .xlo_offset_pointer = ocfs2_xa_bucket_offset_pointer, |
| .xlo_check_space = ocfs2_xa_bucket_check_space, |
| .xlo_can_reuse = ocfs2_xa_bucket_can_reuse, |
| .xlo_get_free_start = ocfs2_xa_bucket_get_free_start, |
| .xlo_wipe_namevalue = ocfs2_xa_bucket_wipe_namevalue, |
| .xlo_add_entry = ocfs2_xa_bucket_add_entry, |
| .xlo_add_namevalue = ocfs2_xa_bucket_add_namevalue, |
| .xlo_fill_value_buf = ocfs2_xa_bucket_fill_value_buf, |
| }; |
| |
| static unsigned int ocfs2_xa_value_clusters(struct ocfs2_xa_loc *loc) |
| { |
| struct ocfs2_xattr_value_buf vb; |
| |
| if (ocfs2_xattr_is_local(loc->xl_entry)) |
| return 0; |
| |
| ocfs2_xa_fill_value_buf(loc, &vb); |
| return le32_to_cpu(vb.vb_xv->xr_clusters); |
| } |
| |
| static int ocfs2_xa_value_truncate(struct ocfs2_xa_loc *loc, u64 bytes, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int trunc_rc, access_rc; |
| struct ocfs2_xattr_value_buf vb; |
| |
| ocfs2_xa_fill_value_buf(loc, &vb); |
| trunc_rc = ocfs2_xattr_value_truncate(loc->xl_inode, &vb, bytes, |
| ctxt); |
| |
| /* |
| * The caller of ocfs2_xa_value_truncate() has already called |
| * ocfs2_xa_journal_access on the loc. However, The truncate code |
| * calls ocfs2_extend_trans(). This may commit the previous |
| * transaction and open a new one. If this is a bucket, truncate |
| * could leave only vb->vb_bh set up for journaling. Meanwhile, |
| * the caller is expecting to dirty the entire bucket. So we must |
| * reset the journal work. We do this even if truncate has failed, |
| * as it could have failed after committing the extend. |
| */ |
| access_rc = ocfs2_xa_journal_access(ctxt->handle, loc, |
| OCFS2_JOURNAL_ACCESS_WRITE); |
| |
| /* Errors in truncate take precedence */ |
| return trunc_rc ? trunc_rc : access_rc; |
| } |
| |
| static void ocfs2_xa_remove_entry(struct ocfs2_xa_loc *loc) |
| { |
| int index, count; |
| struct ocfs2_xattr_header *xh = loc->xl_header; |
| struct ocfs2_xattr_entry *entry = loc->xl_entry; |
| |
| ocfs2_xa_wipe_namevalue(loc); |
| loc->xl_entry = NULL; |
| |
| le16_add_cpu(&xh->xh_count, -1); |
| count = le16_to_cpu(xh->xh_count); |
| |
| /* |
| * Only zero out the entry if there are more remaining. This is |
| * important for an empty bucket, as it keeps track of the |
| * bucket's hash value. It doesn't hurt empty block storage. |
| */ |
| if (count) { |
| index = ((char *)entry - (char *)&xh->xh_entries) / |
| sizeof(struct ocfs2_xattr_entry); |
| memmove(&xh->xh_entries[index], &xh->xh_entries[index + 1], |
| (count - index) * sizeof(struct ocfs2_xattr_entry)); |
| memset(&xh->xh_entries[count], 0, |
| sizeof(struct ocfs2_xattr_entry)); |
| } |
| } |
| |
| /* |
| * If we have a problem adjusting the size of an external value during |
| * ocfs2_xa_prepare_entry() or ocfs2_xa_remove(), we may have an xattr |
| * in an intermediate state. For example, the value may be partially |
| * truncated. |
| * |
| * If the value tree hasn't changed, the extend/truncate went nowhere. |
| * We have nothing to do. The caller can treat it as a straight error. |
| * |
| * If the value tree got partially truncated, we now have a corrupted |
| * extended attribute. We're going to wipe its entry and leak the |
| * clusters. Better to leak some storage than leave a corrupt entry. |
| * |
| * If the value tree grew, it obviously didn't grow enough for the |
| * new entry. We're not going to try and reclaim those clusters either. |
| * If there was already an external value there (orig_clusters != 0), |
| * the new clusters are attached safely and we can just leave the old |
| * value in place. If there was no external value there, we remove |
| * the entry. |
| * |
| * This way, the xattr block we store in the journal will be consistent. |
| * If the size change broke because of the journal, no changes will hit |
| * disk anyway. |
| */ |
| static void ocfs2_xa_cleanup_value_truncate(struct ocfs2_xa_loc *loc, |
| const char *what, |
| unsigned int orig_clusters) |
| { |
| unsigned int new_clusters = ocfs2_xa_value_clusters(loc); |
| char *nameval_buf = ocfs2_xa_offset_pointer(loc, |
| le16_to_cpu(loc->xl_entry->xe_name_offset)); |
| |
| if (new_clusters < orig_clusters) { |
| mlog(ML_ERROR, |
| "Partial truncate while %s xattr %.*s. Leaking " |
| "%u clusters and removing the entry\n", |
| what, loc->xl_entry->xe_name_len, nameval_buf, |
| orig_clusters - new_clusters); |
| ocfs2_xa_remove_entry(loc); |
| } else if (!orig_clusters) { |
| mlog(ML_ERROR, |
| "Unable to allocate an external value for xattr " |
| "%.*s safely. Leaking %u clusters and removing the " |
| "entry\n", |
| loc->xl_entry->xe_name_len, nameval_buf, |
| new_clusters - orig_clusters); |
| ocfs2_xa_remove_entry(loc); |
| } else if (new_clusters > orig_clusters) |
| mlog(ML_ERROR, |
| "Unable to grow xattr %.*s safely. %u new clusters " |
| "have been added, but the value will not be " |
| "modified\n", |
| loc->xl_entry->xe_name_len, nameval_buf, |
| new_clusters - orig_clusters); |
| } |
| |
| static int ocfs2_xa_remove(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int rc = 0; |
| unsigned int orig_clusters; |
| |
| if (!ocfs2_xattr_is_local(loc->xl_entry)) { |
| orig_clusters = ocfs2_xa_value_clusters(loc); |
| rc = ocfs2_xa_value_truncate(loc, 0, ctxt); |
| if (rc) { |
| mlog_errno(rc); |
| /* |
| * Since this is remove, we can return 0 if |
| * ocfs2_xa_cleanup_value_truncate() is going to |
| * wipe the entry anyway. So we check the |
| * cluster count as well. |
| */ |
| if (orig_clusters != ocfs2_xa_value_clusters(loc)) |
| rc = 0; |
| ocfs2_xa_cleanup_value_truncate(loc, "removing", |
| orig_clusters); |
| if (rc) |
| goto out; |
| } |
| } |
| |
| ocfs2_xa_remove_entry(loc); |
| |
| out: |
| return rc; |
| } |
| |
| static void ocfs2_xa_install_value_root(struct ocfs2_xa_loc *loc) |
| { |
| int name_size = OCFS2_XATTR_SIZE(loc->xl_entry->xe_name_len); |
| char *nameval_buf; |
| |
| nameval_buf = ocfs2_xa_offset_pointer(loc, |
| le16_to_cpu(loc->xl_entry->xe_name_offset)); |
| memcpy(nameval_buf + name_size, &def_xv, OCFS2_XATTR_ROOT_SIZE); |
| } |
| |
| /* |
| * Take an existing entry and make it ready for the new value. This |
| * won't allocate space, but it may free space. It should be ready for |
| * ocfs2_xa_prepare_entry() to finish the work. |
| */ |
| static int ocfs2_xa_reuse_entry(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int rc = 0; |
| int name_size = OCFS2_XATTR_SIZE(xi->xi_name_len); |
| unsigned int orig_clusters; |
| char *nameval_buf; |
| int xe_local = ocfs2_xattr_is_local(loc->xl_entry); |
| int xi_local = xi->xi_value_len <= OCFS2_XATTR_INLINE_SIZE; |
| |
| BUG_ON(OCFS2_XATTR_SIZE(loc->xl_entry->xe_name_len) != |
| name_size); |
| |
| nameval_buf = ocfs2_xa_offset_pointer(loc, |
| le16_to_cpu(loc->xl_entry->xe_name_offset)); |
| if (xe_local) { |
| memset(nameval_buf + name_size, 0, |
| namevalue_size_xe(loc->xl_entry) - name_size); |
| if (!xi_local) |
| ocfs2_xa_install_value_root(loc); |
| } else { |
| orig_clusters = ocfs2_xa_value_clusters(loc); |
| if (xi_local) { |
| rc = ocfs2_xa_value_truncate(loc, 0, ctxt); |
| if (rc < 0) |
| mlog_errno(rc); |
| else |
| memset(nameval_buf + name_size, 0, |
| namevalue_size_xe(loc->xl_entry) - |
| name_size); |
| } else if (le64_to_cpu(loc->xl_entry->xe_value_size) > |
| xi->xi_value_len) { |
| rc = ocfs2_xa_value_truncate(loc, xi->xi_value_len, |
| ctxt); |
| if (rc < 0) |
| mlog_errno(rc); |
| } |
| |
| if (rc) { |
| ocfs2_xa_cleanup_value_truncate(loc, "reusing", |
| orig_clusters); |
| goto out; |
| } |
| } |
| |
| loc->xl_entry->xe_value_size = cpu_to_le64(xi->xi_value_len); |
| ocfs2_xattr_set_local(loc->xl_entry, xi_local); |
| |
| out: |
| return rc; |
| } |
| |
| /* |
| * Prepares loc->xl_entry to receive the new xattr. This includes |
| * properly setting up the name+value pair region. If loc->xl_entry |
| * already exists, it will take care of modifying it appropriately. |
| * |
| * Note that this modifies the data. You did journal_access already, |
| * right? |
| */ |
| static int ocfs2_xa_prepare_entry(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi, |
| u32 name_hash, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int rc = 0; |
| unsigned int orig_clusters; |
| __le64 orig_value_size = 0; |
| |
| rc = ocfs2_xa_check_space(loc, xi); |
| if (rc) |
| goto out; |
| |
| if (loc->xl_entry) { |
| if (ocfs2_xa_can_reuse_entry(loc, xi)) { |
| orig_value_size = loc->xl_entry->xe_value_size; |
| rc = ocfs2_xa_reuse_entry(loc, xi, ctxt); |
| if (rc) |
| goto out; |
| goto alloc_value; |
| } |
| |
| if (!ocfs2_xattr_is_local(loc->xl_entry)) { |
| orig_clusters = ocfs2_xa_value_clusters(loc); |
| rc = ocfs2_xa_value_truncate(loc, 0, ctxt); |
| if (rc) { |
| mlog_errno(rc); |
| ocfs2_xa_cleanup_value_truncate(loc, |
| "overwriting", |
| orig_clusters); |
| goto out; |
| } |
| } |
| ocfs2_xa_wipe_namevalue(loc); |
| } else |
| ocfs2_xa_add_entry(loc, name_hash); |
| |
| /* |
| * If we get here, we have a blank entry. Fill it. We grow our |
| * name+value pair back from the end. |
| */ |
| ocfs2_xa_add_namevalue(loc, xi); |
| if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) |
| ocfs2_xa_install_value_root(loc); |
| |
| alloc_value: |
| if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) { |
| orig_clusters = ocfs2_xa_value_clusters(loc); |
| rc = ocfs2_xa_value_truncate(loc, xi->xi_value_len, ctxt); |
| if (rc < 0) { |
| ctxt->set_abort = 1; |
| ocfs2_xa_cleanup_value_truncate(loc, "growing", |
| orig_clusters); |
| /* |
| * If we were growing an existing value, |
| * ocfs2_xa_cleanup_value_truncate() won't remove |
| * the entry. We need to restore the original value |
| * size. |
| */ |
| if (loc->xl_entry) { |
| BUG_ON(!orig_value_size); |
| loc->xl_entry->xe_value_size = orig_value_size; |
| } |
| mlog_errno(rc); |
| } |
| } |
| |
| out: |
| return rc; |
| } |
| |
| /* |
| * Store the value portion of the name+value pair. This will skip |
| * values that are stored externally. Their tree roots were set up |
| * by ocfs2_xa_prepare_entry(). |
| */ |
| static int ocfs2_xa_store_value(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int rc = 0; |
| int nameval_offset = le16_to_cpu(loc->xl_entry->xe_name_offset); |
| int name_size = OCFS2_XATTR_SIZE(xi->xi_name_len); |
| char *nameval_buf; |
| struct ocfs2_xattr_value_buf vb; |
| |
| nameval_buf = ocfs2_xa_offset_pointer(loc, nameval_offset); |
| if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) { |
| ocfs2_xa_fill_value_buf(loc, &vb); |
| rc = __ocfs2_xattr_set_value_outside(loc->xl_inode, |
| ctxt->handle, &vb, |
| xi->xi_value, |
| xi->xi_value_len); |
| } else |
| memcpy(nameval_buf + name_size, xi->xi_value, xi->xi_value_len); |
| |
| return rc; |
| } |
| |
| static int ocfs2_xa_set(struct ocfs2_xa_loc *loc, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int ret; |
| u32 name_hash = ocfs2_xattr_name_hash(loc->xl_inode, xi->xi_name, |
| xi->xi_name_len); |
| |
| ret = ocfs2_xa_journal_access(ctxt->handle, loc, |
| OCFS2_JOURNAL_ACCESS_WRITE); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| /* |
| * From here on out, everything is going to modify the buffer a |
| * little. Errors are going to leave the xattr header in a |
| * sane state. Thus, even with errors we dirty the sucker. |
| */ |
| |
| /* Don't worry, we are never called with !xi_value and !xl_entry */ |
| if (!xi->xi_value) { |
| ret = ocfs2_xa_remove(loc, ctxt); |
| goto out_dirty; |
| } |
| |
| ret = ocfs2_xa_prepare_entry(loc, xi, name_hash, ctxt); |
| if (ret) { |
| if (ret != -ENOSPC) |
| mlog_errno(ret); |
| goto out_dirty; |
| } |
| |
| ret = ocfs2_xa_store_value(loc, xi, ctxt); |
| if (ret) |
| mlog_errno(ret); |
| |
| out_dirty: |
| ocfs2_xa_journal_dirty(ctxt->handle, loc); |
| |
| out: |
| return ret; |
| } |
| |
| 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; |
| |
| BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_XATTR_FL)); |
| |
| loc->xl_inode = inode; |
| loc->xl_ops = &ocfs2_xa_block_loc_ops; |
| loc->xl_storage = bh; |
| loc->xl_entry = entry; |
| loc->xl_size = le16_to_cpu(di->i_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 inode *inode, |
| 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_inode = inode; |
| 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_inode = bucket->bu_inode; |
| 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; |
| } |
| |
| /* |
| * In xattr remove, if it is stored outside and refcounted, we may have |
| * the chance to split the refcount tree. So need the allocators. |
| */ |
| static int ocfs2_lock_xattr_remove_allocators(struct inode *inode, |
| struct ocfs2_xattr_value_root *xv, |
| struct ocfs2_caching_info *ref_ci, |
| struct buffer_head *ref_root_bh, |
| struct ocfs2_alloc_context **meta_ac, |
| int *ref_credits) |
| { |
| int ret, meta_add = 0; |
| u32 p_cluster, num_clusters; |
| unsigned int ext_flags; |
| |
| *ref_credits = 0; |
| ret = ocfs2_xattr_get_clusters(inode, 0, &p_cluster, |
| &num_clusters, |
| &xv->xr_list, |
| &ext_flags); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| if (!(ext_flags & OCFS2_EXT_REFCOUNTED)) |
| goto out; |
| |
| ret = ocfs2_refcounted_xattr_delete_need(inode, ref_ci, |
| ref_root_bh, xv, |
| &meta_add, ref_credits); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| ret = ocfs2_reserve_new_metadata_blocks(OCFS2_SB(inode->i_sb), |
| meta_add, meta_ac); |
| if (ret) |
| mlog_errno(ret); |
| |
| out: |
| return ret; |
| } |
| |
| static int ocfs2_remove_value_outside(struct inode*inode, |
| struct ocfs2_xattr_value_buf *vb, |
| struct ocfs2_xattr_header *header, |
| struct ocfs2_caching_info *ref_ci, |
| struct buffer_head *ref_root_bh) |
| { |
| int ret = 0, i, ref_credits; |
| struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); |
| struct ocfs2_xattr_set_ctxt ctxt = { NULL, NULL, }; |
| void *val; |
| |
| ocfs2_init_dealloc_ctxt(&ctxt.dealloc); |
| |
| for (i = 0; i < le16_to_cpu(header->xh_count); i++) { |
| struct ocfs2_xattr_entry *entry = &header->xh_entries[i]; |
| |
| if (ocfs2_xattr_is_local(entry)) |
| continue; |
| |
| val = (void *)header + |
| le16_to_cpu(entry->xe_name_offset); |
| vb->vb_xv = (struct ocfs2_xattr_value_root *) |
| (val + OCFS2_XATTR_SIZE(entry->xe_name_len)); |
| |
| ret = ocfs2_lock_xattr_remove_allocators(inode, vb->vb_xv, |
| ref_ci, ref_root_bh, |
| &ctxt.meta_ac, |
| &ref_credits); |
| |
| ctxt.handle = ocfs2_start_trans(osb, ref_credits + |
| ocfs2_remove_extent_credits(osb->sb)); |
| if (IS_ERR(ctxt.handle)) { |
| ret = PTR_ERR(ctxt.handle); |
| mlog_errno(ret); |
| break; |
| } |
| |
| ret = ocfs2_xattr_value_truncate(inode, vb, 0, &ctxt); |
| |
| ocfs2_commit_trans(osb, ctxt.handle); |
| if (ctxt.meta_ac) { |
| ocfs2_free_alloc_context(ctxt.meta_ac); |
| ctxt.meta_ac = NULL; |
| } |
| |
| if (ret < 0) { |
| mlog_errno(ret); |
| break; |
| } |
| |
| } |
| |
| if (ctxt.meta_ac) |
| ocfs2_free_alloc_context(ctxt.meta_ac); |
| ocfs2_schedule_truncate_log_flush(osb, 1); |
| ocfs2_run_deallocs(osb, &ctxt.dealloc); |
| return ret; |
| } |
| |
| static int ocfs2_xattr_ibody_remove(struct inode *inode, |
| struct buffer_head *di_bh, |
| struct ocfs2_caching_info *ref_ci, |
| struct buffer_head *ref_root_bh) |
| { |
| |
| struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; |
| struct ocfs2_xattr_header *header; |
| int ret; |
| struct ocfs2_xattr_value_buf vb = { |
| .vb_bh = di_bh, |
| .vb_access = ocfs2_journal_access_di, |
| }; |
| |
| header = (struct ocfs2_xattr_header *) |
| ((void *)di + inode->i_sb->s_blocksize - |
| le16_to_cpu(di->i_xattr_inline_size)); |
| |
| ret = ocfs2_remove_value_outside(inode, &vb, header, |
| ref_ci, ref_root_bh); |
| |
| return ret; |
| } |
| |
| struct ocfs2_rm_xattr_bucket_para { |
| struct ocfs2_caching_info *ref_ci; |
| struct buffer_head *ref_root_bh; |
| }; |
| |
| static int ocfs2_xattr_block_remove(struct inode *inode, |
| struct buffer_head *blk_bh, |
| struct ocfs2_caching_info *ref_ci, |
| struct buffer_head *ref_root_bh) |
| { |
| struct ocfs2_xattr_block *xb; |
| int ret = 0; |
| struct ocfs2_xattr_value_buf vb = { |
| .vb_bh = blk_bh, |
| .vb_access = ocfs2_journal_access_xb, |
| }; |
| struct ocfs2_rm_xattr_bucket_para args = { |
| .ref_ci = ref_ci, |
| .ref_root_bh = ref_root_bh, |
| }; |
| |
| xb = (struct ocfs2_xattr_block *)blk_bh->b_data; |
| if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { |
| struct ocfs2_xattr_header *header = &(xb->xb_attrs.xb_header); |
| ret = ocfs2_remove_value_outside(inode, &vb, header, |
| ref_ci, ref_root_bh); |
| } else |
| ret = ocfs2_iterate_xattr_index_block(inode, |
| blk_bh, |
| ocfs2_rm_xattr_cluster, |
| &args); |
| |
| return ret; |
| } |
| |
| static int ocfs2_xattr_free_block(struct inode *inode, |
| u64 block, |
| struct ocfs2_caching_info *ref_ci, |
| struct buffer_head *ref_root_bh) |
| { |
| struct inode *xb_alloc_inode; |
| struct buffer_head *xb_alloc_bh = NULL; |
| struct buffer_head *blk_bh = NULL; |
| struct ocfs2_xattr_block *xb; |
| struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); |
| handle_t *handle; |
| int ret = 0; |
| u64 blk, bg_blkno; |
| u16 bit; |
| |
| ret = ocfs2_read_xattr_block(inode, block, &blk_bh); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| ret = ocfs2_xattr_block_remove(inode, blk_bh, ref_ci, ref_root_bh); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| xb = (struct ocfs2_xattr_block *)blk_bh->b_data; |
| blk = le64_to_cpu(xb->xb_blkno); |
| bit = le16_to_cpu(xb->xb_suballoc_bit); |
| if (xb->xb_suballoc_loc) |
| bg_blkno = le64_to_cpu(xb->xb_suballoc_loc); |
| else |
| bg_blkno = ocfs2_which_suballoc_group(blk, bit); |
| |
| xb_alloc_inode = ocfs2_get_system_file_inode(osb, |
| EXTENT_ALLOC_SYSTEM_INODE, |
| le16_to_cpu(xb->xb_suballoc_slot)); |
| if (!xb_alloc_inode) { |
| ret = -ENOMEM; |
| mlog_errno(ret); |
| goto out; |
| } |
| inode_lock(xb_alloc_inode); |
| |
| ret = ocfs2_inode_lock(xb_alloc_inode, &xb_alloc_bh, 1); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto out_mutex; |
| } |
| |
| handle = ocfs2_start_trans(osb, OCFS2_SUBALLOC_FREE); |
| if (IS_ERR(handle)) { |
| ret = PTR_ERR(handle); |
| mlog_errno(ret); |
| goto out_unlock; |
| } |
| |
| ret = ocfs2_free_suballoc_bits(handle, xb_alloc_inode, xb_alloc_bh, |
| bit, bg_blkno, 1); |
| if (ret < 0) |
| mlog_errno(ret); |
| |
| ocfs2_commit_trans(osb, handle); |
| out_unlock: |
| ocfs2_inode_unlock(xb_alloc_inode, 1); |
| brelse(xb_alloc_bh); |
| out_mutex: |
| inode_unlock(xb_alloc_inode); |
| iput(xb_alloc_inode); |
| out: |
| brelse(blk_bh); |
| return ret; |
| } |
| |
| /* |
| * ocfs2_xattr_remove() |
| * |
| * Free extended attribute resources associated with this inode. |
| */ |
| int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh) |
| { |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; |
| struct ocfs2_refcount_tree *ref_tree = NULL; |
| struct buffer_head *ref_root_bh = NULL; |
| struct ocfs2_caching_info *ref_ci = NULL; |
| handle_t *handle; |
| int ret; |
| |
| if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb))) |
| return 0; |
| |
| if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) |
| return 0; |
| |
| if (ocfs2_is_refcount_inode(inode)) { |
| ret = ocfs2_lock_refcount_tree(OCFS2_SB(inode->i_sb), |
| le64_to_cpu(di->i_refcount_loc), |
| 1, &ref_tree, &ref_root_bh); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| ref_ci = &ref_tree->rf_ci; |
| |
| } |
| |
| if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { |
| ret = ocfs2_xattr_ibody_remove(inode, di_bh, |
| ref_ci, ref_root_bh); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto out; |
| } |
| } |
| |
| if (di->i_xattr_loc) { |
| ret = ocfs2_xattr_free_block(inode, |
| le64_to_cpu(di->i_xattr_loc), |
| ref_ci, ref_root_bh); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto out; |
| } |
| } |
| |
| handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), |
| OCFS2_INODE_UPDATE_CREDITS); |
| if (IS_ERR(handle)) { |
| ret = PTR_ERR(handle); |
| mlog_errno(ret); |
| goto out; |
| } |
| ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, |
| OCFS2_JOURNAL_ACCESS_WRITE); |
| if (ret) { |
| mlog_errno(ret); |
| goto out_commit; |
| } |
| |
| di->i_xattr_loc = 0; |
| |
| spin_lock(&oi->ip_lock); |
| oi->ip_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL); |
| di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); |
| spin_unlock(&oi->ip_lock); |
| ocfs2_update_inode_fsync_trans(handle, inode, 0); |
| |
| ocfs2_journal_dirty(handle, di_bh); |
| out_commit: |
| ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); |
| out: |
| if (ref_tree) |
| ocfs2_unlock_refcount_tree(OCFS2_SB(inode->i_sb), ref_tree, 1); |
| brelse(ref_root_bh); |
| return ret; |
| } |
| |
| static int ocfs2_xattr_has_space_inline(struct inode *inode, |
| struct ocfs2_dinode *di) |
| { |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| unsigned int xattrsize = OCFS2_SB(inode->i_sb)->s_xattr_inline_size; |
| int free; |
| |
| if (xattrsize < OCFS2_MIN_XATTR_INLINE_SIZE) |
| return 0; |
| |
| if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { |
| struct ocfs2_inline_data *idata = &di->id2.i_data; |
| free = le16_to_cpu(idata->id_count) - le64_to_cpu(di->i_size); |
| } else if (ocfs2_inode_is_fast_symlink(inode)) { |
| free = ocfs2_fast_symlink_chars(inode->i_sb) - |
| le64_to_cpu(di->i_size); |
| } else { |
| struct ocfs2_extent_list *el = &di->id2.i_list; |
| free = (le16_to_cpu(el->l_count) - |
| le16_to_cpu(el->l_next_free_rec)) * |
| sizeof(struct ocfs2_extent_rec); |
| } |
| if (free >= xattrsize) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* |
| * ocfs2_xattr_ibody_find() |
| * |
| * Find extended attribute in inode block and |
| * fill search info into struct ocfs2_xattr_search. |
| */ |
| static int ocfs2_xattr_ibody_find(struct inode *inode, |
| int name_index, |
| const char *name, |
| struct ocfs2_xattr_search *xs) |
| { |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; |
| int ret; |
| int has_space = 0; |
| |
| if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) |
| return 0; |
| |
| if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { |
| down_read(&oi->ip_alloc_sem); |
| has_space = ocfs2_xattr_has_space_inline(inode, di); |
| up_read(&oi->ip_alloc_sem); |
| if (!has_space) |
| return 0; |
| } |
| |
| xs->xattr_bh = xs->inode_bh; |
| xs->end = (void *)di + inode->i_sb->s_blocksize; |
| if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) |
| xs->header = (struct ocfs2_xattr_header *) |
| (xs->end - le16_to_cpu(di->i_xattr_inline_size)); |
| else |
| xs->header = (struct ocfs2_xattr_header *) |
| (xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size); |
| xs->base = (void *)xs->header; |
| xs->here = xs->header->xh_entries; |
| |
| /* Find the named attribute. */ |
| if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { |
| ret = ocfs2_xattr_find_entry(name_index, name, xs); |
| if (ret && ret != -ENODATA) |
| return ret; |
| xs->not_found = ret; |
| } |
| |
| return 0; |
| } |
| |
| static int ocfs2_xattr_ibody_init(struct inode *inode, |
| struct buffer_head *di_bh, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int ret; |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; |
| struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); |
| unsigned int xattrsize = osb->s_xattr_inline_size; |
| |
| if (!ocfs2_xattr_has_space_inline(inode, di)) { |
| ret = -ENOSPC; |
| goto out; |
| } |
| |
| ret = ocfs2_journal_access_di(ctxt->handle, INODE_CACHE(inode), di_bh, |
| OCFS2_JOURNAL_ACCESS_WRITE); |
| if (ret) { |
| mlog_errno(ret); |
| goto out; |
| } |
| |
| /* |
| * Adjust extent record count or inline data size |
| * to reserve space for extended attribute. |
| */ |
| if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { |
| struct ocfs2_inline_data *idata = &di->id2.i_data; |
| le16_add_cpu(&idata->id_count, -xattrsize); |
| } else if (!(ocfs2_inode_is_fast_symlink(inode))) { |
| struct ocfs2_extent_list *el = &di->id2.i_list; |
| le16_add_cpu(&el->l_count, -(xattrsize / |
| sizeof(struct ocfs2_extent_rec))); |
| } |
| di->i_xattr_inline_size = cpu_to_le16(xattrsize); |
| |
| spin_lock(&oi->ip_lock); |
| oi->ip_dyn_features |= OCFS2_INLINE_XATTR_FL|OCFS2_HAS_XATTR_FL; |
| di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); |
| spin_unlock(&oi->ip_lock); |
| |
| ocfs2_journal_dirty(ctxt->handle, di_bh); |
| |
| out: |
| return ret; |
| } |
| |
| /* |
| * ocfs2_xattr_ibody_set() |
| * |
| * Set, replace or remove an extended attribute into inode block. |
| * |
| */ |
| static int ocfs2_xattr_ibody_set(struct inode *inode, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_search *xs, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| int ret; |
| struct ocfs2_inode_info *oi = OCFS2_I(inode); |
| struct ocfs2_xa_loc loc; |
| |
| if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) |
| return -ENOSPC; |
| |
| down_write(&oi->ip_alloc_sem); |
| if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { |
| ret = ocfs2_xattr_ibody_init(inode, xs->inode_bh, ctxt); |
| if (ret) { |
| if (ret != -ENOSPC) |
| mlog_errno(ret); |
| goto out; |
| } |
| } |
| |
| ocfs2_init_dinode_xa_loc(&loc, inode, xs->inode_bh, |
| xs->not_found ? NULL : xs->here); |
| ret = ocfs2_xa_set(&loc, xi, ctxt); |
| if (ret) { |
| if (ret != -ENOSPC) |
| mlog_errno(ret); |
| goto out; |
| } |
| xs->here = loc.xl_entry; |
| |
| out: |
| up_write(&oi->ip_alloc_sem); |
| |
| return ret; |
| } |
| |
| /* |
| * ocfs2_xattr_block_find() |
| * |
| * Find extended attribute in external block and |
| * fill search info into struct ocfs2_xattr_search. |
| */ |
| static int ocfs2_xattr_block_find(struct inode *inode, |
| int name_index, |
| const char *name, |
| struct ocfs2_xattr_search *xs) |
| { |
| struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; |
| struct buffer_head *blk_bh = NULL; |
| struct ocfs2_xattr_block *xb; |
| int ret = 0; |
| |
| if (!di->i_xattr_loc) |
| return ret; |
| |
| ret = ocfs2_read_xattr_block(inode, le64_to_cpu(di->i_xattr_loc), |
| &blk_bh); |
| if (ret < 0) { |
| mlog_errno(ret); |
| return ret; |
| } |
| |
| xs->xattr_bh = blk_bh; |
| xb = (struct ocfs2_xattr_block *)blk_bh->b_data; |
| |
| if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { |
| xs->header = &xb->xb_attrs.xb_header; |
| xs->base = (void *)xs->header; |
| xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; |
| xs->here = xs->header->xh_entries; |
| |
| ret = ocfs2_xattr_find_entry(name_index, name, xs); |
| } else |
| ret = ocfs2_xattr_index_block_find(inode, blk_bh, |
| name_index, |
| name, xs); |
| |
| if (ret && ret != -ENODATA) { |
| xs->xattr_bh = NULL; |
| goto cleanup; |
| } |
| xs->not_found = ret; |
| return 0; |
| cleanup: |
| brelse(blk_bh); |
| |
| return ret; |
| } |
| |
| static int ocfs2_create_xattr_block(struct inode *inode, |
| struct buffer_head *inode_bh, |
| struct ocfs2_xattr_set_ctxt *ctxt, |
| int indexed, |
| struct buffer_head **ret_bh) |
| { |
| int ret; |
| u16 suballoc_bit_start; |
| u32 num_got; |
| u64 suballoc_loc, first_blkno; |
| struct ocfs2_dinode *di = (struct ocfs2_dinode *)inode_bh->b_data; |
| struct buffer_head *new_bh = NULL; |
| struct ocfs2_xattr_block *xblk; |
| |
| ret = ocfs2_journal_access_di(ctxt->handle, INODE_CACHE(inode), |
| inode_bh, OCFS2_JOURNAL_ACCESS_CREATE); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto end; |
| } |
| |
| ret = ocfs2_claim_metadata(ctxt->handle, ctxt->meta_ac, 1, |
| &suballoc_loc, &suballoc_bit_start, |
| &num_got, &first_blkno); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto end; |
| } |
| |
| new_bh = sb_getblk(inode->i_sb, first_blkno); |
| if (!new_bh) { |
| ret = -ENOMEM; |
| mlog_errno(ret); |
| goto end; |
| } |
| |
| ocfs2_set_new_buffer_uptodate(INODE_CACHE(inode), new_bh); |
| |
| ret = ocfs2_journal_access_xb(ctxt->handle, INODE_CACHE(inode), |
| new_bh, |
| OCFS2_JOURNAL_ACCESS_CREATE); |
| if (ret < 0) { |
| mlog_errno(ret); |
| goto end; |
| } |
| |
| /* Initialize ocfs2_xattr_block */ |
| xblk = (struct ocfs2_xattr_block *)new_bh->b_data; |
| memset(xblk, 0, inode->i_sb->s_blocksize); |
| strcpy((void *)xblk, OCFS2_XATTR_BLOCK_SIGNATURE); |
| xblk->xb_suballoc_slot = cpu_to_le16(ctxt->meta_ac->ac_alloc_slot); |
| xblk->xb_suballoc_loc = cpu_to_le64(suballoc_loc); |
| xblk->xb_suballoc_bit = cpu_to_le16(suballoc_bit_start); |
| xblk->xb_fs_generation = |
| cpu_to_le32(OCFS2_SB(inode->i_sb)->fs_generation); |
| xblk->xb_blkno = cpu_to_le64(first_blkno); |
| if (indexed) { |
| struct ocfs2_xattr_tree_root *xr = &xblk->xb_attrs.xb_root; |
| xr->xt_clusters = cpu_to_le32(1); |
| xr->xt_last_eb_blk = 0; |
| xr->xt_list.l_tree_depth = 0; |
| xr->xt_list.l_count = cpu_to_le16( |
| ocfs2_xattr_recs_per_xb(inode->i_sb)); |
| xr->xt_list.l_next_free_rec = cpu_to_le16(1); |
| xblk->xb_flags = cpu_to_le16(OCFS2_XATTR_INDEXED); |
| } |
| ocfs2_journal_dirty(ctxt->handle, new_bh); |
| |
| /* Add it to the inode */ |
| di->i_xattr_loc = cpu_to_le64(first_blkno); |
| |
| spin_lock(&OCFS2_I(inode)->ip_lock); |
| OCFS2_I(inode)->ip_dyn_features |= OCFS2_HAS_XATTR_FL; |
| di->i_dyn_features = cpu_to_le16(OCFS2_I(inode)->ip_dyn_features); |
| spin_unlock(&OCFS2_I(inode)->ip_lock); |
| |
| ocfs2_journal_dirty(ctxt->handle, inode_bh); |
| |
| *ret_bh = new_bh; |
| new_bh = NULL; |
| |
| end: |
| brelse(new_bh); |
| return ret; |
| } |
| |
| /* |
| * ocfs2_xattr_block_set() |
| * |
| * Set, replace or remove an extended attribute into external block. |
| * |
| */ |
| static int ocfs2_xattr_block_set(struct inode *inode, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_search *xs, |
| struct ocfs2_xattr_set_ctxt *ctxt) |
| { |
| struct buffer_head *new_bh = NULL; |
| struct ocfs2_xattr_block *xblk = NULL; |
| int ret; |
| struct ocfs2_xa_loc loc; |
| |
| if (!xs->xattr_bh) { |
| ret = ocfs2_create_xattr_block(inode, xs->inode_bh, ctxt, |
| 0, &new_bh); |
| if (ret) { |
| mlog_errno(ret); |
| goto end; |
| } |
| |
| xs->xattr_bh = new_bh; |
| xblk = (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; |
| xs->header = &xblk->xb_attrs.xb_header; |
| xs->base = (void *)xs->header; |
| xs->end = (void *)xblk + inode->i_sb->s_blocksize; |
| xs->here = xs->header->xh_entries; |
| } else |
| xblk = (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; |
| |
| if (!(le16_to_cpu(xblk->xb_flags) & OCFS2_XATTR_INDEXED)) { |
| ocfs2_init_xattr_block_xa_loc(&loc, inode, xs->xattr_bh, |
| xs->not_found ? NULL : xs->here); |
| |
| ret = ocfs2_xa_set(&loc, xi, ctxt); |
| if (!ret) |
| xs->here = loc.xl_entry; |
| else if ((ret != -ENOSPC) || ctxt->set_abort) |
| goto end; |
| else { |
| ret = ocfs2_xattr_create_index_block(inode, xs, ctxt); |
| if (ret) |
| goto end; |
| } |
| } |
| |
| if (le16_to_cpu(xblk->xb_flags) & OCFS2_XATTR_INDEXED) |
| ret = ocfs2_xattr_set_entry_index_block(inode, xi, xs, ctxt); |
| |
| end: |
| return ret; |
| } |
| |
| /* Check whether the new xattr can be inserted into the inode. */ |
| static int ocfs2_xattr_can_be_in_inode(struct inode *inode, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_search *xs) |
| { |
| struct ocfs2_xattr_entry *last; |
| int free, i; |
| size_t min_offs = xs->end - xs->base; |
| |
| if (!xs->header) |
| return 0; |
| |
| last = xs->header->xh_entries; |
| |
| for (i = 0; i < le16_to_cpu(xs->header->xh_count); i++) { |
| size_t offs = le16_to_cpu(last->xe_name_offset); |
| if (offs < min_offs) |
| min_offs = offs; |
| last += 1; |
| } |
| |
| free = min_offs - ((void *)last - xs->base) - OCFS2_XATTR_HEADER_GAP; |
| if (free < 0) |
| return 0; |
| |
| BUG_ON(!xs->not_found); |
| |
| if (free >= (sizeof(struct ocfs2_xattr_entry) + namevalue_size_xi(xi))) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int ocfs2_calc_xattr_set_need(struct inode *inode, |
| struct ocfs2_dinode *di, |
| struct ocfs2_xattr_info *xi, |
| struct ocfs2_xattr_search *xis, |
| struct ocfs2_xattr_search *xbs, |
| int *clusters_need, |
| int *meta_need, |
| int *credits_need) |
| { |
| int ret = 0, old_in_xb = 0; |
| int clusters_add = 0, meta_add = 0, credits = 0; |
| struct buffer_head *bh = NULL; |
| struct ocfs2_xattr_block *xb = NULL; |
| struct ocfs2_xattr_entry *xe = NULL; |
| struct ocfs2_xattr_value_root *xv = NULL; |
| char *base = NULL; |
| int name_offset, name_len = 0; |
| u32 new_clusters = ocfs2_clusters_for_bytes(inode->i_sb, |
| xi->xi_value_len); |
| u64 value_size; |
| |
| /* |
| * Calculate the clusters we need to write. |
| * No matter whether we replace an old one or add a new one, |
| * we need this for writing. |
| */ |
| if (xi-> |