18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * FUSE: Filesystem in Userspace
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Canonical Ltd. <seth.forshee@canonical.com>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This program can be distributed under the terms of the GNU GPL.
68c2ecf20Sopenharmony_ci * See the file COPYING.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "fuse_i.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/posix_acl.h>
128c2ecf20Sopenharmony_ci#include <linux/posix_acl_xattr.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct posix_acl *fuse_get_acl(struct inode *inode, int type)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct fuse_conn *fc = get_fuse_conn(inode);
178c2ecf20Sopenharmony_ci	int size;
188c2ecf20Sopenharmony_ci	const char *name;
198c2ecf20Sopenharmony_ci	void *value = NULL;
208c2ecf20Sopenharmony_ci	struct posix_acl *acl;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	if (fuse_is_bad(inode))
238c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	if (!fc->posix_acl || fc->no_getxattr)
268c2ecf20Sopenharmony_ci		return NULL;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (type == ACL_TYPE_ACCESS)
298c2ecf20Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_ACCESS;
308c2ecf20Sopenharmony_ci	else if (type == ACL_TYPE_DEFAULT)
318c2ecf20Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_DEFAULT;
328c2ecf20Sopenharmony_ci	else
338c2ecf20Sopenharmony_ci		return ERR_PTR(-EOPNOTSUPP);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	value = kmalloc(PAGE_SIZE, GFP_KERNEL);
368c2ecf20Sopenharmony_ci	if (!value)
378c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
388c2ecf20Sopenharmony_ci	size = fuse_getxattr(inode, name, value, PAGE_SIZE);
398c2ecf20Sopenharmony_ci	if (size > 0)
408c2ecf20Sopenharmony_ci		acl = posix_acl_from_xattr(fc->user_ns, value, size);
418c2ecf20Sopenharmony_ci	else if ((size == 0) || (size == -ENODATA) ||
428c2ecf20Sopenharmony_ci		 (size == -EOPNOTSUPP && fc->no_getxattr))
438c2ecf20Sopenharmony_ci		acl = NULL;
448c2ecf20Sopenharmony_ci	else if (size == -ERANGE)
458c2ecf20Sopenharmony_ci		acl = ERR_PTR(-E2BIG);
468c2ecf20Sopenharmony_ci	else
478c2ecf20Sopenharmony_ci		acl = ERR_PTR(size);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	kfree(value);
508c2ecf20Sopenharmony_ci	return acl;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciint fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct fuse_conn *fc = get_fuse_conn(inode);
568c2ecf20Sopenharmony_ci	const char *name;
578c2ecf20Sopenharmony_ci	int ret;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (fuse_is_bad(inode))
608c2ecf20Sopenharmony_ci		return -EIO;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!fc->posix_acl || fc->no_setxattr)
638c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (type == ACL_TYPE_ACCESS)
668c2ecf20Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_ACCESS;
678c2ecf20Sopenharmony_ci	else if (type == ACL_TYPE_DEFAULT)
688c2ecf20Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_DEFAULT;
698c2ecf20Sopenharmony_ci	else
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (acl) {
738c2ecf20Sopenharmony_ci		/*
748c2ecf20Sopenharmony_ci		 * Fuse userspace is responsible for updating access
758c2ecf20Sopenharmony_ci		 * permissions in the inode, if needed. fuse_setxattr
768c2ecf20Sopenharmony_ci		 * invalidates the inode attributes, which will force
778c2ecf20Sopenharmony_ci		 * them to be refreshed the next time they are used,
788c2ecf20Sopenharmony_ci		 * and it also updates i_ctime.
798c2ecf20Sopenharmony_ci		 */
808c2ecf20Sopenharmony_ci		size_t size = posix_acl_xattr_size(acl->a_count);
818c2ecf20Sopenharmony_ci		void *value;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		if (size > PAGE_SIZE)
848c2ecf20Sopenharmony_ci			return -E2BIG;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		value = kmalloc(size, GFP_KERNEL);
878c2ecf20Sopenharmony_ci		if (!value)
888c2ecf20Sopenharmony_ci			return -ENOMEM;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		ret = posix_acl_to_xattr(fc->user_ns, acl, value, size);
918c2ecf20Sopenharmony_ci		if (ret < 0) {
928c2ecf20Sopenharmony_ci			kfree(value);
938c2ecf20Sopenharmony_ci			return ret;
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		ret = fuse_setxattr(inode, name, value, size, 0);
978c2ecf20Sopenharmony_ci		kfree(value);
988c2ecf20Sopenharmony_ci	} else {
998c2ecf20Sopenharmony_ci		ret = fuse_removexattr(inode, name);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	forget_all_cached_acls(inode);
1028c2ecf20Sopenharmony_ci	fuse_invalidate_attr(inode);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return ret;
1058c2ecf20Sopenharmony_ci}
106