Commit edadd0e5 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse updates from Miklos Szeredi:
 "This adds POSIX ACL permission checking to the fuse kernel module.

  In addition there are minor bug fixes as well as cleanups"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: limit xattr returned size
  fuse: remove duplicate cs->offset assignment
  fuse: don't use fuse_ioctl_copy_user() helper
  fuse_ioctl_copy_user(): don't open-code copy_page_{to,from}_iter()
  fuse: get rid of fc->flags
  fuse: use timespec64
  fuse: don't use ->d_time
  fuse: Add posix ACL support
  fuse: handle killpriv in userspace fs
  fuse: fix killing s[ug]id in setattr
  fuse: invalidate dir dentry after chmod
  fuse: Use generic xattr ops
  fuse: listxattr: verify xattr list
parents 3fb75cb8 63401ccd
config FUSE_FS config FUSE_FS
tristate "FUSE (Filesystem in Userspace) support" tristate "FUSE (Filesystem in Userspace) support"
select FS_POSIX_ACL
help help
With FUSE it is possible to implement a fully functional filesystem With FUSE it is possible to implement a fully functional filesystem
in a userspace program. in a userspace program.
......
...@@ -5,4 +5,4 @@ ...@@ -5,4 +5,4 @@
obj-$(CONFIG_FUSE_FS) += fuse.o obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.o obj-$(CONFIG_CUSE) += cuse.o
fuse-objs := dev.o dir.o file.o inode.o control.o fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o
/*
* FUSE: Filesystem in Userspace
* Copyright (C) 2016 Canonical Ltd. <seth.forshee@canonical.com>
*
* This program can be distributed under the terms of the GNU GPL.
* See the file COPYING.
*/
#include "fuse_i.h"
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
struct posix_acl *fuse_get_acl(struct inode *inode, int type)
{
struct fuse_conn *fc = get_fuse_conn(inode);
int size;
const char *name;
void *value = NULL;
struct posix_acl *acl;
if (!fc->posix_acl || fc->no_getxattr)
return NULL;
if (type == ACL_TYPE_ACCESS)
name = XATTR_NAME_POSIX_ACL_ACCESS;
else if (type == ACL_TYPE_DEFAULT)
name = XATTR_NAME_POSIX_ACL_DEFAULT;
else
return ERR_PTR(-EOPNOTSUPP);
value = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!value)
return ERR_PTR(-ENOMEM);
size = fuse_getxattr(inode, name, value, PAGE_SIZE);
if (size > 0)
acl = posix_acl_from_xattr(&init_user_ns, value, size);
else if ((size == 0) || (size == -ENODATA) ||
(size == -EOPNOTSUPP && fc->no_getxattr))
acl = NULL;
else if (size == -ERANGE)
acl = ERR_PTR(-E2BIG);
else
acl = ERR_PTR(size);
kfree(value);
return acl;
}
int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
struct fuse_conn *fc = get_fuse_conn(inode);
const char *name;
int ret;
if (!fc->posix_acl || fc->no_setxattr)
return -EOPNOTSUPP;
if (type == ACL_TYPE_ACCESS)
name = XATTR_NAME_POSIX_ACL_ACCESS;
else if (type == ACL_TYPE_DEFAULT)
name = XATTR_NAME_POSIX_ACL_DEFAULT;
else
return -EINVAL;
if (acl) {
/*
* Fuse userspace is responsible for updating access
* permissions in the inode, if needed. fuse_setxattr
* invalidates the inode attributes, which will force
* them to be refreshed the next time they are used,
* and it also updates i_ctime.
*/
size_t size = posix_acl_xattr_size(acl->a_count);
void *value;
if (size > PAGE_SIZE)
return -E2BIG;
value = kmalloc(size, GFP_KERNEL);
if (!value)
return -ENOMEM;
ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
if (ret < 0) {
kfree(value);
return ret;
}
ret = fuse_setxattr(inode, name, value, size, 0);
kfree(value);
} else {
ret = fuse_removexattr(inode, name);
}
forget_all_cached_acls(inode);
fuse_invalidate_attr(inode);
return ret;
}
...@@ -767,7 +767,6 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) ...@@ -767,7 +767,6 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
cs->len = err; cs->len = err;
cs->offset = off; cs->offset = off;
cs->pg = page; cs->pg = page;
cs->offset = off;
iov_iter_advance(cs->iter, err); iov_iter_advance(cs->iter, err);
} }
......
This diff is collapsed.
...@@ -2326,49 +2326,6 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence) ...@@ -2326,49 +2326,6 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence)
return retval; return retval;
} }
static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
unsigned int nr_segs, size_t bytes, bool to_user)
{
struct iov_iter ii;
int page_idx = 0;
if (!bytes)
return 0;
iov_iter_init(&ii, to_user ? READ : WRITE, iov, nr_segs, bytes);
while (iov_iter_count(&ii)) {
struct page *page = pages[page_idx++];
size_t todo = min_t(size_t, PAGE_SIZE, iov_iter_count(&ii));
void *kaddr;
kaddr = kmap(page);
while (todo) {
char __user *uaddr = ii.iov->iov_base + ii.iov_offset;
size_t iov_len = ii.iov->iov_len - ii.iov_offset;
size_t copy = min(todo, iov_len);
size_t left;
if (!to_user)
left = copy_from_user(kaddr, uaddr, copy);
else
left = copy_to_user(uaddr, kaddr, copy);
if (unlikely(left))
return -EFAULT;
iov_iter_advance(&ii, copy);
todo -= copy;
kaddr += copy;
}
kunmap(page);
}
return 0;
}
/* /*
* CUSE servers compiled on 32bit broke on 64bit kernels because the * CUSE servers compiled on 32bit broke on 64bit kernels because the
* ABI was defined to be 'struct iovec' which is different on 32bit * ABI was defined to be 'struct iovec' which is different on 32bit
...@@ -2520,8 +2477,9 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, ...@@ -2520,8 +2477,9 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
struct iovec *iov_page = NULL; struct iovec *iov_page = NULL;
struct iovec *in_iov = NULL, *out_iov = NULL; struct iovec *in_iov = NULL, *out_iov = NULL;
unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages; unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages;
size_t in_size, out_size, transferred; size_t in_size, out_size, transferred, c;
int err; int err, i;
struct iov_iter ii;
#if BITS_PER_LONG == 32 #if BITS_PER_LONG == 32
inarg.flags |= FUSE_IOCTL_32BIT; inarg.flags |= FUSE_IOCTL_32BIT;
...@@ -2603,10 +2561,13 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, ...@@ -2603,10 +2561,13 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
req->in.args[1].size = in_size; req->in.args[1].size = in_size;
req->in.argpages = 1; req->in.argpages = 1;
err = fuse_ioctl_copy_user(pages, in_iov, in_iovs, in_size, err = -EFAULT;
false); iov_iter_init(&ii, WRITE, in_iov, in_iovs, in_size);
if (err) for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= num_pages); i++) {
goto out; c = copy_page_from_iter(pages[i], 0, PAGE_SIZE, &ii);
if (c != PAGE_SIZE && iov_iter_count(&ii))
goto out;
}
} }
req->out.numargs = 2; req->out.numargs = 2;
...@@ -2672,7 +2633,14 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, ...@@ -2672,7 +2633,14 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
if (transferred > inarg.out_size) if (transferred > inarg.out_size)
goto out; goto out;
err = fuse_ioctl_copy_user(pages, out_iov, out_iovs, transferred, true); err = -EFAULT;
iov_iter_init(&ii, READ, out_iov, out_iovs, transferred);
for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= num_pages); i++) {
c = copy_page_to_iter(pages[i], 0, PAGE_SIZE, &ii);
if (c != PAGE_SIZE && iov_iter_count(&ii))
goto out;
}
err = 0;
out: out:
if (req) if (req)
fuse_put_request(fc, req); fuse_put_request(fc, req);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/xattr.h>
/** Max number of pages that can be used in a single read request */ /** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32 #define FUSE_MAX_PAGES_PER_REQ 32
...@@ -36,15 +37,6 @@ ...@@ -36,15 +37,6 @@
/** Number of dentries for each connection in the control filesystem */ /** Number of dentries for each connection in the control filesystem */
#define FUSE_CTL_NUM_DENTRIES 5 #define FUSE_CTL_NUM_DENTRIES 5
/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
module will check permissions based on the file mode. Otherwise no
permission checking is done in the kernel */
#define FUSE_DEFAULT_PERMISSIONS (1 << 0)
/** If the FUSE_ALLOW_OTHER flag is given, then not only the user
doing the mount will be allowed to access the filesystem */
#define FUSE_ALLOW_OTHER (1 << 1)
/** Number of page pointers embedded in fuse_req */ /** Number of page pointers embedded in fuse_req */
#define FUSE_REQ_INLINE_PAGES 1 #define FUSE_REQ_INLINE_PAGES 1
...@@ -469,9 +461,6 @@ struct fuse_conn { ...@@ -469,9 +461,6 @@ struct fuse_conn {
/** The group id for this mount */ /** The group id for this mount */
kgid_t group_id; kgid_t group_id;
/** The fuse mount flags for this mount */
unsigned flags;
/** Maximum read size */ /** Maximum read size */
unsigned max_read; unsigned max_read;
...@@ -547,6 +536,9 @@ struct fuse_conn { ...@@ -547,6 +536,9 @@ struct fuse_conn {
/** allow parallel lookups and readdir (default is serialized) */ /** allow parallel lookups and readdir (default is serialized) */
unsigned parallel_dirops:1; unsigned parallel_dirops:1;
/** handle fs handles killing suid/sgid/cap on write/chown/trunc */
unsigned handle_killpriv:1;
/* /*
* The following bitfields are only for optimization purposes * The following bitfields are only for optimization purposes
* and hence races in setting them will not cause malfunction * and hence races in setting them will not cause malfunction
...@@ -624,6 +616,15 @@ struct fuse_conn { ...@@ -624,6 +616,15 @@ struct fuse_conn {
/** Is lseek not implemented by fs? */ /** Is lseek not implemented by fs? */
unsigned no_lseek:1; unsigned no_lseek:1;
/** Does the filesystem support posix acls? */
unsigned posix_acl:1;
/** Check permissions based on the file mode or not? */
unsigned default_permissions:1;
/** Allow other than the mounter user to access the filesystem ? */
unsigned allow_other:1;
/** The number of requests waiting for completion */ /** The number of requests waiting for completion */
atomic_t num_waiting; atomic_t num_waiting;
...@@ -902,6 +903,8 @@ int fuse_allow_current_process(struct fuse_conn *fc); ...@@ -902,6 +903,8 @@ int fuse_allow_current_process(struct fuse_conn *fc);
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
void fuse_update_ctime(struct inode *inode);
int fuse_update_attributes(struct inode *inode, struct kstat *stat, int fuse_update_attributes(struct inode *inode, struct kstat *stat,
struct file *file, bool *refreshed); struct file *file, bool *refreshed);
...@@ -966,4 +969,17 @@ void fuse_set_initialized(struct fuse_conn *fc); ...@@ -966,4 +969,17 @@ void fuse_set_initialized(struct fuse_conn *fc);
void fuse_unlock_inode(struct inode *inode); void fuse_unlock_inode(struct inode *inode);
void fuse_lock_inode(struct inode *inode); void fuse_lock_inode(struct inode *inode);
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
size_t size, int flags);
ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
size_t size);
ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
int fuse_removexattr(struct inode *inode, const char *name);
extern const struct xattr_handler *fuse_xattr_handlers[];
extern const struct xattr_handler *fuse_acl_xattr_handlers[];
struct posix_acl;
struct posix_acl *fuse_get_acl(struct inode *inode, int type);
int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
#endif /* _FS_FUSE_I_H */ #endif /* _FS_FUSE_I_H */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/posix_acl.h>
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
MODULE_DESCRIPTION("Filesystem in Userspace"); MODULE_DESCRIPTION("Filesystem in Userspace");
...@@ -66,7 +67,8 @@ struct fuse_mount_data { ...@@ -66,7 +67,8 @@ struct fuse_mount_data {
unsigned rootmode_present:1; unsigned rootmode_present:1;
unsigned user_id_present:1; unsigned user_id_present:1;
unsigned group_id_present:1; unsigned group_id_present:1;
unsigned flags; unsigned default_permissions:1;
unsigned allow_other:1;
unsigned max_read; unsigned max_read;
unsigned blksize; unsigned blksize;
}; };
...@@ -192,7 +194,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, ...@@ -192,7 +194,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
* check in may_delete(). * check in may_delete().
*/ */
fi->orig_i_mode = inode->i_mode; fi->orig_i_mode = inode->i_mode;
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) if (!fc->default_permissions)
inode->i_mode &= ~S_ISVTX; inode->i_mode &= ~S_ISVTX;
fi->orig_ino = attr->ino; fi->orig_ino = attr->ino;
...@@ -340,6 +342,7 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid, ...@@ -340,6 +342,7 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
return -ENOENT; return -ENOENT;
fuse_invalidate_attr(inode); fuse_invalidate_attr(inode);
forget_all_cached_acls(inode);
if (offset >= 0) { if (offset >= 0) {
pg_start = offset >> PAGE_SHIFT; pg_start = offset >> PAGE_SHIFT;
if (len <= 0) if (len <= 0)
...@@ -532,11 +535,11 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) ...@@ -532,11 +535,11 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)
break; break;
case OPT_DEFAULT_PERMISSIONS: case OPT_DEFAULT_PERMISSIONS:
d->flags |= FUSE_DEFAULT_PERMISSIONS; d->default_permissions = 1;
break; break;
case OPT_ALLOW_OTHER: case OPT_ALLOW_OTHER:
d->flags |= FUSE_ALLOW_OTHER; d->allow_other = 1;
break; break;
case OPT_MAX_READ: case OPT_MAX_READ:
...@@ -570,9 +573,9 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root) ...@@ -570,9 +573,9 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
seq_printf(m, ",user_id=%u", from_kuid_munged(&init_user_ns, fc->user_id)); seq_printf(m, ",user_id=%u", from_kuid_munged(&init_user_ns, fc->user_id));
seq_printf(m, ",group_id=%u", from_kgid_munged(&init_user_ns, fc->group_id)); seq_printf(m, ",group_id=%u", from_kgid_munged(&init_user_ns, fc->group_id));
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) if (fc->default_permissions)
seq_puts(m, ",default_permissions"); seq_puts(m, ",default_permissions");
if (fc->flags & FUSE_ALLOW_OTHER) if (fc->allow_other)
seq_puts(m, ",allow_other"); seq_puts(m, ",allow_other");
if (fc->max_read != ~0) if (fc->max_read != ~0)
seq_printf(m, ",max_read=%u", fc->max_read); seq_printf(m, ",max_read=%u", fc->max_read);
...@@ -910,8 +913,15 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) ...@@ -910,8 +913,15 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fc->writeback_cache = 1; fc->writeback_cache = 1;
if (arg->flags & FUSE_PARALLEL_DIROPS) if (arg->flags & FUSE_PARALLEL_DIROPS)
fc->parallel_dirops = 1; fc->parallel_dirops = 1;
if (arg->flags & FUSE_HANDLE_KILLPRIV)
fc->handle_killpriv = 1;
if (arg->time_gran && arg->time_gran <= 1000000000) if (arg->time_gran && arg->time_gran <= 1000000000)
fc->sb->s_time_gran = arg->time_gran; fc->sb->s_time_gran = arg->time_gran;
if ((arg->flags & FUSE_POSIX_ACL)) {
fc->default_permissions = 1;
fc->posix_acl = 1;
fc->sb->s_xattr = fuse_acl_xattr_handlers;
}
} else { } else {
ra_pages = fc->max_read / PAGE_SIZE; ra_pages = fc->max_read / PAGE_SIZE;
fc->no_lock = 1; fc->no_lock = 1;
...@@ -941,7 +951,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) ...@@ -941,7 +951,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
FUSE_PARALLEL_DIROPS; FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL;
req->in.h.opcode = FUSE_INIT; req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1; req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg); req->in.args[0].size = sizeof(*arg);
...@@ -1071,6 +1081,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1071,6 +1081,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
} }
sb->s_magic = FUSE_SUPER_MAGIC; sb->s_magic = FUSE_SUPER_MAGIC;
sb->s_op = &fuse_super_operations; sb->s_op = &fuse_super_operations;
sb->s_xattr = fuse_xattr_handlers;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_time_gran = 1; sb->s_time_gran = 1;
sb->s_export_op = &fuse_export_operations; sb->s_export_op = &fuse_export_operations;
...@@ -1109,7 +1120,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1109,7 +1120,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
fc->dont_mask = 1; fc->dont_mask = 1;
sb->s_flags |= MS_POSIXACL; sb->s_flags |= MS_POSIXACL;
fc->flags = d.flags; fc->default_permissions = d.default_permissions;
fc->allow_other = d.allow_other;
fc->user_id = d.user_id; fc->user_id = d.user_id;
fc->group_id = d.group_id; fc->group_id = d.group_id;
fc->max_read = max_t(unsigned, 4096, d.max_read); fc->max_read = max_t(unsigned, 4096, d.max_read);
......
/*
* 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 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
};
...@@ -108,6 +108,10 @@ ...@@ -108,6 +108,10 @@
* *
* 7.25 * 7.25
* - add FUSE_PARALLEL_DIROPS * - add FUSE_PARALLEL_DIROPS
*
* 7.26
* - add FUSE_HANDLE_KILLPRIV
* - add FUSE_POSIX_ACL
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
...@@ -143,7 +147,7 @@ ...@@ -143,7 +147,7 @@
#define FUSE_KERNEL_VERSION 7 #define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */ /** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 25 #define FUSE_KERNEL_MINOR_VERSION 26
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
...@@ -238,6 +242,8 @@ struct fuse_file_lock { ...@@ -238,6 +242,8 @@ struct fuse_file_lock {
* FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes
* FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens
* FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir
* FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc
* FUSE_POSIX_ACL: filesystem supports posix acls
*/ */
#define FUSE_ASYNC_READ (1 << 0) #define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_POSIX_LOCKS (1 << 1)
...@@ -258,6 +264,8 @@ struct fuse_file_lock { ...@@ -258,6 +264,8 @@ struct fuse_file_lock {
#define FUSE_WRITEBACK_CACHE (1 << 16) #define FUSE_WRITEBACK_CACHE (1 << 16)
#define FUSE_NO_OPEN_SUPPORT (1 << 17) #define FUSE_NO_OPEN_SUPPORT (1 << 17)
#define FUSE_PARALLEL_DIROPS (1 << 18) #define FUSE_PARALLEL_DIROPS (1 << 18)
#define FUSE_HANDLE_KILLPRIV (1 << 19)
#define FUSE_POSIX_ACL (1 << 20)
/** /**
* CUSE INIT request/reply flags * CUSE INIT request/reply flags
......
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