xref: /openbmc/linux/fs/ceph/acl.c (revision 7221fe4c2ed72804b28633c8e0217d65abb0023f)
1*7221fe4cSGuangliang Zhao /*
2*7221fe4cSGuangliang Zhao  * linux/fs/ceph/acl.c
3*7221fe4cSGuangliang Zhao  *
4*7221fe4cSGuangliang Zhao  * Copyright (C) 2013 Guangliang Zhao, <lucienchao@gmail.com>
5*7221fe4cSGuangliang Zhao  *
6*7221fe4cSGuangliang Zhao  * This program is free software; you can redistribute it and/or
7*7221fe4cSGuangliang Zhao  * modify it under the terms of the GNU General Public
8*7221fe4cSGuangliang Zhao  * License v2 as published by the Free Software Foundation.
9*7221fe4cSGuangliang Zhao  *
10*7221fe4cSGuangliang Zhao  * This program is distributed in the hope that it will be useful,
11*7221fe4cSGuangliang Zhao  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*7221fe4cSGuangliang Zhao  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13*7221fe4cSGuangliang Zhao  * General Public License for more details.
14*7221fe4cSGuangliang Zhao  *
15*7221fe4cSGuangliang Zhao  * You should have received a copy of the GNU General Public
16*7221fe4cSGuangliang Zhao  * License along with this program; if not, write to the
17*7221fe4cSGuangliang Zhao  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18*7221fe4cSGuangliang Zhao  * Boston, MA 021110-1307, USA.
19*7221fe4cSGuangliang Zhao  */
20*7221fe4cSGuangliang Zhao 
21*7221fe4cSGuangliang Zhao #include <linux/ceph/ceph_debug.h>
22*7221fe4cSGuangliang Zhao #include <linux/fs.h>
23*7221fe4cSGuangliang Zhao #include <linux/string.h>
24*7221fe4cSGuangliang Zhao #include <linux/xattr.h>
25*7221fe4cSGuangliang Zhao #include <linux/posix_acl_xattr.h>
26*7221fe4cSGuangliang Zhao #include <linux/posix_acl.h>
27*7221fe4cSGuangliang Zhao #include <linux/sched.h>
28*7221fe4cSGuangliang Zhao #include <linux/slab.h>
29*7221fe4cSGuangliang Zhao 
30*7221fe4cSGuangliang Zhao #include "super.h"
31*7221fe4cSGuangliang Zhao 
32*7221fe4cSGuangliang Zhao static inline void ceph_set_cached_acl(struct inode *inode,
33*7221fe4cSGuangliang Zhao 					int type, struct posix_acl *acl)
34*7221fe4cSGuangliang Zhao {
35*7221fe4cSGuangliang Zhao 	struct ceph_inode_info *ci = ceph_inode(inode);
36*7221fe4cSGuangliang Zhao 
37*7221fe4cSGuangliang Zhao 	spin_lock(&ci->i_ceph_lock);
38*7221fe4cSGuangliang Zhao 	if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0))
39*7221fe4cSGuangliang Zhao 		set_cached_acl(inode, type, acl);
40*7221fe4cSGuangliang Zhao 	spin_unlock(&ci->i_ceph_lock);
41*7221fe4cSGuangliang Zhao }
42*7221fe4cSGuangliang Zhao 
43*7221fe4cSGuangliang Zhao static inline struct posix_acl *ceph_get_cached_acl(struct inode *inode,
44*7221fe4cSGuangliang Zhao 							int type)
45*7221fe4cSGuangliang Zhao {
46*7221fe4cSGuangliang Zhao 	struct ceph_inode_info *ci = ceph_inode(inode);
47*7221fe4cSGuangliang Zhao 	struct posix_acl *acl = ACL_NOT_CACHED;
48*7221fe4cSGuangliang Zhao 
49*7221fe4cSGuangliang Zhao 	spin_lock(&ci->i_ceph_lock);
50*7221fe4cSGuangliang Zhao 	if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0))
51*7221fe4cSGuangliang Zhao 		acl = get_cached_acl(inode, type);
52*7221fe4cSGuangliang Zhao 	spin_unlock(&ci->i_ceph_lock);
53*7221fe4cSGuangliang Zhao 
54*7221fe4cSGuangliang Zhao 	return acl;
55*7221fe4cSGuangliang Zhao }
56*7221fe4cSGuangliang Zhao 
57*7221fe4cSGuangliang Zhao void ceph_forget_all_cached_acls(struct inode *inode)
58*7221fe4cSGuangliang Zhao {
59*7221fe4cSGuangliang Zhao 	forget_all_cached_acls(inode);
60*7221fe4cSGuangliang Zhao }
61*7221fe4cSGuangliang Zhao 
62*7221fe4cSGuangliang Zhao struct posix_acl *ceph_get_acl(struct inode *inode, int type)
63*7221fe4cSGuangliang Zhao {
64*7221fe4cSGuangliang Zhao 	int size;
65*7221fe4cSGuangliang Zhao 	const char *name;
66*7221fe4cSGuangliang Zhao 	char *value = NULL;
67*7221fe4cSGuangliang Zhao 	struct posix_acl *acl;
68*7221fe4cSGuangliang Zhao 
69*7221fe4cSGuangliang Zhao 	if (!IS_POSIXACL(inode))
70*7221fe4cSGuangliang Zhao 		return NULL;
71*7221fe4cSGuangliang Zhao 
72*7221fe4cSGuangliang Zhao 	acl = ceph_get_cached_acl(inode, type);
73*7221fe4cSGuangliang Zhao 	if (acl != ACL_NOT_CACHED)
74*7221fe4cSGuangliang Zhao 		return acl;
75*7221fe4cSGuangliang Zhao 
76*7221fe4cSGuangliang Zhao 	switch (type) {
77*7221fe4cSGuangliang Zhao 	case ACL_TYPE_ACCESS:
78*7221fe4cSGuangliang Zhao 		name = POSIX_ACL_XATTR_ACCESS;
79*7221fe4cSGuangliang Zhao 		break;
80*7221fe4cSGuangliang Zhao 	case ACL_TYPE_DEFAULT:
81*7221fe4cSGuangliang Zhao 		name = POSIX_ACL_XATTR_DEFAULT;
82*7221fe4cSGuangliang Zhao 		break;
83*7221fe4cSGuangliang Zhao 	default:
84*7221fe4cSGuangliang Zhao 		BUG();
85*7221fe4cSGuangliang Zhao 	}
86*7221fe4cSGuangliang Zhao 
87*7221fe4cSGuangliang Zhao 	size = __ceph_getxattr(inode, name, "", 0);
88*7221fe4cSGuangliang Zhao 	if (size > 0) {
89*7221fe4cSGuangliang Zhao 		value = kzalloc(size, GFP_NOFS);
90*7221fe4cSGuangliang Zhao 		if (!value)
91*7221fe4cSGuangliang Zhao 			return ERR_PTR(-ENOMEM);
92*7221fe4cSGuangliang Zhao 		size = __ceph_getxattr(inode, name, value, size);
93*7221fe4cSGuangliang Zhao 	}
94*7221fe4cSGuangliang Zhao 
95*7221fe4cSGuangliang Zhao 	if (size > 0)
96*7221fe4cSGuangliang Zhao 		acl = posix_acl_from_xattr(&init_user_ns, value, size);
97*7221fe4cSGuangliang Zhao 	else if (size == -ERANGE || size == -ENODATA || size == 0)
98*7221fe4cSGuangliang Zhao 		acl = NULL;
99*7221fe4cSGuangliang Zhao 	else
100*7221fe4cSGuangliang Zhao 		acl = ERR_PTR(-EIO);
101*7221fe4cSGuangliang Zhao 
102*7221fe4cSGuangliang Zhao 	kfree(value);
103*7221fe4cSGuangliang Zhao 
104*7221fe4cSGuangliang Zhao 	if (!IS_ERR(acl))
105*7221fe4cSGuangliang Zhao 		ceph_set_cached_acl(inode, type, acl);
106*7221fe4cSGuangliang Zhao 
107*7221fe4cSGuangliang Zhao 	return acl;
108*7221fe4cSGuangliang Zhao }
109*7221fe4cSGuangliang Zhao 
110*7221fe4cSGuangliang Zhao static int ceph_set_acl(struct dentry *dentry, struct inode *inode,
111*7221fe4cSGuangliang Zhao 				struct posix_acl *acl, int type)
112*7221fe4cSGuangliang Zhao {
113*7221fe4cSGuangliang Zhao 	int ret = 0, size = 0;
114*7221fe4cSGuangliang Zhao 	const char *name = NULL;
115*7221fe4cSGuangliang Zhao 	char *value = NULL;
116*7221fe4cSGuangliang Zhao 	struct iattr newattrs;
117*7221fe4cSGuangliang Zhao 	umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
118*7221fe4cSGuangliang Zhao 
119*7221fe4cSGuangliang Zhao 	if (acl) {
120*7221fe4cSGuangliang Zhao 		ret = posix_acl_valid(acl);
121*7221fe4cSGuangliang Zhao 		if (ret < 0)
122*7221fe4cSGuangliang Zhao 			goto out;
123*7221fe4cSGuangliang Zhao 	}
124*7221fe4cSGuangliang Zhao 
125*7221fe4cSGuangliang Zhao 	switch (type) {
126*7221fe4cSGuangliang Zhao 	case ACL_TYPE_ACCESS:
127*7221fe4cSGuangliang Zhao 		name = POSIX_ACL_XATTR_ACCESS;
128*7221fe4cSGuangliang Zhao 		if (acl) {
129*7221fe4cSGuangliang Zhao 			ret = posix_acl_equiv_mode(acl, &new_mode);
130*7221fe4cSGuangliang Zhao 			if (ret < 0)
131*7221fe4cSGuangliang Zhao 				goto out;
132*7221fe4cSGuangliang Zhao 			if (ret == 0)
133*7221fe4cSGuangliang Zhao 				acl = NULL;
134*7221fe4cSGuangliang Zhao 		}
135*7221fe4cSGuangliang Zhao 		break;
136*7221fe4cSGuangliang Zhao 	case ACL_TYPE_DEFAULT:
137*7221fe4cSGuangliang Zhao 		if (!S_ISDIR(inode->i_mode)) {
138*7221fe4cSGuangliang Zhao 			ret = acl ? -EINVAL : 0;
139*7221fe4cSGuangliang Zhao 			goto out;
140*7221fe4cSGuangliang Zhao 		}
141*7221fe4cSGuangliang Zhao 		name = POSIX_ACL_XATTR_DEFAULT;
142*7221fe4cSGuangliang Zhao 		break;
143*7221fe4cSGuangliang Zhao 	default:
144*7221fe4cSGuangliang Zhao 		ret = -EINVAL;
145*7221fe4cSGuangliang Zhao 		goto out;
146*7221fe4cSGuangliang Zhao 	}
147*7221fe4cSGuangliang Zhao 
148*7221fe4cSGuangliang Zhao 	if (acl) {
149*7221fe4cSGuangliang Zhao 		size = posix_acl_xattr_size(acl->a_count);
150*7221fe4cSGuangliang Zhao 		value = kmalloc(size, GFP_NOFS);
151*7221fe4cSGuangliang Zhao 		if (!value) {
152*7221fe4cSGuangliang Zhao 			ret = -ENOMEM;
153*7221fe4cSGuangliang Zhao 			goto out;
154*7221fe4cSGuangliang Zhao 		}
155*7221fe4cSGuangliang Zhao 
156*7221fe4cSGuangliang Zhao 		ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
157*7221fe4cSGuangliang Zhao 		if (ret < 0)
158*7221fe4cSGuangliang Zhao 			goto out_free;
159*7221fe4cSGuangliang Zhao 	}
160*7221fe4cSGuangliang Zhao 
161*7221fe4cSGuangliang Zhao 	if (new_mode != old_mode) {
162*7221fe4cSGuangliang Zhao 		newattrs.ia_mode = new_mode;
163*7221fe4cSGuangliang Zhao 		newattrs.ia_valid = ATTR_MODE;
164*7221fe4cSGuangliang Zhao 		ret = ceph_setattr(dentry, &newattrs);
165*7221fe4cSGuangliang Zhao 		if (ret)
166*7221fe4cSGuangliang Zhao 			goto out_free;
167*7221fe4cSGuangliang Zhao 	}
168*7221fe4cSGuangliang Zhao 
169*7221fe4cSGuangliang Zhao 	if (value)
170*7221fe4cSGuangliang Zhao 		ret = __ceph_setxattr(dentry, name, value, size, 0);
171*7221fe4cSGuangliang Zhao 	else
172*7221fe4cSGuangliang Zhao 		ret = __ceph_removexattr(dentry, name);
173*7221fe4cSGuangliang Zhao 
174*7221fe4cSGuangliang Zhao 	if (ret) {
175*7221fe4cSGuangliang Zhao 		if (new_mode != old_mode) {
176*7221fe4cSGuangliang Zhao 			newattrs.ia_mode = old_mode;
177*7221fe4cSGuangliang Zhao 			newattrs.ia_valid = ATTR_MODE;
178*7221fe4cSGuangliang Zhao 			ceph_setattr(dentry, &newattrs);
179*7221fe4cSGuangliang Zhao 		}
180*7221fe4cSGuangliang Zhao 		goto out_free;
181*7221fe4cSGuangliang Zhao 	}
182*7221fe4cSGuangliang Zhao 
183*7221fe4cSGuangliang Zhao 	ceph_set_cached_acl(inode, type, acl);
184*7221fe4cSGuangliang Zhao 
185*7221fe4cSGuangliang Zhao out_free:
186*7221fe4cSGuangliang Zhao 	kfree(value);
187*7221fe4cSGuangliang Zhao out:
188*7221fe4cSGuangliang Zhao 	return ret;
189*7221fe4cSGuangliang Zhao }
190*7221fe4cSGuangliang Zhao 
191*7221fe4cSGuangliang Zhao int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
192*7221fe4cSGuangliang Zhao {
193*7221fe4cSGuangliang Zhao 	struct posix_acl *acl = NULL;
194*7221fe4cSGuangliang Zhao 	int ret = 0;
195*7221fe4cSGuangliang Zhao 
196*7221fe4cSGuangliang Zhao 	if (!S_ISLNK(inode->i_mode)) {
197*7221fe4cSGuangliang Zhao 		if (IS_POSIXACL(dir)) {
198*7221fe4cSGuangliang Zhao 			acl = ceph_get_acl(dir, ACL_TYPE_DEFAULT);
199*7221fe4cSGuangliang Zhao 			if (IS_ERR(acl)) {
200*7221fe4cSGuangliang Zhao 				ret = PTR_ERR(acl);
201*7221fe4cSGuangliang Zhao 				goto out;
202*7221fe4cSGuangliang Zhao 			}
203*7221fe4cSGuangliang Zhao 		}
204*7221fe4cSGuangliang Zhao 
205*7221fe4cSGuangliang Zhao 		if (!acl)
206*7221fe4cSGuangliang Zhao 			inode->i_mode &= ~current_umask();
207*7221fe4cSGuangliang Zhao 	}
208*7221fe4cSGuangliang Zhao 
209*7221fe4cSGuangliang Zhao 	if (IS_POSIXACL(dir) && acl) {
210*7221fe4cSGuangliang Zhao 		if (S_ISDIR(inode->i_mode)) {
211*7221fe4cSGuangliang Zhao 			ret = ceph_set_acl(dentry, inode, acl,
212*7221fe4cSGuangliang Zhao 						ACL_TYPE_DEFAULT);
213*7221fe4cSGuangliang Zhao 			if (ret)
214*7221fe4cSGuangliang Zhao 				goto out_release;
215*7221fe4cSGuangliang Zhao 		}
216*7221fe4cSGuangliang Zhao 		ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
217*7221fe4cSGuangliang Zhao 		if (ret < 0)
218*7221fe4cSGuangliang Zhao 			goto out;
219*7221fe4cSGuangliang Zhao 		else if (ret > 0)
220*7221fe4cSGuangliang Zhao 			ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
221*7221fe4cSGuangliang Zhao 		else
222*7221fe4cSGuangliang Zhao 			cache_no_acl(inode);
223*7221fe4cSGuangliang Zhao 	} else {
224*7221fe4cSGuangliang Zhao 		cache_no_acl(inode);
225*7221fe4cSGuangliang Zhao 	}
226*7221fe4cSGuangliang Zhao 
227*7221fe4cSGuangliang Zhao out_release:
228*7221fe4cSGuangliang Zhao 	posix_acl_release(acl);
229*7221fe4cSGuangliang Zhao out:
230*7221fe4cSGuangliang Zhao 	return ret;
231*7221fe4cSGuangliang Zhao }
232*7221fe4cSGuangliang Zhao 
233*7221fe4cSGuangliang Zhao int ceph_acl_chmod(struct dentry *dentry, struct inode *inode)
234*7221fe4cSGuangliang Zhao {
235*7221fe4cSGuangliang Zhao 	struct posix_acl *acl;
236*7221fe4cSGuangliang Zhao 	int ret = 0;
237*7221fe4cSGuangliang Zhao 
238*7221fe4cSGuangliang Zhao 	if (S_ISLNK(inode->i_mode)) {
239*7221fe4cSGuangliang Zhao 		ret = -EOPNOTSUPP;
240*7221fe4cSGuangliang Zhao 		goto out;
241*7221fe4cSGuangliang Zhao 	}
242*7221fe4cSGuangliang Zhao 
243*7221fe4cSGuangliang Zhao 	if (!IS_POSIXACL(inode))
244*7221fe4cSGuangliang Zhao 		goto out;
245*7221fe4cSGuangliang Zhao 
246*7221fe4cSGuangliang Zhao 	acl = ceph_get_acl(inode, ACL_TYPE_ACCESS);
247*7221fe4cSGuangliang Zhao 	if (IS_ERR_OR_NULL(acl)) {
248*7221fe4cSGuangliang Zhao 		ret = PTR_ERR(acl);
249*7221fe4cSGuangliang Zhao 		goto out;
250*7221fe4cSGuangliang Zhao 	}
251*7221fe4cSGuangliang Zhao 
252*7221fe4cSGuangliang Zhao 	ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
253*7221fe4cSGuangliang Zhao 	if (ret)
254*7221fe4cSGuangliang Zhao 		goto out;
255*7221fe4cSGuangliang Zhao 	ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
256*7221fe4cSGuangliang Zhao 	posix_acl_release(acl);
257*7221fe4cSGuangliang Zhao out:
258*7221fe4cSGuangliang Zhao 	return ret;
259*7221fe4cSGuangliang Zhao }
260*7221fe4cSGuangliang Zhao 
261*7221fe4cSGuangliang Zhao static int ceph_xattr_acl_get(struct dentry *dentry, const char *name,
262*7221fe4cSGuangliang Zhao 				void *value, size_t size, int type)
263*7221fe4cSGuangliang Zhao {
264*7221fe4cSGuangliang Zhao 	struct posix_acl *acl;
265*7221fe4cSGuangliang Zhao 	int ret = 0;
266*7221fe4cSGuangliang Zhao 
267*7221fe4cSGuangliang Zhao 	if (!IS_POSIXACL(dentry->d_inode))
268*7221fe4cSGuangliang Zhao 		return -EOPNOTSUPP;
269*7221fe4cSGuangliang Zhao 
270*7221fe4cSGuangliang Zhao 	acl = ceph_get_acl(dentry->d_inode, type);
271*7221fe4cSGuangliang Zhao 	if (IS_ERR(acl))
272*7221fe4cSGuangliang Zhao 		return PTR_ERR(acl);
273*7221fe4cSGuangliang Zhao 	if (acl == NULL)
274*7221fe4cSGuangliang Zhao 		return -ENODATA;
275*7221fe4cSGuangliang Zhao 
276*7221fe4cSGuangliang Zhao 	ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
277*7221fe4cSGuangliang Zhao 	posix_acl_release(acl);
278*7221fe4cSGuangliang Zhao 
279*7221fe4cSGuangliang Zhao 	return ret;
280*7221fe4cSGuangliang Zhao }
281*7221fe4cSGuangliang Zhao 
282*7221fe4cSGuangliang Zhao static int ceph_xattr_acl_set(struct dentry *dentry, const char *name,
283*7221fe4cSGuangliang Zhao 			const void *value, size_t size, int flags, int type)
284*7221fe4cSGuangliang Zhao {
285*7221fe4cSGuangliang Zhao 	int ret = 0;
286*7221fe4cSGuangliang Zhao 	struct posix_acl *acl = NULL;
287*7221fe4cSGuangliang Zhao 
288*7221fe4cSGuangliang Zhao 	if (!inode_owner_or_capable(dentry->d_inode)) {
289*7221fe4cSGuangliang Zhao 		ret = -EPERM;
290*7221fe4cSGuangliang Zhao 		goto out;
291*7221fe4cSGuangliang Zhao 	}
292*7221fe4cSGuangliang Zhao 
293*7221fe4cSGuangliang Zhao 	if (!IS_POSIXACL(dentry->d_inode)) {
294*7221fe4cSGuangliang Zhao 		ret = -EOPNOTSUPP;
295*7221fe4cSGuangliang Zhao 		goto out;
296*7221fe4cSGuangliang Zhao 	}
297*7221fe4cSGuangliang Zhao 
298*7221fe4cSGuangliang Zhao 	if (value) {
299*7221fe4cSGuangliang Zhao 		acl = posix_acl_from_xattr(&init_user_ns, value, size);
300*7221fe4cSGuangliang Zhao 		if (IS_ERR(acl)) {
301*7221fe4cSGuangliang Zhao 			ret = PTR_ERR(acl);
302*7221fe4cSGuangliang Zhao 			goto out;
303*7221fe4cSGuangliang Zhao 		}
304*7221fe4cSGuangliang Zhao 
305*7221fe4cSGuangliang Zhao 		if (acl) {
306*7221fe4cSGuangliang Zhao 			ret = posix_acl_valid(acl);
307*7221fe4cSGuangliang Zhao 			if (ret)
308*7221fe4cSGuangliang Zhao 				goto out_release;
309*7221fe4cSGuangliang Zhao 		}
310*7221fe4cSGuangliang Zhao 	}
311*7221fe4cSGuangliang Zhao 
312*7221fe4cSGuangliang Zhao 	ret = ceph_set_acl(dentry, dentry->d_inode, acl, type);
313*7221fe4cSGuangliang Zhao 
314*7221fe4cSGuangliang Zhao out_release:
315*7221fe4cSGuangliang Zhao 	posix_acl_release(acl);
316*7221fe4cSGuangliang Zhao out:
317*7221fe4cSGuangliang Zhao 	return ret;
318*7221fe4cSGuangliang Zhao }
319*7221fe4cSGuangliang Zhao 
320*7221fe4cSGuangliang Zhao const struct xattr_handler ceph_xattr_acl_default_handler = {
321*7221fe4cSGuangliang Zhao 	.prefix = POSIX_ACL_XATTR_DEFAULT,
322*7221fe4cSGuangliang Zhao 	.flags  = ACL_TYPE_DEFAULT,
323*7221fe4cSGuangliang Zhao 	.get    = ceph_xattr_acl_get,
324*7221fe4cSGuangliang Zhao 	.set    = ceph_xattr_acl_set,
325*7221fe4cSGuangliang Zhao };
326*7221fe4cSGuangliang Zhao 
327*7221fe4cSGuangliang Zhao const struct xattr_handler ceph_xattr_acl_access_handler = {
328*7221fe4cSGuangliang Zhao 	.prefix = POSIX_ACL_XATTR_ACCESS,
329*7221fe4cSGuangliang Zhao 	.flags  = ACL_TYPE_ACCESS,
330*7221fe4cSGuangliang Zhao 	.get    = ceph_xattr_acl_get,
331*7221fe4cSGuangliang Zhao 	.set    = ceph_xattr_acl_set,
332*7221fe4cSGuangliang Zhao };
333