xref: /openbmc/linux/fs/ceph/acl.c (revision eb3fcf00)
1 /*
2  * linux/fs/ceph/acl.c
3  *
4  * Copyright (C) 2013 Guangliang Zhao, <lucienchao@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public
8  * License v2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 021110-1307, USA.
19  */
20 
21 #include <linux/ceph/ceph_debug.h>
22 #include <linux/fs.h>
23 #include <linux/string.h>
24 #include <linux/xattr.h>
25 #include <linux/posix_acl_xattr.h>
26 #include <linux/posix_acl.h>
27 #include <linux/sched.h>
28 #include <linux/slab.h>
29 
30 #include "super.h"
31 
32 static inline void ceph_set_cached_acl(struct inode *inode,
33 					int type, struct posix_acl *acl)
34 {
35 	struct ceph_inode_info *ci = ceph_inode(inode);
36 
37 	spin_lock(&ci->i_ceph_lock);
38 	if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 0))
39 		set_cached_acl(inode, type, acl);
40 	spin_unlock(&ci->i_ceph_lock);
41 }
42 
43 struct posix_acl *ceph_get_acl(struct inode *inode, int type)
44 {
45 	int size;
46 	const char *name;
47 	char *value = NULL;
48 	struct posix_acl *acl;
49 
50 	switch (type) {
51 	case ACL_TYPE_ACCESS:
52 		name = POSIX_ACL_XATTR_ACCESS;
53 		break;
54 	case ACL_TYPE_DEFAULT:
55 		name = POSIX_ACL_XATTR_DEFAULT;
56 		break;
57 	default:
58 		BUG();
59 	}
60 
61 	size = __ceph_getxattr(inode, name, "", 0);
62 	if (size > 0) {
63 		value = kzalloc(size, GFP_NOFS);
64 		if (!value)
65 			return ERR_PTR(-ENOMEM);
66 		size = __ceph_getxattr(inode, name, value, size);
67 	}
68 
69 	if (size > 0)
70 		acl = posix_acl_from_xattr(&init_user_ns, value, size);
71 	else if (size == -ERANGE || size == -ENODATA || size == 0)
72 		acl = NULL;
73 	else
74 		acl = ERR_PTR(-EIO);
75 
76 	kfree(value);
77 
78 	if (!IS_ERR(acl))
79 		ceph_set_cached_acl(inode, type, acl);
80 
81 	return acl;
82 }
83 
84 int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
85 {
86 	int ret = 0, size = 0;
87 	const char *name = NULL;
88 	char *value = NULL;
89 	struct iattr newattrs;
90 	umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
91 	struct dentry *dentry;
92 
93 	switch (type) {
94 	case ACL_TYPE_ACCESS:
95 		name = POSIX_ACL_XATTR_ACCESS;
96 		if (acl) {
97 			ret = posix_acl_equiv_mode(acl, &new_mode);
98 			if (ret < 0)
99 				goto out;
100 			if (ret == 0)
101 				acl = NULL;
102 		}
103 		break;
104 	case ACL_TYPE_DEFAULT:
105 		if (!S_ISDIR(inode->i_mode)) {
106 			ret = acl ? -EINVAL : 0;
107 			goto out;
108 		}
109 		name = POSIX_ACL_XATTR_DEFAULT;
110 		break;
111 	default:
112 		ret = -EINVAL;
113 		goto out;
114 	}
115 
116 	if (acl) {
117 		size = posix_acl_xattr_size(acl->a_count);
118 		value = kmalloc(size, GFP_NOFS);
119 		if (!value) {
120 			ret = -ENOMEM;
121 			goto out;
122 		}
123 
124 		ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
125 		if (ret < 0)
126 			goto out_free;
127 	}
128 
129 	dentry = d_find_alias(inode);
130 	if (new_mode != old_mode) {
131 		newattrs.ia_mode = new_mode;
132 		newattrs.ia_valid = ATTR_MODE;
133 		ret = ceph_setattr(dentry, &newattrs);
134 		if (ret)
135 			goto out_dput;
136 	}
137 
138 	ret = __ceph_setxattr(dentry, name, value, size, 0);
139 	if (ret) {
140 		if (new_mode != old_mode) {
141 			newattrs.ia_mode = old_mode;
142 			newattrs.ia_valid = ATTR_MODE;
143 			ceph_setattr(dentry, &newattrs);
144 		}
145 		goto out_dput;
146 	}
147 
148 	ceph_set_cached_acl(inode, type, acl);
149 
150 out_dput:
151 	dput(dentry);
152 out_free:
153 	kfree(value);
154 out:
155 	return ret;
156 }
157 
158 int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
159 		       struct ceph_acls_info *info)
160 {
161 	struct posix_acl *acl, *default_acl;
162 	size_t val_size1 = 0, val_size2 = 0;
163 	struct ceph_pagelist *pagelist = NULL;
164 	void *tmp_buf = NULL;
165 	int err;
166 
167 	err = posix_acl_create(dir, mode, &default_acl, &acl);
168 	if (err)
169 		return err;
170 
171 	if (acl) {
172 		int ret = posix_acl_equiv_mode(acl, mode);
173 		if (ret < 0)
174 			goto out_err;
175 		if (ret == 0) {
176 			posix_acl_release(acl);
177 			acl = NULL;
178 		}
179 	}
180 
181 	if (!default_acl && !acl)
182 		return 0;
183 
184 	if (acl)
185 		val_size1 = posix_acl_xattr_size(acl->a_count);
186 	if (default_acl)
187 		val_size2 = posix_acl_xattr_size(default_acl->a_count);
188 
189 	err = -ENOMEM;
190 	tmp_buf = kmalloc(max(val_size1, val_size2), GFP_KERNEL);
191 	if (!tmp_buf)
192 		goto out_err;
193 	pagelist = kmalloc(sizeof(struct ceph_pagelist), GFP_KERNEL);
194 	if (!pagelist)
195 		goto out_err;
196 	ceph_pagelist_init(pagelist);
197 
198 	err = ceph_pagelist_reserve(pagelist, PAGE_SIZE);
199 	if (err)
200 		goto out_err;
201 
202 	ceph_pagelist_encode_32(pagelist, acl && default_acl ? 2 : 1);
203 
204 	if (acl) {
205 		size_t len = strlen(POSIX_ACL_XATTR_ACCESS);
206 		err = ceph_pagelist_reserve(pagelist, len + val_size1 + 8);
207 		if (err)
208 			goto out_err;
209 		ceph_pagelist_encode_string(pagelist, POSIX_ACL_XATTR_ACCESS,
210 					    len);
211 		err = posix_acl_to_xattr(&init_user_ns, acl,
212 					 tmp_buf, val_size1);
213 		if (err < 0)
214 			goto out_err;
215 		ceph_pagelist_encode_32(pagelist, val_size1);
216 		ceph_pagelist_append(pagelist, tmp_buf, val_size1);
217 	}
218 	if (default_acl) {
219 		size_t len = strlen(POSIX_ACL_XATTR_DEFAULT);
220 		err = ceph_pagelist_reserve(pagelist, len + val_size2 + 8);
221 		if (err)
222 			goto out_err;
223 		err = ceph_pagelist_encode_string(pagelist,
224 						  POSIX_ACL_XATTR_DEFAULT, len);
225 		err = posix_acl_to_xattr(&init_user_ns, default_acl,
226 					 tmp_buf, val_size2);
227 		if (err < 0)
228 			goto out_err;
229 		ceph_pagelist_encode_32(pagelist, val_size2);
230 		ceph_pagelist_append(pagelist, tmp_buf, val_size2);
231 	}
232 
233 	kfree(tmp_buf);
234 
235 	info->acl = acl;
236 	info->default_acl = default_acl;
237 	info->pagelist = pagelist;
238 	return 0;
239 
240 out_err:
241 	posix_acl_release(acl);
242 	posix_acl_release(default_acl);
243 	kfree(tmp_buf);
244 	if (pagelist)
245 		ceph_pagelist_release(pagelist);
246 	return err;
247 }
248 
249 void ceph_init_inode_acls(struct inode* inode, struct ceph_acls_info *info)
250 {
251 	if (!inode)
252 		return;
253 	ceph_set_cached_acl(inode, ACL_TYPE_ACCESS, info->acl);
254 	ceph_set_cached_acl(inode, ACL_TYPE_DEFAULT, info->default_acl);
255 }
256 
257 void ceph_release_acls_info(struct ceph_acls_info *info)
258 {
259 	posix_acl_release(info->acl);
260 	posix_acl_release(info->default_acl);
261 	if (info->pagelist)
262 		ceph_pagelist_release(info->pagelist);
263 }
264