#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) \
- - sizeof(__u32))
+ - 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) \
- - sizeof(__u32))
+ - OCFS2_XATTR_HEADER_GAP)
static struct ocfs2_xattr_def_value_root def_xv = {
.xv.xr_list.l_count = cpu_to_le16(1),
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);
}
{
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]);
}
/* reserve clusters for xattr value which will be set in B tree*/
- if (si->value_len > OCFS2_XATTR_INLINE_SIZE)
- *want_clusters += ocfs2_clusters_for_bytes(dir->i_sb,
- si->value_len);
+ 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 ret = 0;
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
- int s_size = 0;
- int a_size = 0;
- int acl_len = 0;
+ 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),
*xattr_credits += ocfs2_blocks_per_xattr_bucket(dir->i_sb);
}
- /* reserve clusters for xattr value which will be set in B tree*/
- if (si->enable && si->value_len > OCFS2_XATTR_INLINE_SIZE)
- *want_clusters += ocfs2_clusters_for_bytes(dir->i_sb,
- si->value_len);
+ /*
+ * 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) {
- *want_clusters += ocfs2_clusters_for_bytes(dir->i_sb, acl_len);
- if (S_ISDIR(mode))
- *want_clusters += ocfs2_clusters_for_bytes(dir->i_sb,
- acl_len);
+ /* 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;
const void *value,
int value_len)
{
- int ret = 0, i, cp_len, credits;
+ 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);
BUG_ON(clusters > le32_to_cpu(xv->xr_clusters));
- /*
- * In __ocfs2_xattr_set_value_outside has already been dirtied,
- * so we don't need to worry about whether ocfs2_extend_trans
- * will create a new transactio for us or not.
- */
- credits = clusters * bpc;
- ret = ocfs2_extend_trans(handle, credits);
- if (ret) {
- mlog_errno(ret);
- goto out;
- }
-
while (cpos < clusters) {
ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster,
&num_clusters, &xv->xr_list);
last += 1;
}
- free = min_offs - ((void *)last - xs->base) - sizeof(__u32);
+ free = min_offs - ((void *)last - xs->base) - OCFS2_XATTR_HEADER_GAP;
if (free < 0)
return -EIO;
oi->ip_dyn_features |= flag;
di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
spin_unlock(&oi->ip_lock);
- /* Update inode ctime */
- inode->i_ctime = CURRENT_TIME;
- di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
- di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
ret = ocfs2_journal_dirty(handle, xs->inode_bh);
if (ret < 0)
mlog_errno(ret);
goto out;
}
- ret = ocfs2_journal_access(handle, inode, di_bh,
- OCFS2_JOURNAL_ACCESS_WRITE);
+ ret = ocfs2_journal_access_di(handle, inode, di_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out_commit;
int ret;
if (!xs->xattr_bh) {
- ret = ocfs2_journal_access(handle, inode, xs->inode_bh,
- OCFS2_JOURNAL_ACCESS_CREATE);
+ ret = ocfs2_journal_access_di(handle, inode, xs->inode_bh,
+ OCFS2_JOURNAL_ACCESS_CREATE);
if (ret < 0) {
mlog_errno(ret);
goto end;
new_bh = sb_getblk(inode->i_sb, first_blkno);
ocfs2_set_new_buffer_uptodate(inode, new_bh);
- ret = ocfs2_journal_access(handle, inode, new_bh,
- OCFS2_JOURNAL_ACCESS_CREATE);
+ ret = ocfs2_journal_access_xb(handle, inode, new_bh,
+ OCFS2_JOURNAL_ACCESS_CREATE);
if (ret < 0) {
mlog_errno(ret);
goto end;
last += 1;
}
- free = min_offs - ((void *)last - xs->base) - sizeof(__u32);
+ free = min_offs - ((void *)last - xs->base) - OCFS2_XATTR_HEADER_GAP;
if (free < 0)
return 0;
xi->value_len);
u64 value_size;
+ /*
+ * Calculate the clusters we need to write.
+ * No matter whether we replace an old one or add a new one,
+ * we need this for writing.
+ */
+ if (xi->value_len > OCFS2_XATTR_INLINE_SIZE)
+ credits += new_clusters *
+ ocfs2_clusters_to_blocks(inode->i_sb, 1);
+
if (xis->not_found && xbs->not_found) {
credits += ocfs2_blocks_per_xattr_bucket(inode->i_sb);
} else
xb = (struct ocfs2_xattr_block *)xbs->xattr_bh->b_data;
+ /*
+ * If there is already an xattr tree, good, we can calculate
+ * like other b-trees. Otherwise we may have the chance of
+ * create a tree, the credit calculation is borrowed from
+ * ocfs2_calc_extend_credits with root_el = NULL. And the
+ * new tree will be cluster based, so no meta is needed.
+ */
if (le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED) {
struct ocfs2_extent_list *el =
&xb->xb_attrs.xb_root.xt_list;
meta_add += ocfs2_extend_meta_needed(el);
credits += ocfs2_calc_extend_credits(inode->i_sb,
el, 1);
- }
+ } else
+ credits += OCFS2_SUBALLOC_ALLOC + 1;
/*
* This cluster will be used either for new bucket or for
}
}
+ if (!ret) {
+ /* Update inode ctime. */
+ ret = ocfs2_journal_access_di(ctxt->handle, inode,
+ xis->inode_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ inode->i_ctime = CURRENT_TIME;
+ di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
+ di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+ ocfs2_journal_dirty(ctxt->handle, xis->inode_bh);
+ }
out:
return ret;
}
/*
* This function only called duing creating inode
* for init security/acl xattrs of the new inode.
- * The xattrs could be put into ibody or extent block,
- * xattr bucket would not be use in this case.
- * transanction credits also be reserved in here.
+ * All transanction credits have been reserved in mknod.
*/
int ocfs2_xattr_set_handle(handle_t *handle,
struct inode *inode,
if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb)))
return -EOPNOTSUPP;
+ /*
+ * In extreme situation, may need xattr bucket when
+ * block size is too small. And we have already reserved
+ * the credits for bucket in mknod.
+ */
+ if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) {
+ xbs.bucket = ocfs2_xattr_bucket_new(inode);
+ if (!xbs.bucket) {
+ mlog_errno(-ENOMEM);
+ return -ENOMEM;
+ }
+ }
+
xis.inode_bh = xbs.inode_bh = di_bh;
di = (struct ocfs2_dinode *)di_bh->b_data;
cleanup:
up_write(&OCFS2_I(inode)->ip_xattr_sem);
brelse(xbs.xattr_bh);
+ ocfs2_xattr_bucket_free(xbs.bucket);
return ret;
}
goto cleanup;
}
+ /* we need to update inode's ctime field, so add credit for it. */
+ credits += OCFS2_INODE_UPDATE_CREDITS;
ctxt.handle = ocfs2_start_trans(osb, credits);
if (IS_ERR(ctxt.handle)) {
ret = PTR_ERR(ctxt.handle);
*/
down_write(&oi->ip_alloc_sem);
- ret = ocfs2_journal_access(handle, inode, xb_bh,
- OCFS2_JOURNAL_ACCESS_WRITE);
+ ret = ocfs2_journal_access_xb(handle, inode, xb_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out;
ocfs2_init_xattr_tree_extent_tree(&et, inode, root_bh);
- ret = ocfs2_journal_access(handle, inode, root_bh,
- OCFS2_JOURNAL_ACCESS_WRITE);
+ ret = ocfs2_journal_access_xb(handle, inode, root_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
if (ret < 0) {
mlog_errno(ret);
goto leave;
vb.vb_xv = (struct ocfs2_xattr_value_root *)
(vb.vb_bh->b_data + offset % blocksize);
- ret = ocfs2_xattr_bucket_journal_access(ctxt->handle, bucket,
- OCFS2_JOURNAL_ACCESS_WRITE);
- if (ret) {
- mlog_errno(ret);
- goto out;
- }
-
/*
* From here on out we have to dirty the bucket. The generic
* value calls only modify one of the bucket's bhs, but we need
ret = ocfs2_xattr_value_truncate(inode, &vb, len, ctxt);
if (ret) {
mlog_errno(ret);
- goto out_dirty;
+ goto out;
+ }
+
+ ret = ocfs2_xattr_bucket_journal_access(ctxt->handle, bucket,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
}
xe->xe_value_size = cpu_to_le64(len);
-out_dirty:
ocfs2_xattr_bucket_journal_dirty(ctxt->handle, bucket);
out:
goto out;
}
- ret = ocfs2_journal_access(handle, inode, root_bh,
- OCFS2_JOURNAL_ACCESS_WRITE);
+ ret = ocfs2_journal_access_xb(handle, inode, root_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
if (ret) {
mlog_errno(ret);
goto out_commit;
xh_free_start = le16_to_cpu(xh->xh_free_start);
header_size = sizeof(struct ocfs2_xattr_header) +
count * sizeof(struct ocfs2_xattr_entry);
- max_free = OCFS2_XATTR_BUCKET_SIZE -
- le16_to_cpu(xh->xh_name_value_len) - header_size;
+ max_free = OCFS2_XATTR_BUCKET_SIZE - header_size -
+ le16_to_cpu(xh->xh_name_value_len) - OCFS2_XATTR_HEADER_GAP;
mlog_bug_on_msg(header_size > blocksize, "bucket %llu has header size "
"of %u which exceed block size\n",
need = 0;
}
- free = xh_free_start - header_size;
+ free = xh_free_start - header_size - OCFS2_XATTR_HEADER_GAP;
/*
* We need to make sure the new name/value pair
* can exist in the same block.
}
xh_free_start = le16_to_cpu(xh->xh_free_start);
- free = xh_free_start - header_size;
+ free = xh_free_start - header_size
+ - OCFS2_XATTR_HEADER_GAP;
if (xh_free_start % blocksize < need)
free -= xh_free_start % blocksize;
struct inode *dir,
struct ocfs2_security_xattr_info *si)
{
+ /* check whether ocfs2 support feature xattr */
+ if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
+ return -EOPNOTSUPP;
return security_inode_init_security(inode, dir, &si->name, &si->value,
&si->value_len);
}