Commit 33268eaf authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason

Btrfs: Add ACL support

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 6099afe8
...@@ -20,76 +20,302 @@ ...@@ -20,76 +20,302 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include <linux/posix_acl.h>
#include <linux/sched.h> #include <linux/sched.h>
#include "ctree.h" #include "ctree.h"
#include "btrfs_inode.h"
#include "xattr.h" #include "xattr.h"
#ifndef is_owner_or_cap
#define is_owner_or_cap(inode) \ static void btrfs_update_cached_acl(struct inode *inode,
((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER)) struct posix_acl **p_acl,
#endif struct posix_acl *acl)
{
spin_lock(&inode->i_lock);
if (*p_acl && *p_acl != BTRFS_ACL_NOT_CACHED)
posix_acl_release(*p_acl);
*p_acl = posix_acl_dup(acl);
spin_unlock(&inode->i_lock);
}
static struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
{
int size, name_index;
char *value = NULL;
struct posix_acl *acl = NULL, **p_acl;
switch (type) {
case ACL_TYPE_ACCESS:
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS;
p_acl = &BTRFS_I(inode)->i_acl;
break;
case ACL_TYPE_DEFAULT:
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT;
p_acl = &BTRFS_I(inode)->i_default_acl;
break;
default:
return ERR_PTR(-EINVAL);
}
spin_lock(&inode->i_lock);
if (*p_acl != BTRFS_ACL_NOT_CACHED)
acl = posix_acl_dup(*p_acl);
spin_unlock(&inode->i_lock);
if (acl)
return acl;
size = btrfs_xattr_get(inode, name_index, "", NULL, 0);
if (size > 0) {
value = kzalloc(size, GFP_NOFS);
if (!value)
return ERR_PTR(-ENOMEM);
size = btrfs_xattr_get(inode, name_index, "", value, size);
if (size > 0) {
acl = posix_acl_from_xattr(value, size);
btrfs_update_cached_acl(inode, p_acl, acl);
}
kfree(value);
} else if (size == -ENOENT) {
acl = NULL;
btrfs_update_cached_acl(inode, p_acl, acl);
}
return acl;
}
static int btrfs_xattr_get_acl(struct inode *inode, int type,
void *value, size_t size)
{
struct posix_acl *acl;
int ret = 0;
acl = btrfs_get_acl(inode, type);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl == NULL)
return -ENODATA;
ret = posix_acl_to_xattr(acl, value, size);
posix_acl_release(acl);
return ret;
}
/*
* Needs to be called with fs_mutex held
*/
static int btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
int ret, name_index = 0, size = 0;
struct posix_acl **p_acl;
char *value = NULL;
mode_t mode;
if (acl) {
ret = posix_acl_valid(acl);
if (ret < 0)
return ret;
ret = 0;
}
switch (type) {
case ACL_TYPE_ACCESS:
mode = inode->i_mode;
ret = posix_acl_equiv_mode(acl, &mode);
if (ret < 0)
return ret;
ret = 0;
inode->i_mode = mode;
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS;
p_acl = &BTRFS_I(inode)->i_acl;
break;
case ACL_TYPE_DEFAULT:
if (!S_ISDIR(inode->i_mode))
return acl ? -EINVAL : 0;
name_index = BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT;
p_acl = &BTRFS_I(inode)->i_default_acl;
break;
default:
return -EINVAL;
}
if (acl) {
size = posix_acl_xattr_size(acl->a_count);
value = kmalloc(size, GFP_NOFS);
if (!value) {
ret = -ENOMEM;
goto out;
}
ret = posix_acl_to_xattr(acl, value, size);
if (ret < 0)
goto out;
}
ret = btrfs_xattr_set(inode, name_index, "", value, size, 0);
out:
if (value)
kfree(value);
if (!ret)
btrfs_update_cached_acl(inode, p_acl, acl);
return ret;
}
static int btrfs_xattr_set_acl(struct inode *inode, int type, static int btrfs_xattr_set_acl(struct inode *inode, int type,
const void *value, size_t size) const void *value, size_t size)
{ {
int ret = 0; int ret = 0;
struct posix_acl *acl; struct posix_acl *acl = NULL;
if (!is_owner_or_cap(inode))
return -EPERM;
if (value) { if (value) {
acl = posix_acl_from_xattr(value, size); acl = posix_acl_from_xattr(value, size);
if (acl == NULL) { if (acl == NULL) {
value = NULL; value = NULL;
size = 0; size = 0;
} else if (IS_ERR(acl)) { } else if (IS_ERR(acl)) {
ret = PTR_ERR(acl); return PTR_ERR(acl);
} else {
ret = posix_acl_valid(acl);
posix_acl_release(acl);
} }
if (ret)
return ret;
} }
return btrfs_xattr_set(inode, type, "", value, size, 0);
}
static int btrfs_xattr_get_acl(struct inode *inode, int type, ret = btrfs_set_acl(inode, acl, type);
void *value, size_t size)
{ posix_acl_release(acl);
return btrfs_xattr_get(inode, type, "", value, size);
return ret;
} }
static int btrfs_xattr_acl_access_get(struct inode *inode, const char *name, static int btrfs_xattr_acl_access_get(struct inode *inode, const char *name,
void *value, size_t size) void *value, size_t size)
{ {
if (*name != '\0') return btrfs_xattr_get_acl(inode, ACL_TYPE_ACCESS, value, size);
return -EINVAL;
return btrfs_xattr_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
value, size);
} }
static int btrfs_xattr_acl_access_set(struct inode *inode, const char *name, static int btrfs_xattr_acl_access_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
{ {
if (*name != '\0') return btrfs_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
return -EINVAL;
return btrfs_xattr_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS,
value, size);
} }
static int btrfs_xattr_acl_default_get(struct inode *inode, const char *name, static int btrfs_xattr_acl_default_get(struct inode *inode, const char *name,
void *value, size_t size) void *value, size_t size)
{ {
if (*name != '\0') return btrfs_xattr_get_acl(inode, ACL_TYPE_DEFAULT, value, size);
return -EINVAL;
return btrfs_xattr_get_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
value, size);
} }
static int btrfs_xattr_acl_default_set(struct inode *inode, const char *name, static int btrfs_xattr_acl_default_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
{ {
if (*name != '\0') return btrfs_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
return -EINVAL; }
return btrfs_xattr_set_acl(inode, BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT,
value, size); int btrfs_check_acl(struct inode *inode, int mask)
{
struct posix_acl *acl;
int error = -EAGAIN;
acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl);
}
return error;
}
/*
* btrfs_init_acl is already generally called under fs_mutex, so the locking
* stuff has been fixed to work with that. If the locking stuff changes, we
* need to re-evaluate the acl locking stuff.
*/
int btrfs_init_acl(struct inode *inode, struct inode *dir)
{
struct posix_acl *acl = NULL;
int ret = 0;
/* this happens with subvols */
if (!dir)
return 0;
if (!S_ISLNK(inode->i_mode)) {
if (IS_POSIXACL(dir)) {
acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
}
if (!acl)
inode->i_mode &= ~current->fs->umask;
}
if (IS_POSIXACL(dir) && acl) {
struct posix_acl *clone;
mode_t mode;
if (S_ISDIR(inode->i_mode)) {
ret = btrfs_set_acl(inode, acl, ACL_TYPE_DEFAULT);
if (ret)
goto failed;
}
clone = posix_acl_clone(acl, GFP_NOFS);
ret = -ENOMEM;
if (!clone)
goto failed;
mode = inode->i_mode;
ret = posix_acl_create_masq(clone, &mode);
if (ret >= 0) {
inode->i_mode = mode;
if (ret > 0) {
/* we need an acl */
ret = btrfs_set_acl(inode, clone,
ACL_TYPE_ACCESS);
}
}
}
failed:
posix_acl_release(acl);
return ret;
}
int btrfs_acl_chmod(struct inode *inode)
{
struct posix_acl *acl, *clone;
int ret = 0;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
if (!IS_POSIXACL(inode))
return 0;
acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl)
return PTR_ERR(acl);
clone = posix_acl_clone(acl, GFP_KERNEL);
posix_acl_release(acl);
if (!clone)
return -ENOMEM;
ret = posix_acl_chmod_masq(clone, inode->i_mode);
if (!ret)
ret = btrfs_set_acl(inode, clone, ACL_TYPE_ACCESS);
posix_acl_release(clone);
return ret;
} }
struct xattr_handler btrfs_xattr_acl_default_handler = { struct xattr_handler btrfs_xattr_acl_default_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT, .prefix = POSIX_ACL_XATTR_DEFAULT,
.list = btrfs_xattr_generic_list, .list = btrfs_xattr_generic_list,
......
...@@ -36,6 +36,9 @@ struct btrfs_inode { ...@@ -36,6 +36,9 @@ struct btrfs_inode {
struct inode vfs_inode; struct inode vfs_inode;
struct btrfs_ordered_inode_tree ordered_tree; struct btrfs_ordered_inode_tree ordered_tree;
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
/* /*
* transid of the trans_handle that last modified this inode * transid of the trans_handle that last modified this inode
*/ */
......
...@@ -42,6 +42,8 @@ struct btrfs_ordered_sum; ...@@ -42,6 +42,8 @@ struct btrfs_ordered_sum;
#define BTRFS_MAGIC "_B5RfS_M" #define BTRFS_MAGIC "_B5RfS_M"
#define BTRFS_ACL_NOT_CACHED ((void *)-1)
#ifdef CONFIG_LOCKDEP #ifdef CONFIG_LOCKDEP
# define BTRFS_MAX_LEVEL 7 # define BTRFS_MAX_LEVEL 7
#else #else
...@@ -1694,4 +1696,9 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size); ...@@ -1694,4 +1696,9 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
u64 btrfs_parse_size(char *str); u64 btrfs_parse_size(char *str);
int btrfs_parse_options(struct btrfs_root *root, char *options); int btrfs_parse_options(struct btrfs_root *root, char *options);
int btrfs_sync_fs(struct super_block *sb, int wait); int btrfs_sync_fs(struct super_block *sb, int wait);
/* acl.c */
int btrfs_check_acl(struct inode *inode, int mask);
int btrfs_init_acl(struct inode *inode, struct inode *dir);
int btrfs_acl_chmod(struct inode *inode);
#endif #endif
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/bit_spinlock.h> #include <linux/bit_spinlock.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/posix_acl.h>
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "transaction.h" #include "transaction.h"
...@@ -1478,6 +1479,9 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -1478,6 +1479,9 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
} }
out: out:
err = inode_setattr(inode, attr); err = inode_setattr(inode, attr);
if (!err && ((attr->ia_valid & ATTR_MODE)))
err = btrfs_acl_chmod(inode);
fail: fail:
return err; return err;
} }
...@@ -2184,6 +2188,12 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry, ...@@ -2184,6 +2188,12 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_unlock; goto out_unlock;
err = btrfs_init_acl(inode, dir);
if (err) {
drop_inode = 1;
goto out_unlock;
}
btrfs_set_trans_block_group(trans, inode); btrfs_set_trans_block_group(trans, inode);
err = btrfs_add_nondir(trans, dentry, inode, 0); err = btrfs_add_nondir(trans, dentry, inode, 0);
if (err) if (err)
...@@ -2239,6 +2249,12 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, ...@@ -2239,6 +2249,12 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_unlock; goto out_unlock;
err = btrfs_init_acl(inode, dir);
if (err) {
drop_inode = 1;
goto out_unlock;
}
btrfs_set_trans_block_group(trans, inode); btrfs_set_trans_block_group(trans, inode);
err = btrfs_add_nondir(trans, dentry, inode, 0); err = btrfs_add_nondir(trans, dentry, inode, 0);
if (err) if (err)
...@@ -2366,6 +2382,11 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -2366,6 +2382,11 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
} }
drop_on_err = 1; drop_on_err = 1;
err = btrfs_init_acl(inode, dir);
if (err)
goto out_fail;
inode->i_op = &btrfs_dir_inode_operations; inode->i_op = &btrfs_dir_inode_operations;
inode->i_fop = &btrfs_dir_file_operations; inode->i_fop = &btrfs_dir_file_operations;
btrfs_set_trans_block_group(trans, inode); btrfs_set_trans_block_group(trans, inode);
...@@ -3023,6 +3044,8 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) ...@@ -3023,6 +3044,8 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
return NULL; return NULL;
ei->last_trans = 0; ei->last_trans = 0;
btrfs_ordered_inode_tree_init(&ei->ordered_tree); btrfs_ordered_inode_tree_init(&ei->ordered_tree);
ei->i_acl = BTRFS_ACL_NOT_CACHED;
ei->i_default_acl = BTRFS_ACL_NOT_CACHED;
return &ei->vfs_inode; return &ei->vfs_inode;
} }
...@@ -3032,6 +3055,13 @@ void btrfs_destroy_inode(struct inode *inode) ...@@ -3032,6 +3055,13 @@ void btrfs_destroy_inode(struct inode *inode)
WARN_ON(!list_empty(&inode->i_dentry)); WARN_ON(!list_empty(&inode->i_dentry));
WARN_ON(inode->i_data.nrpages); WARN_ON(inode->i_data.nrpages);
if (BTRFS_I(inode)->i_acl &&
BTRFS_I(inode)->i_acl != BTRFS_ACL_NOT_CACHED)
posix_acl_release(BTRFS_I(inode)->i_acl);
if (BTRFS_I(inode)->i_default_acl &&
BTRFS_I(inode)->i_default_acl != BTRFS_ACL_NOT_CACHED)
posix_acl_release(BTRFS_I(inode)->i_default_acl);
while(1) { while(1) {
ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1); ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1);
if (!ordered) if (!ordered)
...@@ -3230,6 +3260,12 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -3230,6 +3260,12 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_unlock; goto out_unlock;
err = btrfs_init_acl(inode, dir);
if (err) {
drop_inode = 1;
goto out_unlock;
}
btrfs_set_trans_block_group(trans, inode); btrfs_set_trans_block_group(trans, inode);
err = btrfs_add_nondir(trans, dentry, inode, 0); err = btrfs_add_nondir(trans, dentry, inode, 0);
if (err) if (err)
...@@ -3310,7 +3346,7 @@ static int btrfs_permission(struct inode *inode, int mask, ...@@ -3310,7 +3346,7 @@ static int btrfs_permission(struct inode *inode, int mask,
{ {
if (btrfs_test_flag(inode, READONLY) && (mask & MAY_WRITE)) if (btrfs_test_flag(inode, READONLY) && (mask & MAY_WRITE))
return -EACCES; return -EACCES;
return generic_permission(inode, mask, NULL); return generic_permission(inode, mask, btrfs_check_acl);
} }
static struct inode_operations btrfs_dir_inode_operations = { static struct inode_operations btrfs_dir_inode_operations = {
...@@ -3392,6 +3428,10 @@ static struct inode_operations btrfs_special_inode_operations = { ...@@ -3392,6 +3428,10 @@ static struct inode_operations btrfs_special_inode_operations = {
.getattr = btrfs_getattr, .getattr = btrfs_getattr,
.setattr = btrfs_setattr, .setattr = btrfs_setattr,
.permission = btrfs_permission, .permission = btrfs_permission,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
.removexattr = generic_removexattr,
}; };
static struct inode_operations btrfs_symlink_inode_operations = { static struct inode_operations btrfs_symlink_inode_operations = {
.readlink = generic_readlink, .readlink = generic_readlink,
......
...@@ -68,7 +68,7 @@ static void btrfs_put_super (struct super_block * sb) ...@@ -68,7 +68,7 @@ static void btrfs_put_super (struct super_block * sb)
enum { enum {
Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow, Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow,
Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier, Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier,
Opt_ssd, Opt_thread_pool, Opt_err, Opt_ssd, Opt_thread_pool, Opt_noacl, Opt_err,
}; };
static match_table_t tokens = { static match_table_t tokens = {
...@@ -83,7 +83,8 @@ static match_table_t tokens = { ...@@ -83,7 +83,8 @@ static match_table_t tokens = {
{Opt_alloc_start, "alloc_start=%s"}, {Opt_alloc_start, "alloc_start=%s"},
{Opt_thread_pool, "thread_pool=%d"}, {Opt_thread_pool, "thread_pool=%d"},
{Opt_ssd, "ssd"}, {Opt_ssd, "ssd"},
{Opt_err, NULL} {Opt_noacl, "noacl"},
{Opt_err, NULL},
}; };
u64 btrfs_parse_size(char *str) u64 btrfs_parse_size(char *str)
...@@ -215,6 +216,9 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) ...@@ -215,6 +216,9 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
info->alloc_start); info->alloc_start);
} }
break; break;
case Opt_noacl:
root->fs_info->sb->s_flags &= ~MS_POSIXACL;
break;
default: default:
break; break;
} }
...@@ -301,6 +305,7 @@ static int btrfs_fill_super(struct super_block * sb, ...@@ -301,6 +305,7 @@ static int btrfs_fill_super(struct super_block * sb,
sb->s_op = &btrfs_super_ops; sb->s_op = &btrfs_super_ops;
sb->s_xattr = btrfs_xattr_handlers; sb->s_xattr = btrfs_xattr_handlers;
sb->s_time_gran = 1; sb->s_time_gran = 1;
sb->s_flags |= MS_POSIXACL;
tree_root = open_ctree(sb, fs_devices, (char *)data); tree_root = open_ctree(sb, fs_devices, (char *)data);
......
...@@ -26,25 +26,27 @@ ...@@ -26,25 +26,27 @@
#include "transaction.h" #include "transaction.h"
#include "xattr.h" #include "xattr.h"
#include "disk-io.h" #include "disk-io.h"
static struct xattr_handler *btrfs_xattr_handler_map[] = { static struct xattr_handler *btrfs_xattr_handler_map[] = {
[BTRFS_XATTR_INDEX_USER] = &btrfs_xattr_user_handler, [BTRFS_XATTR_INDEX_USER] = &btrfs_xattr_user_handler,
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
// [BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS] = &btrfs_xattr_acl_access_handler, [BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS] = &btrfs_xattr_acl_access_handler,
// [BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &btrfs_xattr_acl_default_handler, [BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &btrfs_xattr_acl_default_handler,
#endif #endif
[BTRFS_XATTR_INDEX_TRUSTED] = &btrfs_xattr_trusted_handler, [BTRFS_XATTR_INDEX_TRUSTED] = &btrfs_xattr_trusted_handler,
[BTRFS_XATTR_INDEX_SECURITY] = &btrfs_xattr_security_handler, [BTRFS_XATTR_INDEX_SECURITY] = &btrfs_xattr_security_handler,
// [BTRFS_XATTR_INDEX_SYSTEM] = &btrfs_xattr_system_handler, [BTRFS_XATTR_INDEX_SYSTEM] = &btrfs_xattr_system_handler,
}; };
struct xattr_handler *btrfs_xattr_handlers[] = { struct xattr_handler *btrfs_xattr_handlers[] = {
&btrfs_xattr_user_handler, &btrfs_xattr_user_handler,
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
// &btrfs_xattr_acl_access_handler, &btrfs_xattr_acl_access_handler,
// &btrfs_xattr_acl_default_handler, &btrfs_xattr_acl_default_handler,
#endif #endif
&btrfs_xattr_trusted_handler, &btrfs_xattr_trusted_handler,
&btrfs_xattr_security_handler, &btrfs_xattr_security_handler,
// &btrfs_xattr_system_handler, &btrfs_xattr_system_handler,
NULL, NULL,
}; };
...@@ -237,10 +239,14 @@ int btrfs_xattr_set(struct inode *inode, int name_index, ...@@ -237,10 +239,14 @@ int btrfs_xattr_set(struct inode *inode, int name_index,
mod = 1; mod = 1;
goto out; goto out;
} }
} else if (flags & XATTR_REPLACE) { } else {
/* we couldn't find the attr to replace, so error out */ btrfs_release_path(root, path);
ret = -ENODATA;
goto out; if (flags & XATTR_REPLACE) {
/* we couldn't find the attr to replace */
ret = -ENODATA;
goto out;
}
} }
/* ok we have to create a completely new xattr */ /* ok we have to create a completely new xattr */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment