Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * FUSE: Filesystem in Userspace
  3 * Copyright (C) 2016 Canonical Ltd. <seth.forshee@canonical.com>
  4 *
  5 * This program can be distributed under the terms of the GNU GPL.
  6 * See the file COPYING.
  7 */
  8
  9#include "fuse_i.h"
 10
 11#include <linux/posix_acl.h>
 12#include <linux/posix_acl_xattr.h>
 13
 14static struct posix_acl *__fuse_get_acl(struct fuse_conn *fc,
 15					struct inode *inode, int type, bool rcu)
 16{
 17	int size;
 18	const char *name;
 19	void *value = NULL;
 20	struct posix_acl *acl;
 21
 22	if (rcu)
 23		return ERR_PTR(-ECHILD);
 24
 25	if (fuse_is_bad(inode))
 26		return ERR_PTR(-EIO);
 27
 28	if (fc->no_getxattr)
 29		return NULL;
 30
 31	if (type == ACL_TYPE_ACCESS)
 32		name = XATTR_NAME_POSIX_ACL_ACCESS;
 33	else if (type == ACL_TYPE_DEFAULT)
 34		name = XATTR_NAME_POSIX_ACL_DEFAULT;
 35	else
 36		return ERR_PTR(-EOPNOTSUPP);
 37
 38	value = kmalloc(PAGE_SIZE, GFP_KERNEL);
 39	if (!value)
 40		return ERR_PTR(-ENOMEM);
 41	size = fuse_getxattr(inode, name, value, PAGE_SIZE);
 42	if (size > 0)
 43		acl = posix_acl_from_xattr(fc->user_ns, value, size);
 44	else if ((size == 0) || (size == -ENODATA) ||
 45		 (size == -EOPNOTSUPP && fc->no_getxattr))
 46		acl = NULL;
 47	else if (size == -ERANGE)
 48		acl = ERR_PTR(-E2BIG);
 49	else
 50		acl = ERR_PTR(size);
 51
 52	kfree(value);
 53	return acl;
 54}
 55
 56static inline bool fuse_no_acl(const struct fuse_conn *fc,
 57			       const struct inode *inode)
 58{
 59	/*
 60	 * Refuse interacting with POSIX ACLs for daemons that
 61	 * don't support FUSE_POSIX_ACL and are not mounted on
 62	 * the host to retain backwards compatibility.
 63	 */
 64	return !fc->posix_acl && (i_user_ns(inode) != &init_user_ns);
 65}
 66
 67struct posix_acl *fuse_get_acl(struct mnt_idmap *idmap,
 68			       struct dentry *dentry, int type)
 69{
 70	struct inode *inode = d_inode(dentry);
 71	struct fuse_conn *fc = get_fuse_conn(inode);
 72
 73	if (fuse_no_acl(fc, inode))
 74		return ERR_PTR(-EOPNOTSUPP);
 75
 76	return __fuse_get_acl(fc, inode, type, false);
 77}
 78
 79struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu)
 80{
 81	struct fuse_conn *fc = get_fuse_conn(inode);
 82
 83	/*
 84	 * FUSE daemons before FUSE_POSIX_ACL was introduced could get and set
 85	 * POSIX ACLs without them being used for permission checking by the
 86	 * vfs. Retain that behavior for backwards compatibility as there are
 87	 * filesystems that do all permission checking for acls in the daemon
 88	 * and not in the kernel.
 89	 */
 90	if (!fc->posix_acl)
 91		return NULL;
 92	return __fuse_get_acl(fc,  inode, type, rcu);
 93}
 94
 95int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
 96		 struct posix_acl *acl, int type)
 97{
 98	struct inode *inode = d_inode(dentry);
 99	struct fuse_conn *fc = get_fuse_conn(inode);
100	const char *name;
101	int ret;
102
103	if (fuse_is_bad(inode))
104		return -EIO;
105
106	if (fc->no_setxattr || fuse_no_acl(fc, inode))
107		return -EOPNOTSUPP;
108
109	if (type == ACL_TYPE_ACCESS)
110		name = XATTR_NAME_POSIX_ACL_ACCESS;
111	else if (type == ACL_TYPE_DEFAULT)
112		name = XATTR_NAME_POSIX_ACL_DEFAULT;
113	else
114		return -EINVAL;
115
116	if (acl) {
117		unsigned int extra_flags = 0;
118		/*
119		 * Fuse userspace is responsible for updating access
120		 * permissions in the inode, if needed. fuse_setxattr
121		 * invalidates the inode attributes, which will force
122		 * them to be refreshed the next time they are used,
123		 * and it also updates i_ctime.
124		 */
125		size_t size = posix_acl_xattr_size(acl->a_count);
126		void *value;
127
128		if (size > PAGE_SIZE)
129			return -E2BIG;
130
131		value = kmalloc(size, GFP_KERNEL);
132		if (!value)
133			return -ENOMEM;
134
135		ret = posix_acl_to_xattr(fc->user_ns, acl, value, size);
136		if (ret < 0) {
137			kfree(value);
138			return ret;
139		}
140
141		/*
142		 * Fuse daemons without FUSE_POSIX_ACL never changed the passed
143		 * through POSIX ACLs. Such daemons don't expect setgid bits to
144		 * be stripped.
145		 */
146		if (fc->posix_acl &&
147		    !in_group_or_capable(idmap, inode,
148					 i_gid_into_vfsgid(idmap, inode)))
149			extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID;
150
151		ret = fuse_setxattr(inode, name, value, size, 0, extra_flags);
152		kfree(value);
153	} else {
154		ret = fuse_removexattr(inode, name);
155	}
156
157	if (fc->posix_acl) {
158		/*
159		 * Fuse daemons without FUSE_POSIX_ACL never cached POSIX ACLs
160		 * and didn't invalidate attributes. Retain that behavior.
161		 */
162		forget_all_cached_acls(inode);
163		fuse_invalidate_attr(inode);
164	}
165
166	return ret;
167}