From: Seth Forshee <seth.forshee@canonical.com>
To: fuse-devel@lists.sourceforge.net, linux-fsdevel@vger.kernel.org
Cc: "Miklos Szeredi" <miklos@szeredi.hu>,
"Eric W. Biederman" <ebiederm@xmission.com>,
"Michael j Theall" <mtheall@us.ibm.com>,
"Jean-Pierre André" <jean-pierre.andre@wanadoo.fr>,
"Nikolaus Rath" <Nikolaus@rath.org>,
"Seth Forshee" <seth.forshee@canonical.com>
Subject: [RFC v3 2/2] fuse: Add posix acl support
Date: Mon, 1 Aug 2016 16:27:26 -0500 [thread overview]
Message-ID: <1470086846-19844-3-git-send-email-seth.forshee@canonical.com> (raw)
In-Reply-To: <1470086846-19844-1-git-send-email-seth.forshee@canonical.com>
Support posix acls in fuse only when CONFIG_FUSE_FS_POSIX_ACL=y
and default_permissions is used for the filesystem. When
default_permissions is not used fuse cannot meaninfully support
cals, as fuse_permission() only sends FUSE_PERMISSION from the
access, faccessat, chdir, fchdir, and chroot system calls.
Therefore whenever CONFIG_FUSE_FS_POSIX_ACL=n or
default_permissions is not used fuse will return -EOPNOTSUPP
whenever posix acl xattrs are read or written.
XXX: Default acls are currently broken for files created via
atomic_open.
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
---
fs/fuse/Kconfig | 13 ++++
fs/fuse/Makefile | 2 +-
fs/fuse/acl.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/fuse/dir.c | 28 +++++++-
fs/fuse/fuse_i.h | 29 ++++++++
fs/fuse/inode.c | 2 +
fs/fuse/xattr.c | 13 ++--
7 files changed, 279 insertions(+), 8 deletions(-)
create mode 100644 fs/fuse/acl.c
diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig
index 1b2f6c2c3aaf..5d4ebb0cc0dc 100644
--- a/fs/fuse/Kconfig
+++ b/fs/fuse/Kconfig
@@ -16,6 +16,19 @@ config FUSE_FS
If you want to develop a userspace FS, or if you want to use
a filesystem based on FUSE, answer Y or M.
+config FUSE_FS_POSIX_ACL
+ bool "Fuse POSIX Access Control Lists"
+ depends on FUSE_FS
+ select FS_POSIX_ACL
+ help
+ POSIX Access Control Lists (ACLs) support permissions for users and
+ groups beyond the owner/group/world scheme.
+
+ To learn more about Access Control Lists, visit the POSIX ACLs for
+ Linux website <http://acl.bestbits.at/>.
+
+ If you don't know what Access Control Lists are, say N
+
config CUSE
tristate "Character device in Userspace support"
depends on FUSE_FS
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index 448aa27ada00..60da84a86dab 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -5,4 +5,4 @@
obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.o
-fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o
+fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o
diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c
new file mode 100644
index 000000000000..c0f412dfe9a7
--- /dev/null
+++ b/fs/fuse/acl.c
@@ -0,0 +1,200 @@
+/*
+ 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>
+
+#ifdef CONFIG_FUSE_FS_POSIX_ACL
+
+static int fuse_xattr_acl_get(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *inode,
+ const char *name, void *value, size_t size)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
+ if (handler->flags == ACL_TYPE_ACCESS)
+ return posix_acl_access_xattr_handler.get(
+ &posix_acl_access_xattr_handler,
+ dentry, inode, name, value, size);
+ if (handler->flags == ACL_TYPE_DEFAULT)
+ return posix_acl_default_xattr_handler.get(
+ &posix_acl_default_xattr_handler,
+ dentry, inode, name, value, size);
+ }
+ return -EOPNOTSUPP;
+}
+
+static int fuse_xattr_acl_set(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *inode,
+ const char *name, const void *value, size_t size,
+ int flags)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
+ if (handler->flags == ACL_TYPE_ACCESS)
+ return posix_acl_access_xattr_handler.set(
+ &posix_acl_access_xattr_handler,
+ dentry, inode, name, value, size,
+ flags);
+ if (handler->flags == ACL_TYPE_DEFAULT)
+ return posix_acl_default_xattr_handler.set(
+ &posix_acl_default_xattr_handler,
+ dentry, inode, name, value, size,
+ flags);
+ }
+ return -EOPNOTSUPP;
+}
+
+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->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);
+
+ size = fuse_getxattr(inode, name, NULL, 0);
+ if (size > 0) {
+ value = kzalloc(size, GFP_KERNEL);
+ if (!value)
+ return ERR_PTR(-ENOMEM);
+ size = fuse_getxattr(inode, name, value, 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 {
+ 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->no_setxattr)
+ return -EOPNOTSUPP;
+
+ if (type == ACL_TYPE_ACCESS) {
+ struct iattr attr;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
+ attr.ia_mode = inode->i_mode;
+ ret = posix_acl_equiv_mode(acl, &attr.ia_mode);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ acl = NULL;
+ if (inode->i_mode != attr.ia_mode) {
+ attr.ia_valid = ATTR_MODE | ATTR_CTIME;
+ attr.ia_ctime = current_fs_time(inode->i_sb);
+ ret = fuse_do_setattr(inode, &attr, NULL);
+ if (ret)
+ return ret;
+ }
+ } else if (type == ACL_TYPE_DEFAULT) {
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
+ } else {
+ return -EINVAL;
+ }
+
+ if (acl) {
+ size_t size = posix_acl_xattr_size(acl->a_count);
+ void *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);
+ }
+ if (ret == 0)
+ set_cached_acl(inode, type, acl);
+ return ret;
+}
+
+int fuse_init_acl(struct inode *inode, struct inode *dir)
+{
+ struct posix_acl *default_acl, *acl;
+ int err;
+
+ err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
+ if (err)
+ return err;
+
+ if (default_acl) {
+ err = fuse_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
+ posix_acl_release(default_acl);
+ }
+ if (acl) {
+ if (!err)
+ err = fuse_set_acl(inode, acl, ACL_TYPE_ACCESS);
+ posix_acl_release(default_acl);
+ }
+ return err;
+}
+
+#else /* !CONFIG_FUSE_FS_POSIX_ACL */
+
+static int fuse_xattr_acl_get(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *inode,
+ const char *name, void *value, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
+static int fuse_xattr_acl_set(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *inode,
+ const char *name, const void *value, size_t size,
+ int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_FUSE_FS_POSIX_ACL */
+
+const struct xattr_handler fuse_xattr_acl_access_handler = {
+ .name = XATTR_NAME_POSIX_ACL_ACCESS,
+ .flags = ACL_TYPE_ACCESS,
+ .get = fuse_xattr_acl_get,
+ .set = fuse_xattr_acl_set,
+};
+
+const struct xattr_handler fuse_xattr_acl_default_handler = {
+ .name = XATTR_NAME_POSIX_ACL_DEFAULT,
+ .flags = ACL_TYPE_DEFAULT,
+ .get = fuse_xattr_acl_get,
+ .set = fuse_xattr_acl_set,
+};
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 9df55b8e776a..ca7d573f3121 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -14,6 +14,7 @@
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/xattr.h>
+#include <linux/posix_acl.h>
static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
{
@@ -244,6 +245,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
goto invalid;
+ forget_all_cached_acls(inode);
fuse_change_attributes(inode, &outarg.attr,
entry_attr_timeout(&outarg),
attr_version);
@@ -554,6 +556,14 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
}
kfree(forget);
+ if (args->in.h.opcode != FUSE_LINK) {
+ err = fuse_init_acl(inode, dir);
+ if (err) {
+ iput(inode);
+ return err;
+ }
+ }
+
err = d_instantiate_no_diralias(entry, inode);
if (err)
return err;
@@ -916,6 +926,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
if (time_before64(fi->i_time, get_jiffies_64())) {
r = true;
+ forget_all_cached_acls(inode);
err = fuse_do_getattr(inode, stat, file);
} else {
r = false;
@@ -1062,6 +1073,7 @@ static int fuse_perm_getattr(struct inode *inode, int mask)
if (mask & MAY_NOT_BLOCK)
return -ECHILD;
+ forget_all_cached_acls(inode);
return fuse_do_getattr(inode, NULL, NULL);
}
@@ -1231,6 +1243,7 @@ retry:
fi->nlookup++;
spin_unlock(&fc->lock);
+ forget_all_cached_acls(inode);
fuse_change_attributes(inode, &o->attr,
entry_attr_timeout(o),
attr_version);
@@ -1698,14 +1711,19 @@ error:
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
{
struct inode *inode = d_inode(entry);
+ int ret;
if (!fuse_allow_current_process(get_fuse_conn(inode)))
return -EACCES;
if (attr->ia_valid & ATTR_FILE)
- return fuse_do_setattr(inode, attr, attr->ia_file);
+ ret = fuse_do_setattr(inode, attr, attr->ia_file);
else
- return fuse_do_setattr(inode, attr, NULL);
+ ret = fuse_do_setattr(inode, attr, NULL);
+
+ if (!ret && (attr->ia_valid & ATTR_MODE))
+ ret = posix_acl_chmod(inode, inode->i_mode);
+ return ret;
}
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
@@ -1738,6 +1756,8 @@ static const struct inode_operations fuse_dir_inode_operations = {
.getxattr = generic_getxattr,
.listxattr = fuse_listxattr,
.removexattr = generic_removexattr,
+ .get_acl = fuse_get_acl,
+ .set_acl = fuse_set_acl,
};
static const struct file_operations fuse_dir_operations = {
@@ -1759,6 +1779,8 @@ static const struct inode_operations fuse_common_inode_operations = {
.getxattr = generic_getxattr,
.listxattr = fuse_listxattr,
.removexattr = generic_removexattr,
+ .get_acl = fuse_get_acl,
+ .set_acl = fuse_set_acl,
};
static const struct inode_operations fuse_symlink_inode_operations = {
@@ -1770,6 +1792,8 @@ static const struct inode_operations fuse_symlink_inode_operations = {
.getxattr = generic_getxattr,
.listxattr = fuse_listxattr,
.removexattr = generic_removexattr,
+ .get_acl = fuse_get_acl,
+ .set_acl = fuse_set_acl,
};
void fuse_init_common(struct inode *inode)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 93a5e8191da1..c1a630bb2b21 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -25,6 +25,7 @@
#include <linux/kref.h>
#include <linux/pid_namespace.h>
#include <linux/user_namespace.h>
+#include <linux/xattr.h>
/** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32
@@ -966,7 +967,35 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
void fuse_set_initialized(struct fuse_conn *fc);
+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[];
+struct posix_acl;
+
+extern const struct xattr_handler fuse_xattr_acl_access_handler;
+extern const struct xattr_handler fuse_xattr_acl_default_handler;
+
+#ifdef CONFIG_FUSE_FS_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);
+int fuse_init_acl(struct inode *inode, struct inode *dir);
+
+#else
+
+#define fuse_get_acl NULL
+#define fuse_set_acl NULL
+
+static inline int fuse_init_acl(struct inode *inode, struct inode *dir)
+{
+ return 0;
+}
+
+#endif
+
#endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index fee06e48157d..9c1519c269bb 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -21,6 +21,7 @@
#include <linux/sched.h>
#include <linux/exportfs.h>
#include <linux/pid_namespace.h>
+#include <linux/posix_acl.h>
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
MODULE_DESCRIPTION("Filesystem in Userspace");
@@ -339,6 +340,7 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
return -ENOENT;
fuse_invalidate_attr(inode);
+ forget_all_cached_acls(inode);
if (offset >= 0) {
pg_start = offset >> PAGE_SHIFT;
if (len <= 0)
diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c
index d30e99610217..d87d58112eb1 100644
--- a/fs/fuse/xattr.c
+++ b/fs/fuse/xattr.c
@@ -9,9 +9,10 @@
#include "fuse_i.h"
#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
-static int fuse_setxattr(struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
+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);
@@ -45,8 +46,8 @@ static int fuse_setxattr(struct inode *inode, const char *name,
return err;
}
-static ssize_t fuse_getxattr(struct inode *inode, const char *name,
- void *value, size_t size)
+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);
@@ -128,7 +129,7 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
return ret;
}
-static int fuse_removexattr(struct inode *inode, const char *name)
+int fuse_removexattr(struct inode *inode, const char *name)
{
struct fuse_conn *fc = get_fuse_conn(inode);
FUSE_ARGS(args);
@@ -179,5 +180,7 @@ static const struct xattr_handler fuse_xattr_handler = {
};
const struct xattr_handler *fuse_xattr_handlers[] = {
+ &fuse_xattr_acl_access_handler,
+ &fuse_xattr_acl_default_handler,
&fuse_xattr_handler,
};
--
2.7.4
next prev parent reply other threads:[~2016-08-01 21:27 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-08-01 21:27 [RFC v3 0/2] Support for posix acls in fuse Seth Forshee
2016-08-01 21:27 ` [RFC v3 1/2] fuse: Use generic xattr ops Seth Forshee
2016-08-04 11:09 ` Miklos Szeredi
2016-08-04 14:12 ` Seth Forshee
2016-08-01 21:27 ` Seth Forshee [this message]
2016-08-04 12:11 ` [RFC v3 2/2] fuse: Add posix acl support Miklos Szeredi
[not found] ` <CAJfpegtzeJid8tHkz66scDcpCjNEEwtBb4m8MQqq7u+SCdj3dQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-08-04 12:40 ` Ravishankar N
2016-08-04 14:11 ` Seth Forshee
2016-08-05 23:07 ` Eric W. Biederman
2016-08-06 1:52 ` Seth Forshee
2016-08-06 21:09 ` Miklos Szeredi
2016-08-07 3:46 ` Seth Forshee
2016-08-07 12:59 ` Eric W. Biederman
[not found] ` <87popkrazt.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
2016-08-07 13:51 ` Seth Forshee
2016-08-16 20:59 ` Seth Forshee
2016-08-17 12:01 ` Miklos Szeredi
2016-08-01 23:03 ` [RFC v3 0/2] Support for posix acls in fuse Nikolaus Rath
2016-08-02 3:39 ` Seth Forshee
2016-08-02 15:13 ` [fuse-devel] " Michael Theall
2016-08-09 0:00 ` Nikolaus Rath
2016-08-09 0:03 ` Nikolaus Rath
2016-08-09 0:27 ` Eric W. Biederman
2016-08-09 22:44 ` Nikolaus Rath
2016-08-09 7:06 ` Jean-Pierre André
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1470086846-19844-3-git-send-email-seth.forshee@canonical.com \
--to=seth.forshee@canonical.com \
--cc=Nikolaus@rath.org \
--cc=ebiederm@xmission.com \
--cc=fuse-devel@lists.sourceforge.net \
--cc=jean-pierre.andre@wanadoo.fr \
--cc=linux-fsdevel@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=mtheall@us.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).