staging/lustre: use kmemdup rather than duplicating its implementation
[deliverable/linux.git] / drivers / staging / lustre / lustre / obdclass / acl.c
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
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
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
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
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
96 /* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
97 static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
98 int old_count, int new_count)
99 {
100 int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
101 int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
102 posix_acl_xattr_header *new;
103
104 if (unlikely(old_count <= new_count))
105 return old_size;
106
107 new = kmemdup(*header, new_size, GFP_NOFS);
108 if (unlikely(new == NULL))
109 return -ENOMEM;
110
111 kfree(*header);
112 *header = new;
113 return new_size;
114 }
115
116 /* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
117 static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
118 int old_count)
119 {
120 int ext_count = le32_to_cpu((*header)->a_count);
121 int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
122 ext_acl_xattr_header *new;
123
124 if (unlikely(old_count <= ext_count))
125 return 0;
126
127 new = kmemdup(*header, ext_size, GFP_NOFS);
128 if (unlikely(new == NULL))
129 return -ENOMEM;
130
131 kfree(*header);
132 *header = new;
133 return 0;
134 }
135
136 /*
137 * Generate new extended ACL based on the posix ACL.
138 */
139 ext_acl_xattr_header *
140 lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
141 {
142 int count, i, esize;
143 ext_acl_xattr_header *new;
144
145 if (unlikely(size < 0))
146 return ERR_PTR(-EINVAL);
147 else if (!size)
148 count = 0;
149 else
150 count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
151 esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
152 new = kzalloc(esize, GFP_NOFS);
153 if (unlikely(new == NULL))
154 return ERR_PTR(-ENOMEM);
155
156 new->a_count = cpu_to_le32(count);
157 for (i = 0; i < count; i++) {
158 new->a_entries[i].e_tag = header->a_entries[i].e_tag;
159 new->a_entries[i].e_perm = header->a_entries[i].e_perm;
160 new->a_entries[i].e_id = header->a_entries[i].e_id;
161 new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
162 }
163
164 return new;
165 }
166 EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
167
168 /*
169 * Filter out the "nobody" entries in the posix ACL.
170 */
171 int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
172 posix_acl_xattr_header **out)
173 {
174 int count, i, j, rc = 0;
175 __u32 id;
176 posix_acl_xattr_header *new;
177
178 if (!size)
179 return 0;
180 if (size < sizeof(*new))
181 return -EINVAL;
182
183 new = kzalloc(size, GFP_NOFS);
184 if (unlikely(new == NULL))
185 return -ENOMEM;
186
187 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
188 count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
189 for (i = 0, j = 0; i < count; i++) {
190 id = le32_to_cpu(header->a_entries[i].e_id);
191 switch (le16_to_cpu(header->a_entries[i].e_tag)) {
192 case ACL_USER_OBJ:
193 case ACL_GROUP_OBJ:
194 case ACL_MASK:
195 case ACL_OTHER:
196 if (id != ACL_UNDEFINED_ID) {
197 rc = -EIO;
198 goto _out;
199 }
200
201 memcpy(&new->a_entries[j++], &header->a_entries[i],
202 sizeof(posix_acl_xattr_entry));
203 break;
204 case ACL_USER:
205 if (id != NOBODY_UID)
206 memcpy(&new->a_entries[j++],
207 &header->a_entries[i],
208 sizeof(posix_acl_xattr_entry));
209 break;
210 case ACL_GROUP:
211 if (id != NOBODY_GID)
212 memcpy(&new->a_entries[j++],
213 &header->a_entries[i],
214 sizeof(posix_acl_xattr_entry));
215 break;
216 default:
217 rc = -EIO;
218 goto _out;
219 }
220 }
221
222 /* free unused space. */
223 rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
224 if (rc >= 0) {
225 size = rc;
226 *out = new;
227 rc = 0;
228 }
229
230 _out:
231 if (rc) {
232 kfree(new);
233 size = rc;
234 }
235 return size;
236 }
237 EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
238
239 /*
240 * Release the posix ACL space.
241 */
242 void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
243 {
244 kfree(header);
245 }
246 EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
247
248 /*
249 * Release the extended ACL space.
250 */
251 void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
252 {
253 kfree(header);
254 }
255 EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
256
257 static ext_acl_xattr_entry *
258 lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
259 posix_acl_xattr_entry *entry, int *pos)
260 {
261 int once, start, end, i, j, count = le32_to_cpu(header->a_count);
262
263 once = 0;
264 start = *pos;
265 end = count;
266
267 again:
268 for (i = start; i < end; i++) {
269 if (header->a_entries[i].e_tag == entry->e_tag &&
270 header->a_entries[i].e_id == entry->e_id) {
271 j = i;
272 if (++i >= count)
273 i = 0;
274 *pos = i;
275 return &header->a_entries[j];
276 }
277 }
278
279 if (!once) {
280 once = 1;
281 start = 0;
282 end = *pos;
283 goto again;
284 }
285
286 return NULL;
287 }
288
289 /*
290 * Merge the posix ACL and the extended ACL into new posix ACL.
291 */
292 int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
293 ext_acl_xattr_header *ext_header,
294 posix_acl_xattr_header **out)
295 {
296 int posix_count, posix_size, i, j;
297 int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
298 posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
299 posix_acl_xattr_header *new;
300 ext_acl_xattr_entry *ee, ae;
301
302 lustre_posix_acl_cpu_to_le(&pe, &pe);
303 ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
304 if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
305 /* there are only base ACL entries at most. */
306 posix_count = 3;
307 posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
308 new = kzalloc(posix_size, GFP_NOFS);
309 if (unlikely(new == NULL))
310 return -ENOMEM;
311
312 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
313 for (i = 0, j = 0; i < ext_count; i++) {
314 lustre_ext_acl_le_to_cpu(&ae,
315 &ext_header->a_entries[i]);
316 switch (ae.e_tag) {
317 case ACL_USER_OBJ:
318 case ACL_GROUP_OBJ:
319 case ACL_OTHER:
320 if (ae.e_id != ACL_UNDEFINED_ID) {
321 rc = -EIO;
322 goto _out;
323 }
324
325 if (ae.e_stat != ES_DEL) {
326 new->a_entries[j].e_tag =
327 ext_header->a_entries[i].e_tag;
328 new->a_entries[j].e_perm =
329 ext_header->a_entries[i].e_perm;
330 new->a_entries[j++].e_id =
331 ext_header->a_entries[i].e_id;
332 }
333 break;
334 case ACL_MASK:
335 case ACL_USER:
336 case ACL_GROUP:
337 if (ae.e_stat == ES_DEL)
338 break;
339 default:
340 rc = -EIO;
341 goto _out;
342 }
343 }
344 } else {
345 /* maybe there are valid ACL_USER or ACL_GROUP entries in the
346 * original server-side ACL, they are regarded as ES_UNC stat.*/
347 int ori_posix_count;
348
349 if (unlikely(size < 0))
350 return -EINVAL;
351 else if (!size)
352 ori_posix_count = 0;
353 else
354 ori_posix_count =
355 CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
356 posix_count = ori_posix_count + ext_count;
357 posix_size =
358 CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
359 new = kzalloc(posix_size, GFP_NOFS);
360 if (unlikely(new == NULL))
361 return -ENOMEM;
362
363 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
364 /* 1. process the unchanged ACL entries
365 * in the original server-side ACL. */
366 pos = 0;
367 for (i = 0, j = 0; i < ori_posix_count; i++) {
368 ee = lustre_ext_acl_xattr_search(ext_header,
369 &posix_header->a_entries[i], &pos);
370 if (ee == NULL)
371 memcpy(&new->a_entries[j++],
372 &posix_header->a_entries[i],
373 sizeof(posix_acl_xattr_entry));
374 }
375
376 /* 2. process the non-deleted entries
377 * from client-side extended ACL. */
378 for (i = 0; i < ext_count; i++) {
379 if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
380 ES_DEL) {
381 new->a_entries[j].e_tag =
382 ext_header->a_entries[i].e_tag;
383 new->a_entries[j].e_perm =
384 ext_header->a_entries[i].e_perm;
385 new->a_entries[j++].e_id =
386 ext_header->a_entries[i].e_id;
387 }
388 }
389 }
390
391 /* free unused space. */
392 rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
393 if (rc >= 0) {
394 posix_size = rc;
395 *out = new;
396 rc = 0;
397 }
398
399 _out:
400 if (rc) {
401 kfree(new);
402 posix_size = rc;
403 }
404 return posix_size;
405 }
406 EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
407
408 /*
409 * Merge the posix ACL and the extended ACL into new extended ACL.
410 */
411 ext_acl_xattr_header *
412 lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
413 ext_acl_xattr_header *ext_header)
414 {
415 int ori_ext_count, posix_count, ext_count, ext_size;
416 int i, j, pos = 0, rc = 0;
417 posix_acl_xattr_entry pae;
418 ext_acl_xattr_header *new;
419 ext_acl_xattr_entry *ee, eae;
420
421 if (unlikely(size < 0))
422 return ERR_PTR(-EINVAL);
423 else if (!size)
424 posix_count = 0;
425 else
426 posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
427 ori_ext_count = le32_to_cpu(ext_header->a_count);
428 ext_count = posix_count + ori_ext_count;
429 ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
430
431 new = kzalloc(ext_size, GFP_NOFS);
432 if (unlikely(new == NULL))
433 return ERR_PTR(-ENOMEM);
434
435 for (i = 0, j = 0; i < posix_count; i++) {
436 lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
437 switch (pae.e_tag) {
438 case ACL_USER_OBJ:
439 case ACL_GROUP_OBJ:
440 case ACL_MASK:
441 case ACL_OTHER:
442 if (pae.e_id != ACL_UNDEFINED_ID) {
443 rc = -EIO;
444 goto out;
445 }
446 case ACL_USER:
447 /* ignore "nobody" entry. */
448 if (pae.e_id == NOBODY_UID)
449 break;
450
451 new->a_entries[j].e_tag =
452 posix_header->a_entries[i].e_tag;
453 new->a_entries[j].e_perm =
454 posix_header->a_entries[i].e_perm;
455 new->a_entries[j].e_id =
456 posix_header->a_entries[i].e_id;
457 ee = lustre_ext_acl_xattr_search(ext_header,
458 &posix_header->a_entries[i], &pos);
459 if (ee) {
460 if (posix_header->a_entries[i].e_perm !=
461 ee->e_perm)
462 /* entry modified. */
463 ee->e_stat =
464 new->a_entries[j++].e_stat =
465 cpu_to_le32(ES_MOD);
466 else
467 /* entry unchanged. */
468 ee->e_stat =
469 new->a_entries[j++].e_stat =
470 cpu_to_le32(ES_UNC);
471 } else {
472 /* new entry. */
473 new->a_entries[j++].e_stat =
474 cpu_to_le32(ES_ADD);
475 }
476 break;
477 case ACL_GROUP:
478 /* ignore "nobody" entry. */
479 if (pae.e_id == NOBODY_GID)
480 break;
481 new->a_entries[j].e_tag =
482 posix_header->a_entries[i].e_tag;
483 new->a_entries[j].e_perm =
484 posix_header->a_entries[i].e_perm;
485 new->a_entries[j].e_id =
486 posix_header->a_entries[i].e_id;
487 ee = lustre_ext_acl_xattr_search(ext_header,
488 &posix_header->a_entries[i], &pos);
489 if (ee) {
490 if (posix_header->a_entries[i].e_perm !=
491 ee->e_perm)
492 /* entry modified. */
493 ee->e_stat =
494 new->a_entries[j++].e_stat =
495 cpu_to_le32(ES_MOD);
496 else
497 /* entry unchanged. */
498 ee->e_stat =
499 new->a_entries[j++].e_stat =
500 cpu_to_le32(ES_UNC);
501 } else {
502 /* new entry. */
503 new->a_entries[j++].e_stat =
504 cpu_to_le32(ES_ADD);
505 }
506 break;
507 default:
508 rc = -EIO;
509 goto out;
510 }
511 }
512
513 /* process deleted entries. */
514 for (i = 0; i < ori_ext_count; i++) {
515 lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
516 if (eae.e_stat == ES_UNK) {
517 /* ignore "nobody" entry. */
518 if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
519 (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
520 continue;
521
522 new->a_entries[j].e_tag =
523 ext_header->a_entries[i].e_tag;
524 new->a_entries[j].e_perm =
525 ext_header->a_entries[i].e_perm;
526 new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
527 new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
528 }
529 }
530
531 new->a_count = cpu_to_le32(j);
532 /* free unused space. */
533 rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
534
535 out:
536 if (rc) {
537 kfree(new);
538 new = ERR_PTR(rc);
539 }
540 return new;
541 }
542 EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
543
544 #endif
This page took 0.060141 seconds and 5 git commands to generate.