Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/fs/ocfs2/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 192 kB image not shown  

Quelle  xattr.c   Sprache: C

 
// 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 cluster 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(struct inode *inode, int name_index,
      const char *name,
      struct ocfs2_xattr_search *xs)
{
 struct ocfs2_xattr_entry *entry;
 size_t name_len;
 int i, name_offset, 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++) {
  if ((void *)entry >= xs->end) {
   ocfs2_error(inode->i_sb, "corrupted xattr entries");
   return -EFSCORRUPTED;
  }
  cmp = name_index - ocfs2_xattr_get_type(entry);
  if (!cmp)
   cmp = name_len - entry->xe_name_len;
  if (!cmp) {
   name_offset = le16_to_cpu(entry->xe_name_offset);
   if ((xs->base + name_offset + name_len) > xs->end) {
    ocfs2_error(inode->i_sb,
         "corrupted xattr entries");
    return -EFSCORRUPTED;
   }
   cmp = memcmp(name, (xs->base + 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(inode, 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);
   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);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=88 G=91

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.