ceph: ensure prealloc_blob is in place when removing xattr
In __ceph_build_xattrs_blob(), if a ceph inode's extended attributes are marked dirty, all attributes recorded in its rb_tree index are formatted into a "blob" buffer. The target buffer is recorded in ceph_inode->i_xattrs.prealloc_blob, and it is expected to exist and be of sufficient size to hold the attributes. The extended attributes are marked dirty in two cases: when a new attribute is added to the inode; or when one is removed. In the former case work is done to ensure the prealloc_blob buffer is properly set up, but in the latter it is not. Change the logic in ceph_removexattr() so it matches what is done in ceph_setxattr(). Note that this is done in a way that keeps the two blocks of code nearly identical, in anticipation of a subsequent patch that encapsulates some of this logic into one or more helper routines. Signed-off-by: Alex Elder <elder@dreamhost.com> Signed-off-by: Sage Weil <sage@newdream.net>
This commit is contained in:
@@ -818,6 +818,7 @@ int ceph_removexattr(struct dentry *dentry, const char *name)
|
|||||||
struct ceph_vxattr_cb *vxattrs = ceph_inode_vxattrs(inode);
|
struct ceph_vxattr_cb *vxattrs = ceph_inode_vxattrs(inode);
|
||||||
int issued;
|
int issued;
|
||||||
int err;
|
int err;
|
||||||
|
int required_blob_size;
|
||||||
int dirty;
|
int dirty;
|
||||||
|
|
||||||
if (ceph_snap(inode) != CEPH_NOSNAP)
|
if (ceph_snap(inode) != CEPH_NOSNAP)
|
||||||
@@ -833,14 +834,34 @@ int ceph_removexattr(struct dentry *dentry, const char *name)
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
spin_lock(&ci->i_ceph_lock);
|
spin_lock(&ci->i_ceph_lock);
|
||||||
__build_xattrs(inode);
|
__build_xattrs(inode);
|
||||||
|
retry:
|
||||||
issued = __ceph_caps_issued(ci, NULL);
|
issued = __ceph_caps_issued(ci, NULL);
|
||||||
dout("removexattr %p issued %s\n", inode, ceph_cap_string(issued));
|
dout("removexattr %p issued %s\n", inode, ceph_cap_string(issued));
|
||||||
|
|
||||||
if (!(issued & CEPH_CAP_XATTR_EXCL))
|
if (!(issued & CEPH_CAP_XATTR_EXCL))
|
||||||
goto do_sync;
|
goto do_sync;
|
||||||
|
|
||||||
|
required_blob_size = __get_required_blob_size(ci, 0, 0);
|
||||||
|
|
||||||
|
if (!ci->i_xattrs.prealloc_blob ||
|
||||||
|
required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) {
|
||||||
|
struct ceph_buffer *blob;
|
||||||
|
|
||||||
|
spin_unlock(&ci->i_ceph_lock);
|
||||||
|
dout(" preaallocating new blob size=%d\n", required_blob_size);
|
||||||
|
blob = ceph_buffer_new(required_blob_size, GFP_NOFS);
|
||||||
|
if (!blob)
|
||||||
|
goto out;
|
||||||
|
spin_lock(&ci->i_ceph_lock);
|
||||||
|
if (ci->i_xattrs.prealloc_blob)
|
||||||
|
ceph_buffer_put(ci->i_xattrs.prealloc_blob);
|
||||||
|
ci->i_xattrs.prealloc_blob = blob;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
err = __remove_xattr_by_name(ceph_inode(inode), name);
|
err = __remove_xattr_by_name(ceph_inode(inode), name);
|
||||||
dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
|
dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
|
||||||
ci->i_xattrs.dirty = true;
|
ci->i_xattrs.dirty = true;
|
||||||
@@ -853,6 +874,7 @@ int ceph_removexattr(struct dentry *dentry, const char *name)
|
|||||||
do_sync:
|
do_sync:
|
||||||
spin_unlock(&ci->i_ceph_lock);
|
spin_unlock(&ci->i_ceph_lock);
|
||||||
err = ceph_send_removexattr(dentry, name);
|
err = ceph_send_removexattr(dentry, name);
|
||||||
|
out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user