blob: dd784eb0cd7c42ea1ac26d75eb6f8cdeb9c457b8 [file] [log] [blame]
// 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 *ocfs2_xattr_handlers[] = {
&ocfs2_xattr_user_handler,
&posix_acl_access_xattr_handler,
&posix_acl_default_xattr_handler,
&ocfs2_xattr_trusted_handler,
&ocfs2_xattr_security_handler,
NULL
};
static const struct xattr_handler *ocfs2_xattr_handler_map[OCFS2_XATTR_MAX] = {
[OCFS2_XATTR_INDEX_USER] = &ocfs2_xattr_user_handler,
[OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS]
= &posix_acl_access_xattr_handler,
[OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT]
= &posix_acl_default_xattr_handler,
[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,