xref: /openbmc/linux/fs/orangefs/acl.c (revision 612cf4d2)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) 2001 Clemson University and The University of Chicago
4  *
5  * See COPYING in top-level directory.
6  */
7 
8 #include "protocol.h"
9 #include "orangefs-kernel.h"
10 #include "orangefs-bufmap.h"
11 #include <linux/posix_acl_xattr.h>
12 
13 struct posix_acl *orangefs_get_acl(struct inode *inode, int type, bool rcu)
14 {
15 	struct posix_acl *acl;
16 	int ret;
17 	char *key = NULL, *value = NULL;
18 
19 	if (rcu)
20 		return ERR_PTR(-ECHILD);
21 
22 	switch (type) {
23 	case ACL_TYPE_ACCESS:
24 		key = XATTR_NAME_POSIX_ACL_ACCESS;
25 		break;
26 	case ACL_TYPE_DEFAULT:
27 		key = XATTR_NAME_POSIX_ACL_DEFAULT;
28 		break;
29 	default:
30 		gossip_err("orangefs_get_acl: bogus value of type %d\n", type);
31 		return ERR_PTR(-EINVAL);
32 	}
33 	/*
34 	 * Rather than incurring a network call just to determine the exact
35 	 * length of the attribute, I just allocate a max length to save on
36 	 * the network call. Conceivably, we could pass NULL to
37 	 * orangefs_inode_getxattr() to probe the length of the value, but
38 	 * I don't do that for now.
39 	 */
40 	value = kmalloc(ORANGEFS_MAX_XATTR_VALUELEN, GFP_KERNEL);
41 	if (!value)
42 		return ERR_PTR(-ENOMEM);
43 
44 	gossip_debug(GOSSIP_ACL_DEBUG,
45 		     "inode %pU, key %s, type %d\n",
46 		     get_khandle_from_ino(inode),
47 		     key,
48 		     type);
49 	ret = orangefs_inode_getxattr(inode, key, value,
50 				      ORANGEFS_MAX_XATTR_VALUELEN);
51 	/* if the key exists, convert it to an in-memory rep */
52 	if (ret > 0) {
53 		acl = posix_acl_from_xattr(&init_user_ns, value, ret);
54 	} else if (ret == -ENODATA || ret == -ENOSYS) {
55 		acl = NULL;
56 	} else {
57 		gossip_err("inode %pU retrieving acl's failed with error %d\n",
58 			   get_khandle_from_ino(inode),
59 			   ret);
60 		acl = ERR_PTR(ret);
61 	}
62 	/* kfree(NULL) is safe, so don't worry if value ever got used */
63 	kfree(value);
64 	return acl;
65 }
66 
67 int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
68 {
69 	int error = 0;
70 	void *value = NULL;
71 	size_t size = 0;
72 	const char *name = NULL;
73 
74 	switch (type) {
75 	case ACL_TYPE_ACCESS:
76 		name = XATTR_NAME_POSIX_ACL_ACCESS;
77 		break;
78 	case ACL_TYPE_DEFAULT:
79 		name = XATTR_NAME_POSIX_ACL_DEFAULT;
80 		break;
81 	default:
82 		gossip_err("%s: invalid type %d!\n", __func__, type);
83 		return -EINVAL;
84 	}
85 
86 	gossip_debug(GOSSIP_ACL_DEBUG,
87 		     "%s: inode %pU, key %s type %d\n",
88 		     __func__, get_khandle_from_ino(inode),
89 		     name,
90 		     type);
91 
92 	if (acl) {
93 		size = posix_acl_xattr_size(acl->a_count);
94 		value = kmalloc(size, GFP_KERNEL);
95 		if (!value)
96 			return -ENOMEM;
97 
98 		error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
99 		if (error < 0)
100 			goto out;
101 	}
102 
103 	gossip_debug(GOSSIP_ACL_DEBUG,
104 		     "%s: name %s, value %p, size %zd, acl %p\n",
105 		     __func__, name, value, size, acl);
106 	/*
107 	 * Go ahead and set the extended attribute now. NOTE: Suppose acl
108 	 * was NULL, then value will be NULL and size will be 0 and that
109 	 * will xlate to a removexattr. However, we don't want removexattr
110 	 * complain if attributes does not exist.
111 	 */
112 	error = orangefs_inode_setxattr(inode, name, value, size, 0);
113 
114 out:
115 	kfree(value);
116 	if (!error)
117 		set_cached_acl(inode, type, acl);
118 	return error;
119 }
120 
121 int orangefs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
122 		     struct posix_acl *acl, int type)
123 {
124 	int error;
125 	struct iattr iattr;
126 	int rc;
127 	struct inode *inode = d_inode(dentry);
128 
129 	memset(&iattr, 0, sizeof iattr);
130 
131 	if (type == ACL_TYPE_ACCESS && acl) {
132 		/*
133 		 * posix_acl_update_mode checks to see if the permissions
134 		 * described by the ACL can be encoded into the
135 		 * object's mode. If so, it sets "acl" to NULL
136 		 * and "mode" to the new desired value. It is up to
137 		 * us to propagate the new mode back to the server...
138 		 */
139 		error = posix_acl_update_mode(&init_user_ns, inode,
140 					      &iattr.ia_mode, &acl);
141 		if (error) {
142 			gossip_err("%s: posix_acl_update_mode err: %d\n",
143 				   __func__,
144 				   error);
145 			return error;
146 		}
147 
148 		if (inode->i_mode != iattr.ia_mode)
149 			iattr.ia_valid = ATTR_MODE;
150 
151 	}
152 
153 	rc = __orangefs_set_acl(inode, acl, type);
154 
155 	if (!rc && (iattr.ia_valid == ATTR_MODE))
156 		rc = __orangefs_setattr_mode(dentry, &iattr);
157 
158 	return rc;
159 }
160