39a6497a9b
Now that 9p supports the get and set acl inode operations and the vfs has been switched to the new posi api, 9p can simply rely on the stub posix acl handlers. The custom xattr handlers and associated unused helpers can be removed. Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
346 lines
7.8 KiB
C
346 lines
7.8 KiB
C
// SPDX-License-Identifier: LGPL-2.1
|
|
/*
|
|
* Copyright IBM Corporation, 2010
|
|
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <net/9p/9p.h>
|
|
#include <net/9p/client.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/posix_acl_xattr.h>
|
|
#include "xattr.h"
|
|
#include "acl.h"
|
|
#include "v9fs.h"
|
|
#include "v9fs_vfs.h"
|
|
#include "fid.h"
|
|
|
|
static struct posix_acl *v9fs_fid_get_acl(struct p9_fid *fid, const char *name)
|
|
{
|
|
ssize_t size;
|
|
void *value = NULL;
|
|
struct posix_acl *acl = NULL;
|
|
|
|
size = v9fs_fid_xattr_get(fid, name, NULL, 0);
|
|
if (size < 0)
|
|
return ERR_PTR(size);
|
|
if (size == 0)
|
|
return ERR_PTR(-ENODATA);
|
|
|
|
value = kzalloc(size, GFP_NOFS);
|
|
if (!value)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
size = v9fs_fid_xattr_get(fid, name, value, size);
|
|
if (size < 0)
|
|
acl = ERR_PTR(size);
|
|
else if (size == 0)
|
|
acl = ERR_PTR(-ENODATA);
|
|
else
|
|
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
|
kfree(value);
|
|
return acl;
|
|
}
|
|
|
|
static struct posix_acl *v9fs_acl_get(struct dentry *dentry, const char *name)
|
|
{
|
|
struct p9_fid *fid;
|
|
struct posix_acl *acl = NULL;
|
|
|
|
fid = v9fs_fid_lookup(dentry);
|
|
if (IS_ERR(fid))
|
|
return ERR_CAST(fid);
|
|
|
|
acl = v9fs_fid_get_acl(fid, name);
|
|
p9_fid_put(fid);
|
|
return acl;
|
|
}
|
|
|
|
static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, const char *name)
|
|
{
|
|
int retval;
|
|
struct posix_acl *acl = NULL;
|
|
|
|
acl = v9fs_fid_get_acl(fid, name);
|
|
if (!IS_ERR(acl))
|
|
return acl;
|
|
|
|
retval = PTR_ERR(acl);
|
|
if (retval == -ENODATA || retval == -ENOSYS || retval == -EOPNOTSUPP)
|
|
return NULL;
|
|
|
|
/* map everything else to -EIO */
|
|
return ERR_PTR(-EIO);
|
|
}
|
|
|
|
int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
|
{
|
|
int retval = 0;
|
|
struct posix_acl *pacl, *dacl;
|
|
struct v9fs_session_info *v9ses;
|
|
|
|
v9ses = v9fs_inode2v9ses(inode);
|
|
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
|
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
|
set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL);
|
|
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
|
|
return 0;
|
|
}
|
|
/* get the default/access acl values and cache them */
|
|
dacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_DEFAULT);
|
|
pacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_ACCESS);
|
|
|
|
if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
|
|
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
|
set_cached_acl(inode, ACL_TYPE_ACCESS, pacl);
|
|
} else
|
|
retval = -EIO;
|
|
|
|
if (!IS_ERR(dacl))
|
|
posix_acl_release(dacl);
|
|
|
|
if (!IS_ERR(pacl))
|
|
posix_acl_release(pacl);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
|
{
|
|
struct posix_acl *acl;
|
|
/*
|
|
* 9p Always cache the acl value when
|
|
* instantiating the inode (v9fs_inode_from_fid)
|
|
*/
|
|
acl = get_cached_acl(inode, type);
|
|
BUG_ON(is_uncached_acl(acl));
|
|
return acl;
|
|
}
|
|
|
|
struct posix_acl *v9fs_iop_get_inode_acl(struct inode *inode, int type, bool rcu)
|
|
{
|
|
struct v9fs_session_info *v9ses;
|
|
|
|
if (rcu)
|
|
return ERR_PTR(-ECHILD);
|
|
|
|
v9ses = v9fs_inode2v9ses(inode);
|
|
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
|
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
|
/*
|
|
* On access = client and acl = on mode get the acl
|
|
* values from the server
|
|
*/
|
|
return NULL;
|
|
}
|
|
return v9fs_get_cached_acl(inode, type);
|
|
|
|
}
|
|
|
|
struct posix_acl *v9fs_iop_get_acl(struct user_namespace *mnt_userns,
|
|
struct dentry *dentry, int type)
|
|
{
|
|
struct v9fs_session_info *v9ses;
|
|
|
|
v9ses = v9fs_dentry2v9ses(dentry);
|
|
/* We allow set/get/list of acl when access=client is not specified. */
|
|
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
|
return v9fs_acl_get(dentry, posix_acl_xattr_name(type));
|
|
return v9fs_get_cached_acl(d_inode(dentry), type);
|
|
}
|
|
|
|
int v9fs_iop_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
|
|
struct posix_acl *acl, int type)
|
|
{
|
|
int retval;
|
|
size_t size = 0;
|
|
void *value = NULL;
|
|
const char *acl_name;
|
|
struct v9fs_session_info *v9ses;
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (acl) {
|
|
retval = posix_acl_valid(inode->i_sb->s_user_ns, acl);
|
|
if (retval)
|
|
goto err_out;
|
|
|
|
size = posix_acl_xattr_size(acl->a_count);
|
|
|
|
value = kzalloc(size, GFP_NOFS);
|
|
if (!value) {
|
|
retval = -ENOMEM;
|
|
goto err_out;
|
|
}
|
|
|
|
retval = posix_acl_to_xattr(&init_user_ns, acl, value, size);
|
|
if (retval < 0)
|
|
goto err_out;
|
|
}
|
|
|
|
/*
|
|
* set the attribute on the remote. Without even looking at the
|
|
* xattr value. We leave it to the server to validate
|
|
*/
|
|
acl_name = posix_acl_xattr_name(type);
|
|
v9ses = v9fs_dentry2v9ses(dentry);
|
|
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
|
|
retval = v9fs_xattr_set(dentry, acl_name, value, size, 0);
|
|
goto err_out;
|
|
}
|
|
|
|
if (S_ISLNK(inode->i_mode)) {
|
|
retval = -EOPNOTSUPP;
|
|
goto err_out;
|
|
}
|
|
|
|
if (!inode_owner_or_capable(&init_user_ns, inode)) {
|
|
retval = -EPERM;
|
|
goto err_out;
|
|
}
|
|
|
|
switch (type) {
|
|
case ACL_TYPE_ACCESS:
|
|
if (acl) {
|
|
struct iattr iattr = {};
|
|
struct posix_acl *acl_mode = acl;
|
|
|
|
retval = posix_acl_update_mode(&init_user_ns, inode,
|
|
&iattr.ia_mode,
|
|
&acl_mode);
|
|
if (retval)
|
|
goto err_out;
|
|
if (!acl_mode) {
|
|
/*
|
|
* ACL can be represented by the mode bits.
|
|
* So don't update ACL below.
|
|
*/
|
|
kfree(value);
|
|
value = NULL;
|
|
size = 0;
|
|
}
|
|
iattr.ia_valid = ATTR_MODE;
|
|
/*
|
|
* FIXME should we update ctime ?
|
|
* What is the following setxattr update the mode ?
|
|
*/
|
|
v9fs_vfs_setattr_dotl(&init_user_ns, dentry, &iattr);
|
|
}
|
|
break;
|
|
case ACL_TYPE_DEFAULT:
|
|
if (!S_ISDIR(inode->i_mode)) {
|
|
retval = acl ? -EINVAL : 0;
|
|
goto err_out;
|
|
}
|
|
break;
|
|
}
|
|
|
|
retval = v9fs_xattr_set(dentry, acl_name, value, size, 0);
|
|
if (!retval)
|
|
set_cached_acl(inode, type, acl);
|
|
|
|
err_out:
|
|
kfree(value);
|
|
return retval;
|
|
}
|
|
|
|
static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl)
|
|
{
|
|
int retval;
|
|
char *name;
|
|
size_t size;
|
|
void *buffer;
|
|
|
|
if (!acl)
|
|
return 0;
|
|
|
|
/* Set a setxattr request to server */
|
|
size = posix_acl_xattr_size(acl->a_count);
|
|
buffer = kmalloc(size, GFP_KERNEL);
|
|
if (!buffer)
|
|
return -ENOMEM;
|
|
retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
|
if (retval < 0)
|
|
goto err_free_out;
|
|
switch (type) {
|
|
case ACL_TYPE_ACCESS:
|
|
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
|
break;
|
|
case ACL_TYPE_DEFAULT:
|
|
name = XATTR_NAME_POSIX_ACL_DEFAULT;
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0);
|
|
err_free_out:
|
|
kfree(buffer);
|
|
return retval;
|
|
}
|
|
|
|
int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
|
{
|
|
int retval = 0;
|
|
struct posix_acl *acl;
|
|
|
|
if (S_ISLNK(inode->i_mode))
|
|
return -EOPNOTSUPP;
|
|
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
|
|
if (acl) {
|
|
retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
|
|
if (retval)
|
|
return retval;
|
|
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
|
retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
|
posix_acl_release(acl);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid,
|
|
struct posix_acl *dacl, struct posix_acl *acl)
|
|
{
|
|
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
|
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
|
v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl);
|
|
v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
|
return 0;
|
|
}
|
|
|
|
void v9fs_put_acl(struct posix_acl *dacl,
|
|
struct posix_acl *acl)
|
|
{
|
|
posix_acl_release(dacl);
|
|
posix_acl_release(acl);
|
|
}
|
|
|
|
int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
|
struct posix_acl **dpacl, struct posix_acl **pacl)
|
|
{
|
|
int retval = 0;
|
|
umode_t mode = *modep;
|
|
struct posix_acl *acl = NULL;
|
|
|
|
if (!S_ISLNK(mode)) {
|
|
acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT);
|
|
if (IS_ERR(acl))
|
|
return PTR_ERR(acl);
|
|
if (!acl)
|
|
mode &= ~current_umask();
|
|
}
|
|
if (acl) {
|
|
if (S_ISDIR(mode))
|
|
*dpacl = posix_acl_dup(acl);
|
|
retval = __posix_acl_create(&acl, GFP_NOFS, &mode);
|
|
if (retval < 0)
|
|
return retval;
|
|
if (retval > 0)
|
|
*pacl = acl;
|
|
else
|
|
posix_acl_release(acl);
|
|
}
|
|
*modep = mode;
|
|
return 0;
|
|
}
|