Skip to content

Commit

Permalink
nfs: use generic posix ACL infrastructure for v3 Posix ACLs
Browse files Browse the repository at this point in the history
This causes a small behaviour change in that we don't bother to set
ACLs on file creation if the mode bit can express the access permissions
fully, and thus behaving identical to local filesystems.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Christoph Hellwig authored and Al Viro committed Jan 26, 2014
1 parent e01580b commit 013cdf1
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 281 deletions.
4 changes: 0 additions & 4 deletions fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1641,10 +1641,6 @@ struct inode *nfs_alloc_inode(struct super_block *sb)
return NULL;
nfsi->flags = 0UL;
nfsi->cache_validity = 0UL;
#ifdef CONFIG_NFS_V3_ACL
nfsi->acl_access = ERR_PTR(-EAGAIN);
nfsi->acl_default = ERR_PTR(-EAGAIN);
#endif
#if IS_ENABLED(CONFIG_NFS_V4)
nfsi->nfs4_acl = NULL;
#endif /* CONFIG_NFS_V4 */
Expand Down
291 changes: 55 additions & 236 deletions fs/nfs/nfs3acl.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,179 +10,7 @@

#define NFSDBG_FACILITY NFSDBG_PROC

ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size)
{
struct inode *inode = dentry->d_inode;
struct posix_acl *acl;
int pos=0, len=0;

# define output(s) do { \
if (pos + sizeof(s) <= size) { \
memcpy(buffer + pos, s, sizeof(s)); \
pos += sizeof(s); \
} \
len += sizeof(s); \
} while(0)

acl = nfs3_proc_getacl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
output("system.posix_acl_access");
posix_acl_release(acl);
}

if (S_ISDIR(inode->i_mode)) {
acl = nfs3_proc_getacl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
output("system.posix_acl_default");
posix_acl_release(acl);
}
}

# undef output

if (!buffer || len <= size)
return len;
return -ERANGE;
}

ssize_t nfs3_getxattr(struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
struct inode *inode = dentry->d_inode;
struct posix_acl *acl;
int type, error = 0;

if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
type = ACL_TYPE_ACCESS;
else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
type = ACL_TYPE_DEFAULT;
else
return -EOPNOTSUPP;

acl = nfs3_proc_getacl(inode, type);
if (IS_ERR(acl))
return PTR_ERR(acl);
else if (acl) {
if (type == ACL_TYPE_ACCESS && acl->a_count == 0)
error = -ENODATA;
else
error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
posix_acl_release(acl);
} else
error = -ENODATA;

return error;
}

int nfs3_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct posix_acl *acl;
int type, error;

if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
type = ACL_TYPE_ACCESS;
else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
type = ACL_TYPE_DEFAULT;
else
return -EOPNOTSUPP;

acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl))
return PTR_ERR(acl);
error = nfs3_proc_setacl(inode, type, acl);
posix_acl_release(acl);

return error;
}

int nfs3_removexattr(struct dentry *dentry, const char *name)
{
struct inode *inode = dentry->d_inode;
int type;

if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
type = ACL_TYPE_ACCESS;
else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
type = ACL_TYPE_DEFAULT;
else
return -EOPNOTSUPP;

return nfs3_proc_setacl(inode, type, NULL);
}

static void __nfs3_forget_cached_acls(struct nfs_inode *nfsi)
{
if (!IS_ERR(nfsi->acl_access)) {
posix_acl_release(nfsi->acl_access);
nfsi->acl_access = ERR_PTR(-EAGAIN);
}
if (!IS_ERR(nfsi->acl_default)) {
posix_acl_release(nfsi->acl_default);
nfsi->acl_default = ERR_PTR(-EAGAIN);
}
}

void nfs3_forget_cached_acls(struct inode *inode)
{
dprintk("NFS: nfs3_forget_cached_acls(%s/%ld)\n", inode->i_sb->s_id,
inode->i_ino);
spin_lock(&inode->i_lock);
__nfs3_forget_cached_acls(NFS_I(inode));
spin_unlock(&inode->i_lock);
}

static struct posix_acl *nfs3_get_cached_acl(struct inode *inode, int type)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct posix_acl *acl = ERR_PTR(-EINVAL);

spin_lock(&inode->i_lock);
switch(type) {
case ACL_TYPE_ACCESS:
acl = nfsi->acl_access;
break;

case ACL_TYPE_DEFAULT:
acl = nfsi->acl_default;
break;

default:
goto out;
}
if (IS_ERR(acl))
acl = ERR_PTR(-EAGAIN);
else
acl = posix_acl_dup(acl);
out:
spin_unlock(&inode->i_lock);
dprintk("NFS: nfs3_get_cached_acl(%s/%ld, %d) = %p\n", inode->i_sb->s_id,
inode->i_ino, type, acl);
return acl;
}

static void nfs3_cache_acls(struct inode *inode, struct posix_acl *acl,
struct posix_acl *dfacl)
{
struct nfs_inode *nfsi = NFS_I(inode);

dprintk("nfs3_cache_acls(%s/%ld, %p, %p)\n", inode->i_sb->s_id,
inode->i_ino, acl, dfacl);
spin_lock(&inode->i_lock);
__nfs3_forget_cached_acls(NFS_I(inode));
if (!IS_ERR(acl))
nfsi->acl_access = posix_acl_dup(acl);
if (!IS_ERR(dfacl))
nfsi->acl_default = posix_acl_dup(dfacl);
spin_unlock(&inode->i_lock);
}

struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
{
struct nfs_server *server = NFS_SERVER(inode);
struct page *pages[NFSACL_MAXPAGES] = { };
Expand All @@ -198,7 +26,6 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
.rpc_argp = &args,
.rpc_resp = &res,
};
struct posix_acl *acl;
int status, count;

if (!nfs_server_capable(inode, NFS_CAP_ACLS))
Expand All @@ -207,10 +34,6 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
status = nfs_revalidate_inode(server, inode);
if (status < 0)
return ERR_PTR(status);
acl = nfs3_get_cached_acl(inode, type);
if (acl != ERR_PTR(-EAGAIN))
return acl;
acl = NULL;

/*
* Only get the access acl when explicitly requested: We don't
Expand Down Expand Up @@ -257,40 +80,41 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
}

if (res.acl_access != NULL) {
if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) {
if (posix_acl_equiv_mode(res.acl_access, NULL) ||
res.acl_access->a_count == 0) {
posix_acl_release(res.acl_access);
res.acl_access = NULL;
}
}
nfs3_cache_acls(inode,
(res.mask & NFS_ACL) ? res.acl_access : ERR_PTR(-EINVAL),
(res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL));

switch(type) {
case ACL_TYPE_ACCESS:
acl = res.acl_access;
res.acl_access = NULL;
break;
if (res.mask & NFS_ACL)
set_cached_acl(inode, ACL_TYPE_ACCESS, res.acl_access);
else
forget_cached_acl(inode, ACL_TYPE_ACCESS);

case ACL_TYPE_DEFAULT:
acl = res.acl_default;
res.acl_default = NULL;
if (res.mask & NFS_DFACL)
set_cached_acl(inode, ACL_TYPE_DEFAULT, res.acl_default);
else
forget_cached_acl(inode, ACL_TYPE_DEFAULT);

nfs_free_fattr(res.fattr);
if (type == ACL_TYPE_ACCESS) {
posix_acl_release(res.acl_default);
return res.acl_access;
} else {
posix_acl_release(res.acl_access);
return res.acl_default;
}

getout:
posix_acl_release(res.acl_access);
posix_acl_release(res.acl_default);
nfs_free_fattr(res.fattr);

if (status != 0) {
posix_acl_release(acl);
acl = ERR_PTR(status);
}
return acl;
return ERR_PTR(status);
}

static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
struct posix_acl *dfacl)
int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
struct posix_acl *dfacl)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_fattr *fattr;
Expand Down Expand Up @@ -353,7 +177,8 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
switch (status) {
case 0:
status = nfs_refresh_inode(inode, fattr);
nfs3_cache_acls(inode, acl, dfacl);
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
set_cached_acl(inode, ACL_TYPE_DEFAULT, dfacl);
break;
case -EPFNOSUPPORT:
case -EPROTONOSUPPORT:
Expand All @@ -373,33 +198,27 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
return status;
}

int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl)
int nfs3_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
struct posix_acl *alloc = NULL, *dfacl = NULL;
int status;

if (S_ISDIR(inode->i_mode)) {
switch(type) {
case ACL_TYPE_ACCESS:
alloc = dfacl = nfs3_proc_getacl(inode,
ACL_TYPE_DEFAULT);
if (IS_ERR(alloc))
goto fail;
break;

case ACL_TYPE_DEFAULT:
dfacl = acl;
alloc = acl = nfs3_proc_getacl(inode,
ACL_TYPE_ACCESS);
if (IS_ERR(alloc))
goto fail;
break;

default:
return -EINVAL;
case ACL_TYPE_ACCESS:
alloc = dfacl = get_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(alloc))
goto fail;
break;

case ACL_TYPE_DEFAULT:
dfacl = acl;
alloc = acl = get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(alloc))
goto fail;
break;
}
} else if (type != ACL_TYPE_ACCESS)
return -EINVAL;
}

if (acl == NULL) {
alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
Expand All @@ -417,24 +236,24 @@ int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl)
int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
umode_t mode)
{
struct posix_acl *dfacl, *acl;
int error = 0;
struct posix_acl *default_acl, *acl;
int error;

dfacl = nfs3_proc_getacl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(dfacl)) {
error = PTR_ERR(dfacl);
error = posix_acl_create(dir, &mode, &default_acl, &acl);
if (error)
return (error == -EOPNOTSUPP) ? 0 : error;
}
if (!dfacl)
return 0;
acl = posix_acl_dup(dfacl);
error = __posix_acl_create(&acl, GFP_KERNEL, &mode);
if (error < 0)
goto out_release_dfacl;
error = nfs3_proc_setacls(inode, acl, S_ISDIR(inode->i_mode) ?
dfacl : NULL);
posix_acl_release(acl);
out_release_dfacl:
posix_acl_release(dfacl);

error = nfs3_proc_setacls(inode, acl, default_acl);

if (acl)
posix_acl_release(acl);
if (default_acl)
posix_acl_release(default_acl);
return error;
}

const struct xattr_handler *nfs3_xattr_handlers[] = {
&posix_acl_access_xattr_handler,
&posix_acl_default_xattr_handler,
NULL,
};
Loading

0 comments on commit 013cdf1

Please sign in to comment.