xref: /openbmc/linux/fs/orangefs/acl.c (revision 0ad53fe3)
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 static int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl,
68 			      int type)
69 {
70 	int error = 0;
71 	void *value = NULL;
72 	size_t size = 0;
73 	const char *name = NULL;
74 
75 	switch (type) {
76 	case ACL_TYPE_ACCESS:
77 		name = XATTR_NAME_POSIX_ACL_ACCESS;
78 		break;
79 	case ACL_TYPE_DEFAULT:
80 		name = XATTR_NAME_POSIX_ACL_DEFAULT;
81 		break;
82 	default:
83 		gossip_err("%s: invalid type %d!\n", __func__, type);
84 		return -EINVAL;
85 	}
86 
87 	gossip_debug(GOSSIP_ACL_DEBUG,
88 		     "%s: inode %pU, key %s type %d\n",
89 		     __func__, get_khandle_from_ino(inode),
90 		     name,
91 		     type);
92 
93 	if (acl) {
94 		size = posix_acl_xattr_size(acl->a_count);
95 		value = kmalloc(size, GFP_KERNEL);
96 		if (!value)
97 			return -ENOMEM;
98 
99 		error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
100 		if (error < 0)
101 			goto out;
102 	}
103 
104 	gossip_debug(GOSSIP_ACL_DEBUG,
105 		     "%s: name %s, value %p, size %zd, acl %p\n",
106 		     __func__, name, value, size, acl);
107 	/*
108 	 * Go ahead and set the extended attribute now. NOTE: Suppose acl
109 	 * was NULL, then value will be NULL and size will be 0 and that
110 	 * will xlate to a removexattr. However, we don't want removexattr
111 	 * complain if attributes does not exist.
112 	 */
113 	error = orangefs_inode_setxattr(inode, name, value, size, 0);
114 
115 out:
116 	kfree(value);
117 	if (!error)
118 		set_cached_acl(inode, type, acl);
119 	return error;
120 }
121 
122 int orangefs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
123 		     struct posix_acl *acl, int type)
124 {
125 	int error;
126 	struct iattr iattr;
127 	int rc;
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(inode, &iattr);
157 
158 	return rc;
159 }
160 
161 int orangefs_init_acl(struct inode *inode, struct inode *dir)
162 {
163 	struct posix_acl *default_acl, *acl;
164 	umode_t mode = inode->i_mode;
165 	struct iattr iattr;
166 	int error = 0;
167 
168 	error = posix_acl_create(dir, &mode, &default_acl, &acl);
169 	if (error)
170 		return error;
171 
172 	if (default_acl) {
173 		error = __orangefs_set_acl(inode, default_acl,
174 					   ACL_TYPE_DEFAULT);
175 		posix_acl_release(default_acl);
176 	} else {
177 		inode->i_default_acl = NULL;
178 	}
179 
180 	if (acl) {
181 		if (!error)
182 			error = __orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
183 		posix_acl_release(acl);
184 	} else {
185 		inode->i_acl = NULL;
186 	}
187 
188 	/* If mode of the inode was changed, then do a forcible ->setattr */
189 	if (mode != inode->i_mode) {
190 		memset(&iattr, 0, sizeof iattr);
191 		inode->i_mode = mode;
192 		iattr.ia_mode = mode;
193 		iattr.ia_valid |= ATTR_MODE;
194 		__orangefs_setattr(inode, &iattr);
195 	}
196 
197 	return error;
198 }
199