mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
![Eric W. Biederman](/assets/img/avatar_default.png)
Ensure the translation happens by failing to read or write posix acls when the filesystem has not indicated it supports posix acls. This ensures that modern cached posix acl support is available and used when dealing with posix acls. This is important because only that path has the code to convernt the uids and gids in posix acls into the user namespace of a fuse filesystem. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
254 lines
5.9 KiB
C
254 lines
5.9 KiB
C
/*
|
|
* FUSE: Filesystem in Userspace
|
|
* Copyright (C) 2001-2016 Miklos Szeredi <miklos@szeredi.hu>
|
|
*
|
|
* This program can be distributed under the terms of the GNU GPL.
|
|
* See the file COPYING.
|
|
*/
|
|
|
|
#include "fuse_i.h"
|
|
|
|
#include <linux/xattr.h>
|
|
#include <linux/posix_acl_xattr.h>
|
|
|
|
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
|
size_t size, int flags)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
FUSE_ARGS(args);
|
|
struct fuse_setxattr_in inarg;
|
|
int err;
|
|
|
|
if (fc->no_setxattr)
|
|
return -EOPNOTSUPP;
|
|
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
inarg.size = size;
|
|
inarg.flags = flags;
|
|
args.in.h.opcode = FUSE_SETXATTR;
|
|
args.in.h.nodeid = get_node_id(inode);
|
|
args.in.numargs = 3;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
args.in.args[1].size = strlen(name) + 1;
|
|
args.in.args[1].value = name;
|
|
args.in.args[2].size = size;
|
|
args.in.args[2].value = value;
|
|
err = fuse_simple_request(fc, &args);
|
|
if (err == -ENOSYS) {
|
|
fc->no_setxattr = 1;
|
|
err = -EOPNOTSUPP;
|
|
}
|
|
if (!err) {
|
|
fuse_invalidate_attr(inode);
|
|
fuse_update_ctime(inode);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
|
|
size_t size)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
FUSE_ARGS(args);
|
|
struct fuse_getxattr_in inarg;
|
|
struct fuse_getxattr_out outarg;
|
|
ssize_t ret;
|
|
|
|
if (fc->no_getxattr)
|
|
return -EOPNOTSUPP;
|
|
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
inarg.size = size;
|
|
args.in.h.opcode = FUSE_GETXATTR;
|
|
args.in.h.nodeid = get_node_id(inode);
|
|
args.in.numargs = 2;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
args.in.args[1].size = strlen(name) + 1;
|
|
args.in.args[1].value = name;
|
|
/* This is really two different operations rolled into one */
|
|
args.out.numargs = 1;
|
|
if (size) {
|
|
args.out.argvar = 1;
|
|
args.out.args[0].size = size;
|
|
args.out.args[0].value = value;
|
|
} else {
|
|
args.out.args[0].size = sizeof(outarg);
|
|
args.out.args[0].value = &outarg;
|
|
}
|
|
ret = fuse_simple_request(fc, &args);
|
|
if (!ret && !size)
|
|
ret = min_t(ssize_t, outarg.size, XATTR_SIZE_MAX);
|
|
if (ret == -ENOSYS) {
|
|
fc->no_getxattr = 1;
|
|
ret = -EOPNOTSUPP;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_verify_xattr_list(char *list, size_t size)
|
|
{
|
|
size_t origsize = size;
|
|
|
|
while (size) {
|
|
size_t thislen = strnlen(list, size);
|
|
|
|
if (!thislen || thislen == size)
|
|
return -EIO;
|
|
|
|
size -= thislen + 1;
|
|
list += thislen + 1;
|
|
}
|
|
|
|
return origsize;
|
|
}
|
|
|
|
ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
|
|
{
|
|
struct inode *inode = d_inode(entry);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
FUSE_ARGS(args);
|
|
struct fuse_getxattr_in inarg;
|
|
struct fuse_getxattr_out outarg;
|
|
ssize_t ret;
|
|
|
|
if (!fuse_allow_current_process(fc))
|
|
return -EACCES;
|
|
|
|
if (fc->no_listxattr)
|
|
return -EOPNOTSUPP;
|
|
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
inarg.size = size;
|
|
args.in.h.opcode = FUSE_LISTXATTR;
|
|
args.in.h.nodeid = get_node_id(inode);
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
/* This is really two different operations rolled into one */
|
|
args.out.numargs = 1;
|
|
if (size) {
|
|
args.out.argvar = 1;
|
|
args.out.args[0].size = size;
|
|
args.out.args[0].value = list;
|
|
} else {
|
|
args.out.args[0].size = sizeof(outarg);
|
|
args.out.args[0].value = &outarg;
|
|
}
|
|
ret = fuse_simple_request(fc, &args);
|
|
if (!ret && !size)
|
|
ret = min_t(ssize_t, outarg.size, XATTR_LIST_MAX);
|
|
if (ret > 0 && size)
|
|
ret = fuse_verify_xattr_list(list, ret);
|
|
if (ret == -ENOSYS) {
|
|
fc->no_listxattr = 1;
|
|
ret = -EOPNOTSUPP;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int fuse_removexattr(struct inode *inode, const char *name)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
FUSE_ARGS(args);
|
|
int err;
|
|
|
|
if (fc->no_removexattr)
|
|
return -EOPNOTSUPP;
|
|
|
|
args.in.h.opcode = FUSE_REMOVEXATTR;
|
|
args.in.h.nodeid = get_node_id(inode);
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = strlen(name) + 1;
|
|
args.in.args[0].value = name;
|
|
err = fuse_simple_request(fc, &args);
|
|
if (err == -ENOSYS) {
|
|
fc->no_removexattr = 1;
|
|
err = -EOPNOTSUPP;
|
|
}
|
|
if (!err) {
|
|
fuse_invalidate_attr(inode);
|
|
fuse_update_ctime(inode);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int fuse_xattr_get(const struct xattr_handler *handler,
|
|
struct dentry *dentry, struct inode *inode,
|
|
const char *name, void *value, size_t size)
|
|
{
|
|
return fuse_getxattr(inode, name, value, size);
|
|
}
|
|
|
|
static int fuse_xattr_set(const struct xattr_handler *handler,
|
|
struct dentry *dentry, struct inode *inode,
|
|
const char *name, const void *value, size_t size,
|
|
int flags)
|
|
{
|
|
if (!value)
|
|
return fuse_removexattr(inode, name);
|
|
|
|
return fuse_setxattr(inode, name, value, size, flags);
|
|
}
|
|
|
|
static bool no_xattr_list(struct dentry *dentry)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static int no_xattr_get(const struct xattr_handler *handler,
|
|
struct dentry *dentry, struct inode *inode,
|
|
const char *name, void *value, size_t size)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int no_xattr_set(const struct xattr_handler *handler,
|
|
struct dentry *dentry, struct inode *nodee,
|
|
const char *name, const void *value,
|
|
size_t size, int flags)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static const struct xattr_handler fuse_xattr_handler = {
|
|
.prefix = "",
|
|
.get = fuse_xattr_get,
|
|
.set = fuse_xattr_set,
|
|
};
|
|
|
|
const struct xattr_handler *fuse_xattr_handlers[] = {
|
|
&fuse_xattr_handler,
|
|
NULL
|
|
};
|
|
|
|
const struct xattr_handler *fuse_acl_xattr_handlers[] = {
|
|
&posix_acl_access_xattr_handler,
|
|
&posix_acl_default_xattr_handler,
|
|
&fuse_xattr_handler,
|
|
NULL
|
|
};
|
|
|
|
static const struct xattr_handler fuse_no_acl_access_xattr_handler = {
|
|
.name = XATTR_NAME_POSIX_ACL_ACCESS,
|
|
.flags = ACL_TYPE_ACCESS,
|
|
.list = no_xattr_list,
|
|
.get = no_xattr_get,
|
|
.set = no_xattr_set,
|
|
};
|
|
|
|
static const struct xattr_handler fuse_no_acl_default_xattr_handler = {
|
|
.name = XATTR_NAME_POSIX_ACL_DEFAULT,
|
|
.flags = ACL_TYPE_ACCESS,
|
|
.list = no_xattr_list,
|
|
.get = no_xattr_get,
|
|
.set = no_xattr_set,
|
|
};
|
|
|
|
const struct xattr_handler *fuse_no_acl_xattr_handlers[] = {
|
|
&fuse_no_acl_access_xattr_handler,
|
|
&fuse_no_acl_default_xattr_handler,
|
|
&fuse_xattr_handler,
|
|
NULL
|
|
};
|