From patchwork Wed Jun 29 19:07:31 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Seth Forshee X-Patchwork-Id: 9206081 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 85C3860752 for ; Wed, 29 Jun 2016 19:07:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 72D1428655 for ; Wed, 29 Jun 2016 19:07:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 67B9A28658; Wed, 29 Jun 2016 19:07:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7FDF22865D for ; Wed, 29 Jun 2016 19:07:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932219AbcF2THh (ORCPT ); Wed, 29 Jun 2016 15:07:37 -0400 Received: from mail-ob0-f180.google.com ([209.85.214.180]:34638 "EHLO mail-ob0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752908AbcF2THe (ORCPT ); Wed, 29 Jun 2016 15:07:34 -0400 Received: by mail-ob0-f180.google.com with SMTP id ru5so39077521obc.1 for ; Wed, 29 Jun 2016 12:07:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :user-agent; bh=81DOPULhQPDigMmpr9Uz7it65s3CbBXodX1bbbc7Fyc=; b=LWskzTC1slyvbPsEFSthj0Bcyt6zVMr3kjCMDqnZ+ytnuPJNtWIzMItLsdEmD8rHMu 3lpmaR/nHzA8vEzoZmtqhuc5EmU/WnhHw4jkaYbXVw+zSB8ITs/zns3vRYDDE8sI6DW5 JUjypfJPZtVjDtNkFsX60URLVL4Ac5JICf9cdfh8mplFr5SxptQ2yT38MOKzsmzS7T3M DgjUtsY7C0q/YghG6Z49HxQdjpKhP49c6xNuOGWmUn2eZTMkCQAxpcthyXmarXGH7283 frek+cisl4FaoRcllTNlqoyBAqZwcv1Ts86zQnA/ktcb6+FuLH23RbWHVYFL8zfJvJwh WSSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:user-agent; bh=81DOPULhQPDigMmpr9Uz7it65s3CbBXodX1bbbc7Fyc=; b=LLAoKHJCsyrcSk+c8IJpkCGVt1yLkFQE+NtHy1Yjwp7Gz6vbSP68OIxaiqVs1JBuHW t659m64t0y/lcqPqxQTVfa0ktxRmaQe4+6FloRheaxjCvQUw6ctK82ObJIJZ5SrRHNRT xK1NTFnGw7GT/NJo7C4xpwTrS4y+MXz3Ubd/jJ8DI3GCZjnddgF186FenYjeEk2jSuw4 IwQ/d2BMa4OBPEySn/k5sP5YGmu965ksdCot0uAnF0iK70fPuhaKKEuWQJ2va1r+ialG MqJPcxL9inrDVck4LMQKYx5ME2sWP9PhSIupDc0NcDVKSc/p29xJPWBYnpTcJwJqYTxP Q4tA== X-Gm-Message-State: ALyK8tKWWMlZDnk5mwiKgFH+zNb73RHZ9fNUFqF7ySkkb7yxRwG3SRMKKjI+xqi2HoUA1B94 X-Received: by 10.157.58.52 with SMTP id j49mr7037016otc.118.1467227253226; Wed, 29 Jun 2016 12:07:33 -0700 (PDT) Received: from localhost ([2605:a601:aab:f920:c5db:7dbe:3daa:2b39]) by smtp.gmail.com with ESMTPSA id t69sm1844133oit.24.2016.06.29.12.07.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 29 Jun 2016 12:07:32 -0700 (PDT) Date: Wed, 29 Jun 2016 14:07:31 -0500 From: Seth Forshee To: fuse-devel@lists.sourceforge.net, linux-fsdevel@vger.kernel.org Cc: Miklos Szeredi , "Eric W. Biederman" Subject: [RFC] fuse: Support posix ACLs Message-ID: <20160629190731.GF53123@ubuntu-hedt> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions. There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts? Are there any places where we need to invalidate cached ACLs that aren't covered in the patch? Thanks, Seth --- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 8466e122ee62..2f53b0491159 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx) { @@ -1061,6 +1063,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); } @@ -1719,9 +1722,8 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, return fuse_update_attributes(inode, stat, NULL, NULL); } -static int fuse_setxattr(struct dentry *unused, struct inode *inode, - const char *name, const void *value, - size_t size, int flags) +static 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); @@ -1755,8 +1757,8 @@ static int fuse_setxattr(struct dentry *unused, struct inode *inode, return err; } -static ssize_t fuse_getxattr(struct dentry *entry, struct inode *inode, - const char *name, void *value, size_t size) +static 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); @@ -1838,9 +1840,8 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) return ret; } -static int fuse_removexattr(struct dentry *entry, const char *name) +static int fuse_removexattr(struct inode *inode, const char *name) { - struct inode *inode = d_inode(entry); struct fuse_conn *fc = get_fuse_conn(inode); FUSE_ARGS(args); int err; @@ -1865,6 +1866,138 @@ static int fuse_removexattr(struct dentry *entry, const char *name) 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, +}; + +#ifndef CONFIG_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; +} + +static const struct xattr_handler fuse_xattr_acl_access_handler = { + .name = XATTR_NAME_POSIX_ACL_ACCESS, + .get = fuse_xattr_acl_get, + .set = fuse_xattr_acl_set, +}; + +static const struct xattr_handler fuse_xattr_acl_default_handler = { + .name = XATTR_NAME_POSIX_ACL_DEFAULT, + .get = fuse_xattr_acl_get, + .set = fuse_xattr_acl_set, +}; +#endif /* CONFIG_POSIX_ACL */ + +const struct xattr_handler *fuse_xattr_handlers[] = { +#ifdef CONFIG_FS_POSIX_ACL + &posix_acl_access_xattr_handler, + &posix_acl_default_xattr_handler, +#else + &fuse_xattr_acl_access_handler, + &fuse_xattr_acl_default_handler, +#endif + &fuse_xattr_handler, +}; + +static struct posix_acl *fuse_get_acl(struct inode *inode, int type) +{ + int size; + const char *name; + void *value = NULL; + struct posix_acl *acl; + + 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(inode->i_sb->s_user_ns, value, size); + } else if ((size == 0) || (size == -ENODATA)) { + acl = NULL; + } else { + acl = ERR_PTR(size); + } + kfree(value); + + return acl; +} + +static int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type) +{ + const char *name; + int ret; + + 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) { + struct user_namespace *s_user_ns = inode->i_sb->s_user_ns; + 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(s_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; +} + static const struct inode_operations fuse_dir_inode_operations = { .lookup = fuse_lookup, .mkdir = fuse_mkdir, @@ -1879,10 +2012,12 @@ static const struct inode_operations fuse_dir_inode_operations = { .mknod = fuse_mknod, .permission = fuse_permission, .getattr = fuse_getattr, - .setxattr = fuse_setxattr, - .getxattr = fuse_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = fuse_listxattr, - .removexattr = fuse_removexattr, + .removexattr = generic_removexattr, + .get_acl = fuse_get_acl, + .set_acl = fuse_set_acl, }; static const struct file_operations fuse_dir_operations = { @@ -1900,10 +2035,12 @@ static const struct inode_operations fuse_common_inode_operations = { .setattr = fuse_setattr, .permission = fuse_permission, .getattr = fuse_getattr, - .setxattr = fuse_setxattr, - .getxattr = fuse_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = fuse_listxattr, - .removexattr = fuse_removexattr, + .removexattr = generic_removexattr, + .get_acl = fuse_get_acl, + .set_acl = fuse_set_acl, }; static const struct inode_operations fuse_symlink_inode_operations = { @@ -1911,10 +2048,12 @@ static const struct inode_operations fuse_symlink_inode_operations = { .get_link = fuse_get_link, .readlink = generic_readlink, .getattr = fuse_getattr, - .setxattr = fuse_setxattr, - .getxattr = fuse_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = fuse_listxattr, - .removexattr = fuse_removexattr, + .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 9f4c3c82edd6..02c08796051e 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -693,6 +694,8 @@ extern const struct file_operations fuse_dev_operations; extern const struct dentry_operations fuse_dentry_operations; +extern const struct xattr_handler *fuse_xattr_handlers[]; + /** * Inode to nodeid comparison. */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 254f1944ee98..9c1519c269bb 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -21,6 +21,7 @@ #include #include #include +#include MODULE_AUTHOR("Miklos Szeredi "); 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) @@ -1066,6 +1068,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) } sb->s_magic = FUSE_SUPER_MAGIC; sb->s_op = &fuse_super_operations; + sb->s_xattr = fuse_xattr_handlers; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_time_gran = 1; sb->s_export_op = &fuse_export_operations;