• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2012, Intel Corporation.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/obdclass/acl.c
37  *
38  * Lustre Access Control List.
39  *
40  * Author: Fan Yong <fanyong@clusterfs.com>
41  */
42 
43 #define DEBUG_SUBSYSTEM S_SEC
44 #include "../include/lu_object.h"
45 #include "../include/lustre_acl.h"
46 #include "../include/lustre_eacl.h"
47 #include "../include/obd_support.h"
48 
49 #ifdef CONFIG_FS_POSIX_ACL
50 
51 #define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
52 
53 enum {
54 	ES_UNK  = 0,    /* unknown stat */
55 	ES_UNC  = 1,    /* ACL entry is not changed */
56 	ES_MOD  = 2,    /* ACL entry is modified */
57 	ES_ADD  = 3,    /* ACL entry is added */
58 	ES_DEL  = 4     /* ACL entry is deleted */
59 };
60 
lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry * d,ext_acl_xattr_entry * s)61 static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
62 					    ext_acl_xattr_entry *s)
63 {
64 	d->e_tag	= le16_to_cpu(s->e_tag);
65 	d->e_perm       = le16_to_cpu(s->e_perm);
66 	d->e_id	 = le32_to_cpu(s->e_id);
67 	d->e_stat       = le32_to_cpu(s->e_stat);
68 }
69 
lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry * d,ext_acl_xattr_entry * s)70 static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
71 					    ext_acl_xattr_entry *s)
72 {
73 	d->e_tag	= cpu_to_le16(s->e_tag);
74 	d->e_perm       = cpu_to_le16(s->e_perm);
75 	d->e_id	 = cpu_to_le32(s->e_id);
76 	d->e_stat       = cpu_to_le32(s->e_stat);
77 }
78 
lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry * d,posix_acl_xattr_entry * s)79 static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
80 					      posix_acl_xattr_entry *s)
81 {
82 	d->e_tag	= le16_to_cpu(s->e_tag);
83 	d->e_perm       = le16_to_cpu(s->e_perm);
84 	d->e_id	 = le32_to_cpu(s->e_id);
85 }
86 
lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry * d,posix_acl_xattr_entry * s)87 static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
88 					      posix_acl_xattr_entry *s)
89 {
90 	d->e_tag	= cpu_to_le16(s->e_tag);
91 	d->e_perm       = cpu_to_le16(s->e_perm);
92 	d->e_id	 = cpu_to_le32(s->e_id);
93 }
94 
95 /* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header ** header,int old_count,int new_count)96 static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
97 					       int old_count, int new_count)
98 {
99 	int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
100 	int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
101 	posix_acl_xattr_header *new;
102 
103 	if (unlikely(old_count <= new_count))
104 		return old_size;
105 
106 	new = kmemdup(*header, new_size, GFP_NOFS);
107 	if (unlikely(new == NULL))
108 		return -ENOMEM;
109 
110 	kfree(*header);
111 	*header = new;
112 	return new_size;
113 }
114 
115 /* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header ** header,int old_count)116 static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
117 					     int old_count)
118 {
119 	int ext_count = le32_to_cpu((*header)->a_count);
120 	int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
121 	ext_acl_xattr_header *new;
122 
123 	if (unlikely(old_count <= ext_count))
124 		return 0;
125 
126 	new = kmemdup(*header, ext_size, GFP_NOFS);
127 	if (unlikely(new == NULL))
128 		return -ENOMEM;
129 
130 	kfree(*header);
131 	*header = new;
132 	return 0;
133 }
134 
135 /*
136  * Generate new extended ACL based on the posix ACL.
137  */
138 ext_acl_xattr_header *
lustre_posix_acl_xattr_2ext(posix_acl_xattr_header * header,int size)139 lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
140 {
141 	int count, i, esize;
142 	ext_acl_xattr_header *new;
143 
144 	if (unlikely(size < 0))
145 		return ERR_PTR(-EINVAL);
146 	else if (!size)
147 		count = 0;
148 	else
149 		count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
150 	esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
151 	new = kzalloc(esize, GFP_NOFS);
152 	if (unlikely(new == NULL))
153 		return ERR_PTR(-ENOMEM);
154 
155 	new->a_count = cpu_to_le32(count);
156 	for (i = 0; i < count; i++) {
157 		new->a_entries[i].e_tag  = header->a_entries[i].e_tag;
158 		new->a_entries[i].e_perm = header->a_entries[i].e_perm;
159 		new->a_entries[i].e_id   = header->a_entries[i].e_id;
160 		new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
161 	}
162 
163 	return new;
164 }
165 EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
166 
167 /*
168  * Filter out the "nobody" entries in the posix ACL.
169  */
lustre_posix_acl_xattr_filter(posix_acl_xattr_header * header,size_t size,posix_acl_xattr_header ** out)170 int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
171 				  posix_acl_xattr_header **out)
172 {
173 	int count, i, j, rc = 0;
174 	__u32 id;
175 	posix_acl_xattr_header *new;
176 
177 	if (!size)
178 		return 0;
179 	if (size < sizeof(*new))
180 		return -EINVAL;
181 
182 	new = kzalloc(size, GFP_NOFS);
183 	if (unlikely(new == NULL))
184 		return -ENOMEM;
185 
186 	new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
187 	count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
188 	for (i = 0, j = 0; i < count; i++) {
189 		id = le32_to_cpu(header->a_entries[i].e_id);
190 		switch (le16_to_cpu(header->a_entries[i].e_tag)) {
191 		case ACL_USER_OBJ:
192 		case ACL_GROUP_OBJ:
193 		case ACL_MASK:
194 		case ACL_OTHER:
195 			if (id != ACL_UNDEFINED_ID) {
196 				rc = -EIO;
197 				goto _out;
198 			}
199 
200 			memcpy(&new->a_entries[j++], &header->a_entries[i],
201 			       sizeof(posix_acl_xattr_entry));
202 			break;
203 		case ACL_USER:
204 			if (id != NOBODY_UID)
205 				memcpy(&new->a_entries[j++],
206 				       &header->a_entries[i],
207 				       sizeof(posix_acl_xattr_entry));
208 			break;
209 		case ACL_GROUP:
210 			if (id != NOBODY_GID)
211 				memcpy(&new->a_entries[j++],
212 				       &header->a_entries[i],
213 				       sizeof(posix_acl_xattr_entry));
214 			break;
215 		default:
216 			rc = -EIO;
217 			goto _out;
218 		}
219 	}
220 
221 	/* free unused space. */
222 	rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
223 	if (rc >= 0) {
224 		size = rc;
225 		*out = new;
226 		rc = 0;
227 	}
228 
229 _out:
230 	if (rc) {
231 		kfree(new);
232 		size = rc;
233 	}
234 	return size;
235 }
236 EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
237 
238 /*
239  * Release the posix ACL space.
240  */
lustre_posix_acl_xattr_free(posix_acl_xattr_header * header,int size)241 void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
242 {
243 	kfree(header);
244 }
245 EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
246 
247 /*
248  * Release the extended ACL space.
249  */
lustre_ext_acl_xattr_free(ext_acl_xattr_header * header)250 void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
251 {
252 	kfree(header);
253 }
254 EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
255 
256 static ext_acl_xattr_entry *
lustre_ext_acl_xattr_search(ext_acl_xattr_header * header,posix_acl_xattr_entry * entry,int * pos)257 lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
258 			    posix_acl_xattr_entry *entry, int *pos)
259 {
260 	int once, start, end, i, j, count = le32_to_cpu(header->a_count);
261 
262 	once = 0;
263 	start = *pos;
264 	end = count;
265 
266 again:
267 	for (i = start; i < end; i++) {
268 		if (header->a_entries[i].e_tag == entry->e_tag &&
269 		    header->a_entries[i].e_id == entry->e_id) {
270 			j = i;
271 			if (++i >= count)
272 				i = 0;
273 			*pos = i;
274 			return &header->a_entries[j];
275 		}
276 	}
277 
278 	if (!once) {
279 		once = 1;
280 		start = 0;
281 		end = *pos;
282 		goto again;
283 	}
284 
285 	return NULL;
286 }
287 
288 /*
289  * Merge the posix ACL and the extended ACL into new extended ACL.
290  */
291 ext_acl_xattr_header *
lustre_acl_xattr_merge2ext(posix_acl_xattr_header * posix_header,int size,ext_acl_xattr_header * ext_header)292 lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
293 			   ext_acl_xattr_header *ext_header)
294 {
295 	int ori_ext_count, posix_count, ext_count, ext_size;
296 	int i, j, pos = 0, rc = 0;
297 	posix_acl_xattr_entry pae;
298 	ext_acl_xattr_header *new;
299 	ext_acl_xattr_entry *ee, eae;
300 
301 	if (unlikely(size < 0))
302 		return ERR_PTR(-EINVAL);
303 	else if (!size)
304 		posix_count = 0;
305 	else
306 		posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
307 	ori_ext_count = le32_to_cpu(ext_header->a_count);
308 	ext_count = posix_count + ori_ext_count;
309 	ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
310 
311 	new = kzalloc(ext_size, GFP_NOFS);
312 	if (unlikely(new == NULL))
313 		return ERR_PTR(-ENOMEM);
314 
315 	for (i = 0, j = 0; i < posix_count; i++) {
316 		lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
317 		switch (pae.e_tag) {
318 		case ACL_USER_OBJ:
319 		case ACL_GROUP_OBJ:
320 		case ACL_MASK:
321 		case ACL_OTHER:
322 			if (pae.e_id != ACL_UNDEFINED_ID) {
323 				rc = -EIO;
324 				goto out;
325 		}
326 		case ACL_USER:
327 			/* ignore "nobody" entry. */
328 			if (pae.e_id == NOBODY_UID)
329 				break;
330 
331 			new->a_entries[j].e_tag =
332 					posix_header->a_entries[i].e_tag;
333 			new->a_entries[j].e_perm =
334 					posix_header->a_entries[i].e_perm;
335 			new->a_entries[j].e_id =
336 					posix_header->a_entries[i].e_id;
337 			ee = lustre_ext_acl_xattr_search(ext_header,
338 					&posix_header->a_entries[i], &pos);
339 			if (ee) {
340 				if (posix_header->a_entries[i].e_perm !=
341 								ee->e_perm)
342 					/* entry modified. */
343 					ee->e_stat =
344 					new->a_entries[j++].e_stat =
345 							cpu_to_le32(ES_MOD);
346 				else
347 					/* entry unchanged. */
348 					ee->e_stat =
349 					new->a_entries[j++].e_stat =
350 							cpu_to_le32(ES_UNC);
351 			} else {
352 				/* new entry. */
353 				new->a_entries[j++].e_stat =
354 							cpu_to_le32(ES_ADD);
355 			}
356 			break;
357 		case ACL_GROUP:
358 			/* ignore "nobody" entry. */
359 			if (pae.e_id == NOBODY_GID)
360 				break;
361 			new->a_entries[j].e_tag =
362 					posix_header->a_entries[i].e_tag;
363 			new->a_entries[j].e_perm =
364 					posix_header->a_entries[i].e_perm;
365 			new->a_entries[j].e_id =
366 					posix_header->a_entries[i].e_id;
367 			ee = lustre_ext_acl_xattr_search(ext_header,
368 					&posix_header->a_entries[i], &pos);
369 			if (ee) {
370 				if (posix_header->a_entries[i].e_perm !=
371 								ee->e_perm)
372 					/* entry modified. */
373 					ee->e_stat =
374 					new->a_entries[j++].e_stat =
375 							cpu_to_le32(ES_MOD);
376 				else
377 					/* entry unchanged. */
378 					ee->e_stat =
379 					new->a_entries[j++].e_stat =
380 							cpu_to_le32(ES_UNC);
381 			} else {
382 				/* new entry. */
383 				new->a_entries[j++].e_stat =
384 							cpu_to_le32(ES_ADD);
385 			}
386 			break;
387 		default:
388 			rc = -EIO;
389 			goto out;
390 		}
391 	}
392 
393 	/* process deleted entries. */
394 	for (i = 0; i < ori_ext_count; i++) {
395 		lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
396 		if (eae.e_stat == ES_UNK) {
397 			/* ignore "nobody" entry. */
398 			if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
399 			    (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
400 				continue;
401 
402 			new->a_entries[j].e_tag =
403 						ext_header->a_entries[i].e_tag;
404 			new->a_entries[j].e_perm =
405 						ext_header->a_entries[i].e_perm;
406 			new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
407 			new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
408 		}
409 	}
410 
411 	new->a_count = cpu_to_le32(j);
412 	/* free unused space. */
413 	rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
414 
415 out:
416 	if (rc) {
417 		kfree(new);
418 		new = ERR_PTR(rc);
419 	}
420 	return new;
421 }
422 EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
423 
424 #endif
425