xref: /openbmc/linux/fs/xfs/xfs_acl.c (revision 7132fe4f)
1 /*
2  * Copyright (c) 2008, Christoph Hellwig
3  * All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write the Free Software Foundation,
16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 #include "xfs.h"
19 #include "xfs_format.h"
20 #include "xfs_log_format.h"
21 #include "xfs_trans_resv.h"
22 #include "xfs_ag.h"
23 #include "xfs_sb.h"
24 #include "xfs_mount.h"
25 #include "xfs_inode.h"
26 #include "xfs_acl.h"
27 #include "xfs_attr.h"
28 #include "xfs_trace.h"
29 #include <linux/slab.h>
30 #include <linux/xattr.h>
31 #include <linux/posix_acl_xattr.h>
32 
33 
34 /*
35  * Locking scheme:
36  *  - all ACL updates are protected by inode->i_mutex, which is taken before
37  *    calling into this file.
38  */
39 
40 STATIC struct posix_acl *
41 xfs_acl_from_disk(
42 	struct xfs_acl	*aclp,
43 	int		max_entries)
44 {
45 	struct posix_acl_entry *acl_e;
46 	struct posix_acl *acl;
47 	struct xfs_acl_entry *ace;
48 	unsigned int count, i;
49 
50 	count = be32_to_cpu(aclp->acl_cnt);
51 	if (count > max_entries)
52 		return ERR_PTR(-EFSCORRUPTED);
53 
54 	acl = posix_acl_alloc(count, GFP_KERNEL);
55 	if (!acl)
56 		return ERR_PTR(-ENOMEM);
57 
58 	for (i = 0; i < count; i++) {
59 		acl_e = &acl->a_entries[i];
60 		ace = &aclp->acl_entry[i];
61 
62 		/*
63 		 * The tag is 32 bits on disk and 16 bits in core.
64 		 *
65 		 * Because every access to it goes through the core
66 		 * format first this is not a problem.
67 		 */
68 		acl_e->e_tag = be32_to_cpu(ace->ae_tag);
69 		acl_e->e_perm = be16_to_cpu(ace->ae_perm);
70 
71 		switch (acl_e->e_tag) {
72 		case ACL_USER:
73 			acl_e->e_uid = xfs_uid_to_kuid(be32_to_cpu(ace->ae_id));
74 			break;
75 		case ACL_GROUP:
76 			acl_e->e_gid = xfs_gid_to_kgid(be32_to_cpu(ace->ae_id));
77 			break;
78 		case ACL_USER_OBJ:
79 		case ACL_GROUP_OBJ:
80 		case ACL_MASK:
81 		case ACL_OTHER:
82 			break;
83 		default:
84 			goto fail;
85 		}
86 	}
87 	return acl;
88 
89 fail:
90 	posix_acl_release(acl);
91 	return ERR_PTR(-EINVAL);
92 }
93 
94 STATIC void
95 xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl)
96 {
97 	const struct posix_acl_entry *acl_e;
98 	struct xfs_acl_entry *ace;
99 	int i;
100 
101 	aclp->acl_cnt = cpu_to_be32(acl->a_count);
102 	for (i = 0; i < acl->a_count; i++) {
103 		ace = &aclp->acl_entry[i];
104 		acl_e = &acl->a_entries[i];
105 
106 		ace->ae_tag = cpu_to_be32(acl_e->e_tag);
107 		switch (acl_e->e_tag) {
108 		case ACL_USER:
109 			ace->ae_id = cpu_to_be32(xfs_kuid_to_uid(acl_e->e_uid));
110 			break;
111 		case ACL_GROUP:
112 			ace->ae_id = cpu_to_be32(xfs_kgid_to_gid(acl_e->e_gid));
113 			break;
114 		default:
115 			ace->ae_id = cpu_to_be32(ACL_UNDEFINED_ID);
116 			break;
117 		}
118 
119 		ace->ae_perm = cpu_to_be16(acl_e->e_perm);
120 	}
121 }
122 
123 struct posix_acl *
124 xfs_get_acl(struct inode *inode, int type)
125 {
126 	struct xfs_inode *ip = XFS_I(inode);
127 	struct posix_acl *acl = NULL;
128 	struct xfs_acl *xfs_acl;
129 	unsigned char *ea_name;
130 	int error;
131 	int len;
132 
133 	trace_xfs_get_acl(ip);
134 
135 	switch (type) {
136 	case ACL_TYPE_ACCESS:
137 		ea_name = SGI_ACL_FILE;
138 		break;
139 	case ACL_TYPE_DEFAULT:
140 		ea_name = SGI_ACL_DEFAULT;
141 		break;
142 	default:
143 		BUG();
144 	}
145 
146 	/*
147 	 * If we have a cached ACLs value just return it, not need to
148 	 * go out to the disk.
149 	 */
150 	len = XFS_ACL_MAX_SIZE(ip->i_mount);
151 	xfs_acl = kmem_zalloc_large(len, KM_SLEEP);
152 	if (!xfs_acl)
153 		return ERR_PTR(-ENOMEM);
154 
155 	error = -xfs_attr_get(ip, ea_name, (unsigned char *)xfs_acl,
156 							&len, ATTR_ROOT);
157 	if (error) {
158 		/*
159 		 * If the attribute doesn't exist make sure we have a negative
160 		 * cache entry, for any other error assume it is transient and
161 		 * leave the cache entry as ACL_NOT_CACHED.
162 		 */
163 		if (error == -ENOATTR)
164 			goto out_update_cache;
165 		goto out;
166 	}
167 
168 	acl = xfs_acl_from_disk(xfs_acl, XFS_ACL_MAX_ENTRIES(ip->i_mount));
169 	if (IS_ERR(acl))
170 		goto out;
171 
172 out_update_cache:
173 	set_cached_acl(inode, type, acl);
174 out:
175 	kmem_free(xfs_acl);
176 	return acl;
177 }
178 
179 STATIC int
180 __xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
181 {
182 	struct xfs_inode *ip = XFS_I(inode);
183 	unsigned char *ea_name;
184 	int error;
185 
186 	switch (type) {
187 	case ACL_TYPE_ACCESS:
188 		ea_name = SGI_ACL_FILE;
189 		break;
190 	case ACL_TYPE_DEFAULT:
191 		if (!S_ISDIR(inode->i_mode))
192 			return acl ? -EACCES : 0;
193 		ea_name = SGI_ACL_DEFAULT;
194 		break;
195 	default:
196 		return -EINVAL;
197 	}
198 
199 	if (acl) {
200 		struct xfs_acl *xfs_acl;
201 		int len = XFS_ACL_MAX_SIZE(ip->i_mount);
202 
203 		xfs_acl = kmem_zalloc_large(len, KM_SLEEP);
204 		if (!xfs_acl)
205 			return -ENOMEM;
206 
207 		xfs_acl_to_disk(xfs_acl, acl);
208 
209 		/* subtract away the unused acl entries */
210 		len -= sizeof(struct xfs_acl_entry) *
211 			 (XFS_ACL_MAX_ENTRIES(ip->i_mount) - acl->a_count);
212 
213 		error = -xfs_attr_set(ip, ea_name, (unsigned char *)xfs_acl,
214 				len, ATTR_ROOT);
215 
216 		kmem_free(xfs_acl);
217 	} else {
218 		/*
219 		 * A NULL ACL argument means we want to remove the ACL.
220 		 */
221 		error = -xfs_attr_remove(ip, ea_name, ATTR_ROOT);
222 
223 		/*
224 		 * If the attribute didn't exist to start with that's fine.
225 		 */
226 		if (error == -ENOATTR)
227 			error = 0;
228 	}
229 
230 	if (!error)
231 		set_cached_acl(inode, type, acl);
232 	return error;
233 }
234 
235 static int
236 xfs_set_mode(struct inode *inode, umode_t mode)
237 {
238 	int error = 0;
239 
240 	if (mode != inode->i_mode) {
241 		struct iattr iattr;
242 
243 		iattr.ia_valid = ATTR_MODE | ATTR_CTIME;
244 		iattr.ia_mode = mode;
245 		iattr.ia_ctime = current_fs_time(inode->i_sb);
246 
247 		error = -xfs_setattr_nonsize(XFS_I(inode), &iattr, XFS_ATTR_NOACL);
248 	}
249 
250 	return error;
251 }
252 
253 static int
254 xfs_acl_exists(struct inode *inode, unsigned char *name)
255 {
256 	int len = XFS_ACL_MAX_SIZE(XFS_M(inode->i_sb));
257 
258 	return (xfs_attr_get(XFS_I(inode), name, NULL, &len,
259 			    ATTR_ROOT|ATTR_KERNOVAL) == 0);
260 }
261 
262 int
263 posix_acl_access_exists(struct inode *inode)
264 {
265 	return xfs_acl_exists(inode, SGI_ACL_FILE);
266 }
267 
268 int
269 posix_acl_default_exists(struct inode *inode)
270 {
271 	if (!S_ISDIR(inode->i_mode))
272 		return 0;
273 	return xfs_acl_exists(inode, SGI_ACL_DEFAULT);
274 }
275 
276 int
277 xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
278 {
279 	int error = 0;
280 
281 	if (!acl)
282 		goto set_acl;
283 
284 	error = -E2BIG;
285 	if (acl->a_count > XFS_ACL_MAX_ENTRIES(XFS_M(inode->i_sb)))
286 		return error;
287 
288 	if (type == ACL_TYPE_ACCESS) {
289 		umode_t mode = inode->i_mode;
290 		error = posix_acl_equiv_mode(acl, &mode);
291 
292 		if (error <= 0) {
293 			acl = NULL;
294 
295 			if (error < 0)
296 				return error;
297 		}
298 
299 		error = xfs_set_mode(inode, mode);
300 		if (error)
301 			return error;
302 	}
303 
304  set_acl:
305 	return __xfs_set_acl(inode, type, acl);
306 }
307