diff options
Diffstat (limited to 'fs/btrfs/xattr.c')
| -rw-r--r-- | fs/btrfs/xattr.c | 73 | 
1 files changed, 48 insertions, 25 deletions
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c index 5366fe452ab..69565e5fc6a 100644 --- a/fs/btrfs/xattr.c +++ b/fs/btrfs/xattr.c @@ -102,48 +102,71 @@ static int do_setxattr(struct btrfs_trans_handle *trans,  	if (!path)  		return -ENOMEM; -	/* first lets see if we already have this xattr */ -	di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, -				strlen(name), -1); -	if (IS_ERR(di)) { -		ret = PTR_ERR(di); -		goto out; -	} - -	/* ok we already have this xattr, lets remove it */ -	if (di) { -		/* if we want create only exit */ -		if (flags & XATTR_CREATE) { -			ret = -EEXIST; +	if (flags & XATTR_REPLACE) { +		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, +					name_len, -1); +		if (IS_ERR(di)) { +			ret = PTR_ERR(di); +			goto out; +		} else if (!di) { +			ret = -ENODATA;  			goto out;  		} -  		ret = btrfs_delete_one_dir_name(trans, root, path, di); -		BUG_ON(ret); +		if (ret) +			goto out;  		btrfs_release_path(path); -		/* if we don't have a value then we are removing the xattr */ +		/* +		 * remove the attribute +		 */  		if (!value)  			goto out; -	} else { +	} + +again: +	ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), +				      name, name_len, value, size); +	if (ret == -EEXIST) { +		if (flags & XATTR_CREATE) +			goto out; +		/* +		 * We can't use the path we already have since we won't have the +		 * proper locking for a delete, so release the path and +		 * re-lookup to delete the thing. +		 */  		btrfs_release_path(path); +		di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), +					name, name_len, -1); +		if (IS_ERR(di)) { +			ret = PTR_ERR(di); +			goto out; +		} else if (!di) { +			/* Shouldn't happen but just in case... */ +			btrfs_release_path(path); +			goto again; +		} -		if (flags & XATTR_REPLACE) { -			/* we couldn't find the attr to replace */ -			ret = -ENODATA; +		ret = btrfs_delete_one_dir_name(trans, root, path, di); +		if (ret)  			goto out; + +		/* +		 * We have a value to set, so go back and try to insert it now. +		 */ +		if (value) { +			btrfs_release_path(path); +			goto again;  		}  	} - -	/* ok we have to create a completely new xattr */ -	ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), -				      name, name_len, value, size); -	BUG_ON(ret);  out:  	btrfs_free_path(path);  	return ret;  } +/* + * @value: "" makes the attribute to empty, NULL removes it + */  int __btrfs_setxattr(struct btrfs_trans_handle *trans,  		     struct inode *inode, const char *name,  		     const void *value, size_t size, int flags)  |