Whamcloud - gitweb
land b_colibri_devel on HEAD:
[fs/lustre-release.git] / lustre / obdclass / acl.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *   lustre/obdclass/acl.c
5  *   Lustre Access Control List.
6  *   Author: Fan Yong <fanyong@clusterfs.com>
7  *
8  * Copyright (c) 2004-2007 Cluster File Systems, Inc.
9  *
10  *   This file is part of Lustre, http://www.lustre.org.
11  *
12  *   Lustre is free software; you can redistribute it and/or
13  *   modify it under the terms of version 2 of the GNU General Public
14  *   License as published by the Free Software Foundation.
15  *
16  *   Lustre is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with Lustre; if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25 #ifndef EXPORT_SYMTAB
26 # define EXPORT_SYMTAB
27 #endif
28
29 #define DEBUG_SUBSYSTEM S_SEC
30
31 #include <linux/lustre_acl.h>
32 #include <lustre_eacl.h>
33 #include <obd_support.h>
34
35 #ifdef CONFIG_FS_POSIX_ACL
36
37 #define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
38
39 enum {
40         ES_UNK  = 0,    /* unknown stat */
41         ES_UNC  = 1,    /* ACL entry is not changed */
42         ES_MOD  = 2,    /* ACL entry is modified */
43         ES_ADD  = 3,    /* ACL entry is added */
44         ES_DEL  = 4     /* ACL entry is deleted */
45 };
46
47 static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
48                                             ext_acl_xattr_entry *s)
49 {
50         d->e_tag        = le16_to_cpu(s->e_tag);
51         d->e_perm       = le16_to_cpu(s->e_perm);
52         d->e_id         = le32_to_cpu(s->e_id);
53         d->e_stat       = le32_to_cpu(s->e_stat);
54 }
55
56 static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
57                                             ext_acl_xattr_entry *s)
58 {
59         d->e_tag        = cpu_to_le16(s->e_tag);
60         d->e_perm       = cpu_to_le16(s->e_perm);
61         d->e_id         = cpu_to_le32(s->e_id);
62         d->e_stat       = cpu_to_le32(s->e_stat);
63 }
64
65 static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
66                                               posix_acl_xattr_entry *s)
67 {
68         d->e_tag        = le16_to_cpu(s->e_tag);
69         d->e_perm       = le16_to_cpu(s->e_perm);
70         d->e_id         = le32_to_cpu(s->e_id);
71 }
72
73 static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
74                                               posix_acl_xattr_entry *s)
75 {
76         d->e_tag        = cpu_to_le16(s->e_tag);
77         d->e_perm       = cpu_to_le16(s->e_perm);
78         d->e_id         = cpu_to_le32(s->e_id);
79 }
80
81 /*
82  * Check permission based on POSIX ACL.
83  */
84 int lustre_posix_acl_permission(struct md_ucred *mu, struct lu_attr *la,
85                                 int want, posix_acl_xattr_entry *entry,
86                                 int count)
87 {
88         posix_acl_xattr_entry *pa, *pe, *mask_obj;
89         posix_acl_xattr_entry ae, me;
90         int found = 0;
91
92         if (count <= 0)
93                 return -EACCES;
94
95         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
96                 lustre_posix_acl_le_to_cpu(&ae, pa);
97                 switch (ae.e_tag) {
98                 case ACL_USER_OBJ:
99                         /* (May have been checked already) */
100                         if (la->la_uid == mu->mu_fsuid)
101                                 goto check_perm;
102                         break;
103                 case ACL_USER:
104                         if (ae.e_id == mu->mu_fsuid)
105                                 goto mask;
106                         break;
107                 case ACL_GROUP_OBJ:
108                         if (lustre_in_group_p(mu, la->la_gid)) {
109                                 found = 1;
110                                 if ((ae.e_perm & want) == want)
111                                         goto mask;
112                         }
113                         break;
114                 case ACL_GROUP:
115                         if (lustre_in_group_p(mu, ae.e_id)) {
116                                 found = 1;
117                                 if ((ae.e_perm & want) == want)
118                                         goto mask;
119                         }
120                         break;
121                 case ACL_MASK:
122                         break;
123                 case ACL_OTHER:
124                         if (found)
125                                 return -EACCES;
126                         else
127                                 goto check_perm;
128                 default:
129                         return -EIO;
130                 }
131         }
132         return -EIO;
133
134 mask:
135         for (mask_obj = pa + 1; mask_obj <= pe; mask_obj++) {
136                 lustre_posix_acl_le_to_cpu(&me, mask_obj);
137                 if (me.e_tag == ACL_MASK) {
138                         if ((ae.e_perm & me.e_perm & want) == want)
139                                 return 0;
140
141                         return -EACCES;
142                 }
143         }
144
145 check_perm:
146         if ((ae.e_perm & want) == want)
147                 return 0;
148
149         return -EACCES;
150 }
151 EXPORT_SYMBOL(lustre_posix_acl_permission);
152
153 /*
154  * Modify the ACL for the chmod.
155  */
156 int lustre_posix_acl_chmod_masq(posix_acl_xattr_entry *entry, __u32 mode,
157                                 int count)
158 {
159         posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
160
161         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
162                 switch (le16_to_cpu(pa->e_tag)) {
163                 case ACL_USER_OBJ:
164                         pa->e_perm = cpu_to_le16((mode & S_IRWXU) >> 6);
165                         break;
166                 case ACL_USER:
167                 case ACL_GROUP:
168                         break;
169                 case ACL_GROUP_OBJ:
170                         group_obj = pa;
171                         break;
172                 case ACL_MASK:
173                         mask_obj = pa;
174                         break;
175                 case ACL_OTHER:
176                         pa->e_perm = cpu_to_le16(mode & S_IRWXO);
177                         break;
178                 default:
179                         return -EIO;
180                 }
181         }
182
183         if (mask_obj) {
184                 mask_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
185         } else {
186                 if (!group_obj)
187                         return -EIO;
188                 group_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
189         }
190
191         return 0;
192 }
193 EXPORT_SYMBOL(lustre_posix_acl_chmod_masq);
194
195 /*
196  * Modify acl when creating a new object.
197  */
198 int lustre_posix_acl_create_masq(posix_acl_xattr_entry *entry, __u32 *pmode,
199                                  int count)
200 {
201         posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
202         posix_acl_xattr_entry ae;
203         __u32 mode = *pmode;
204         int not_equiv = 0;
205
206         for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
207                 lustre_posix_acl_le_to_cpu(&ae, pa);
208                 switch (ae.e_tag) {
209                 case ACL_USER_OBJ:
210                         ae.e_perm &= (mode >> 6) | ~S_IRWXO;
211                         pa->e_perm = cpu_to_le16(ae.e_perm);
212                         mode &= (ae.e_perm << 6) | ~S_IRWXU;
213                         break;
214                 case ACL_USER:
215                 case ACL_GROUP:
216                         not_equiv = 1;
217                         break;
218                 case ACL_GROUP_OBJ:
219                         group_obj = pa;
220                         break;
221                 case ACL_OTHER:
222                         ae.e_perm &= mode | ~S_IRWXO;
223                         pa->e_perm = cpu_to_le16(ae.e_perm);
224                         mode &= ae.e_perm | ~S_IRWXO;
225                         break;
226                 case ACL_MASK:
227                         mask_obj = pa;
228                         not_equiv = 1;
229                         break;
230                 default:
231                         return -EIO;
232                 }
233         }
234
235         if (mask_obj) {
236                 ae.e_perm = le16_to_cpu(mask_obj->e_perm) &
237                             ((mode >> 3) | ~S_IRWXO);
238                 mode &= (ae.e_perm << 3) | ~S_IRWXG;
239                 mask_obj->e_perm = cpu_to_le16(ae.e_perm);
240         } else {
241                 if (!group_obj)
242                         return -EIO;
243                 ae.e_perm = le16_to_cpu(group_obj->e_perm) &
244                             ((mode >> 3) | ~S_IRWXO);
245                 mode &= (ae.e_perm << 3) | ~S_IRWXG;
246                 group_obj->e_perm = cpu_to_le16(ae.e_perm);
247         }
248
249         *pmode = (*pmode & ~S_IRWXUGO) | mode;
250         return not_equiv;
251 }
252 EXPORT_SYMBOL(lustre_posix_acl_create_masq);
253
254 /* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
255 static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
256                                                int old_count, int new_count)
257 {
258         int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
259         int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
260         posix_acl_xattr_header *new;
261
262         if (unlikely(old_count <= new_count))
263                 return old_size;
264
265         OBD_ALLOC(new, new_size);
266         if (unlikely(new == NULL))
267                 return -ENOMEM;
268
269         memcpy(new, *header, new_size);
270         OBD_FREE(*header, old_size);
271         *header = new;
272         return new_size;
273 }
274
275 /* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
276 static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
277                                              int old_count)
278 {
279         int ext_count = le32_to_cpu((*header)->a_count);
280         int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
281         int old_size = CFS_ACL_XATTR_SIZE(old_count, ext_acl_xattr);
282         ext_acl_xattr_header *new;
283
284         if (unlikely(old_count <= ext_count))
285                 return 0;
286
287         OBD_ALLOC(new, ext_size);
288         if (unlikely(new == NULL))
289                 return -ENOMEM;
290
291         memcpy(new, *header, ext_size);
292         OBD_FREE(*header, old_size);
293         *header = new;
294         return 0;
295 }
296
297 /*
298  * Generate new extended ACL based on the posix ACL.
299  */
300 ext_acl_xattr_header *
301 lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
302 {
303         int count, i, esize;
304         ext_acl_xattr_header *new;
305         ENTRY;
306
307         if (unlikely(size < 0))
308                 RETURN(ERR_PTR(-EINVAL));
309         else if (!size)
310                 count = 0;
311         else
312                 count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
313         esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
314         OBD_ALLOC(new, esize);
315         if (unlikely(new == NULL))
316                 RETURN(ERR_PTR(-ENOMEM));
317
318         new->a_count = cpu_to_le32(count);
319         for (i = 0; i < count; i++) {
320                 new->a_entries[i].e_tag  = header->a_entries[i].e_tag;
321                 new->a_entries[i].e_perm = header->a_entries[i].e_perm;
322                 new->a_entries[i].e_id   = header->a_entries[i].e_id;
323                 new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
324         }
325
326         RETURN(new);
327 }
328 EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
329
330 /*
331  * Filter out the "nobody" entries in the posix ACL.
332  */
333 int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, int size,
334                                   posix_acl_xattr_header **out)
335 {
336         int count, i, j, rc = 0;
337         __u32 id;
338         posix_acl_xattr_header *new;
339         ENTRY;
340
341         if (unlikely(size < 0))
342                 RETURN(-EINVAL);
343         else if (!size)
344                 RETURN(0);
345
346         OBD_ALLOC(new, size);
347         if (unlikely(new == NULL))
348                 RETURN(-ENOMEM);
349
350         new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
351         count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
352         for (i = 0, j = 0; i < count; i++) {
353                 id = le32_to_cpu(header->a_entries[i].e_id);
354                 switch (le16_to_cpu(header->a_entries[i].e_tag)) {
355                 case ACL_USER_OBJ:
356                 case ACL_GROUP_OBJ:
357                 case ACL_MASK:
358                 case ACL_OTHER:
359                         if (id != ACL_UNDEFINED_ID)
360                                 GOTO(_out, rc = -EIO);
361
362                         memcpy(&new->a_entries[j++], &header->a_entries[i],
363                                sizeof(posix_acl_xattr_entry));
364                         break;
365                 case ACL_USER:
366                         if (id != NOBODY_UID)
367                                 memcpy(&new->a_entries[j++],
368                                        &header->a_entries[i],
369                                        sizeof(posix_acl_xattr_entry));
370                         break;
371                 case ACL_GROUP:
372                         if (id != NOBODY_GID)
373                                 memcpy(&new->a_entries[j++],
374                                        &header->a_entries[i],
375                                        sizeof(posix_acl_xattr_entry));
376                         break;
377                 default:
378                         GOTO(_out, rc = -EIO);
379                 }
380         }
381
382         /* free unused space. */
383         rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
384         if (rc >= 0) {
385                 size = rc;
386                 *out = new;
387                 rc = 0;
388         }
389         EXIT;
390
391 _out:
392         if (rc) {
393                 OBD_FREE(new, size);
394                 size = rc;
395         }
396         return size;
397 }
398 EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
399
400 /*
401  * Convert server-side uid/gid in the posix ACL items to the client-side ones.
402  * convert rule:
403  * @CFS_IC_NOTHING
404  *  nothing to be converted.
405  * @CFS_IC_ALL
406  *  mapped ids are converted to client-side ones,
407  *  unmapped ones are converted to "nobody".
408  * @CFS_IC_MAPPED
409  *  only mapped ids are converted to "nobody".
410  * @CFS_IC_UNMAPPED
411  *  only unmapped ids are converted to "nobody".
412  */
413 int lustre_posix_acl_xattr_id2client(struct md_ucred *mu,
414                                      struct lustre_idmap_table *t,
415                                      posix_acl_xattr_header *header,
416                                      int size, int flags)
417 {
418         int count, i;
419         __u32 id;
420         ENTRY;
421
422         if (unlikely(size < 0))
423                 RETURN(-EINVAL);
424         else if (!size)
425                 RETURN(0);
426
427         if (unlikely(flags == CFS_IC_NOTHING))
428                 RETURN(0);
429
430         count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
431         for (i = 0; i < count; i++) {
432                 id = le32_to_cpu(header->a_entries[i].e_id);
433                 switch (le16_to_cpu(header->a_entries[i].e_tag)) {
434                 case ACL_USER_OBJ:
435                 case ACL_GROUP_OBJ:
436                 case ACL_MASK:
437                 case ACL_OTHER:
438                         if (id != ACL_UNDEFINED_ID)
439                                 RETURN(-EIO);
440                         break;
441                 case ACL_USER:
442                         id = lustre_idmap_lookup_uid(mu, t, 1, id);
443                         if (flags == CFS_IC_ALL) {
444                                 if (id == CFS_IDMAP_NOTFOUND)
445                                         id = NOBODY_UID;
446                                 header->a_entries[i].e_id = cpu_to_le32(id);
447                         } else if (flags == CFS_IC_MAPPED) {
448                                 if (id != CFS_IDMAP_NOTFOUND)
449                                         header->a_entries[i].e_id =
450                                                         cpu_to_le32(NOBODY_UID);
451                         } else if (flags == CFS_IC_UNMAPPED) {
452                                 if (id == CFS_IDMAP_NOTFOUND)
453                                         header->a_entries[i].e_id =
454                                                         cpu_to_le32(NOBODY_UID);
455                         }
456                         break;
457                 case ACL_GROUP:
458                         id = lustre_idmap_lookup_gid(mu, t, 1, id);
459                         if (flags == CFS_IC_ALL) {
460                                 if (id == CFS_IDMAP_NOTFOUND)
461                                         id = NOBODY_GID;
462                                 header->a_entries[i].e_id = cpu_to_le32(id);
463                         } else if (flags == CFS_IC_MAPPED) {
464                                 if (id != CFS_IDMAP_NOTFOUND)
465                                         header->a_entries[i].e_id =
466                                                         cpu_to_le32(NOBODY_GID);
467                         } else if (flags == CFS_IC_UNMAPPED) {
468                                 if (id == CFS_IDMAP_NOTFOUND)
469                                         header->a_entries[i].e_id =
470                                                         cpu_to_le32(NOBODY_GID);
471                         }
472                         break;
473                  default:
474                         RETURN(-EIO);
475                 }
476         }
477     RETURN(0);
478 }
479 EXPORT_SYMBOL(lustre_posix_acl_xattr_id2client);
480
481 /*
482  * Release the posix ACL space.
483  */
484 void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
485 {
486         OBD_FREE(header, size);
487 }
488 EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
489
490 /*
491  * Converts client-side uid/gid in the extended ACL items to server-side ones.
492  * convert rule:
493  *  mapped ids are converted to server-side ones,
494  *  unmapped ones cause "EPERM" error.
495  */
496 int lustre_ext_acl_xattr_id2server(struct md_ucred *mu,
497                                    struct lustre_idmap_table *t,
498                                    ext_acl_xattr_header *header)
499
500 {
501         int i, count = le32_to_cpu(header->a_count);
502         __u32 id;
503         ENTRY;
504
505         for (i = 0; i < count; i++) {
506                 id = le32_to_cpu(header->a_entries[i].e_id);
507                 switch (le16_to_cpu(header->a_entries[i].e_tag)) {
508                 case ACL_USER_OBJ:
509                 case ACL_GROUP_OBJ:
510                 case ACL_MASK:
511                 case ACL_OTHER:
512                         if (id != ACL_UNDEFINED_ID)
513                                 RETURN(-EIO);
514                         break;
515                 case ACL_USER:
516                         id = lustre_idmap_lookup_uid(mu, t, 0, id);
517                         if (id == CFS_IDMAP_NOTFOUND)
518                                 RETURN(-EPERM);
519                         else
520                                 header->a_entries[i].e_id = cpu_to_le32(id);
521                         break;
522                 case ACL_GROUP:
523                         id = lustre_idmap_lookup_gid(mu, t, 0, id);
524                         if (id == CFS_IDMAP_NOTFOUND)
525                                 RETURN(-EPERM);
526                         else
527                                 header->a_entries[i].e_id = cpu_to_le32(id);
528                         break;
529                 default:
530                         RETURN(-EIO);
531                 }
532         }
533         RETURN(0);
534 }
535 EXPORT_SYMBOL(lustre_ext_acl_xattr_id2server);
536
537 /*
538  * Release the extended ACL space.
539  */
540 void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
541 {
542         OBD_FREE(header, CFS_ACL_XATTR_SIZE(le32_to_cpu(header->a_count), \
543                                             ext_acl_xattr));
544 }
545 EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
546
547 static ext_acl_xattr_entry *
548 lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
549                             posix_acl_xattr_entry *entry, int *pos)
550 {
551         int once, start, end, i, j, count = le32_to_cpu(header->a_count);
552
553         once = 0;
554         start = *pos;
555         end = count;
556
557 again:
558         for (i = start; i < end; i++) {
559                 if (header->a_entries[i].e_tag == entry->e_tag &&
560                     header->a_entries[i].e_id == entry->e_id) {
561                         j = i;
562                         if (++i >= count)
563                                 i = 0;
564                         *pos = i;
565                         return &header->a_entries[j];
566                 }
567         }
568
569         if (!once) {
570                 once = 1;
571                 start = 0;
572                 end = *pos;
573                 goto again;
574         }
575
576         return NULL;
577 }
578
579 /*
580  * Merge the posix ACL and the extended ACL into new posix ACL.
581  */
582 int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
583                                  ext_acl_xattr_header *ext_header,
584                                  posix_acl_xattr_header **out)
585 {
586         int posix_count, posix_size, i, j;
587         int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
588         posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
589         posix_acl_xattr_header *new;
590         ext_acl_xattr_entry *ee, ae;
591         ENTRY;
592
593         lustre_posix_acl_cpu_to_le(&pe, &pe);
594         ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
595         if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
596                 /* there are only base ACL entries at most. */
597                 posix_count = 3;
598                 posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
599                 OBD_ALLOC(new, posix_size);
600                 if (unlikely(new == NULL))
601                         RETURN(-ENOMEM);
602
603                 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
604                 for (i = 0, j = 0; i < ext_count; i++) {
605                         lustre_ext_acl_le_to_cpu(&ae,
606                                                  &ext_header->a_entries[i]);
607                         switch (ae.e_tag) {
608                         case ACL_USER_OBJ:
609                         case ACL_GROUP_OBJ:
610                         case ACL_OTHER:
611                                 if (ae.e_id != ACL_UNDEFINED_ID)
612                                         GOTO(_out, rc = -EIO);
613
614                                 if (ae.e_stat != ES_DEL) {
615                                         new->a_entries[j].e_tag =
616                                                 ext_header->a_entries[i].e_tag;
617                                         new->a_entries[j].e_perm =
618                                                 ext_header->a_entries[i].e_perm;
619                                         new->a_entries[j++].e_id =
620                                                 ext_header->a_entries[i].e_id;
621                                 }
622                                 break;
623                         case ACL_MASK:
624                         case ACL_USER:
625                         case ACL_GROUP:
626                                 if (ae.e_stat == ES_DEL)
627                                         break;
628                         default:
629                                 GOTO(_out, rc = -EIO);
630                         }
631                 }
632         } else {
633                 /* maybe there are valid ACL_USER or ACL_GROUP entries in the
634                  * original server-side ACL, they are regarded as ES_UNC stat.*/
635                 int ori_posix_count;
636
637                 if (unlikely(size < 0))
638                         RETURN(-EINVAL);
639                 else if (!size)
640                         ori_posix_count = 0;
641                 else
642                         ori_posix_count =
643                                 CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
644                 posix_count = ori_posix_count + ext_count;
645                 posix_size =
646                         CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
647                 OBD_ALLOC(new, posix_size);
648                 if (unlikely(new == NULL))
649                         RETURN(-ENOMEM);
650
651                 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
652                 /* 1. process the unchanged ACL entries
653                  *    in the original server-side ACL. */
654                 pos = 0;
655                 for (i = 0, j = 0; i < ori_posix_count; i++) {
656                         ee = lustre_ext_acl_xattr_search(ext_header,
657                                         &posix_header->a_entries[i], &pos);
658                         if (ee == NULL)
659                                 memcpy(&new->a_entries[j++],
660                                        &posix_header->a_entries[i],
661                                        sizeof(posix_acl_xattr_entry));
662                 }
663
664                 /* 2. process the non-deleted entries
665                  *    from client-side extended ACL. */
666                 for (i = 0; i < ext_count; i++) {
667                         if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
668                             ES_DEL) {
669                                 new->a_entries[j].e_tag =
670                                                 ext_header->a_entries[i].e_tag;
671                                 new->a_entries[j].e_perm =
672                                                 ext_header->a_entries[i].e_perm;
673                                 new->a_entries[j++].e_id =
674                                                 ext_header->a_entries[i].e_id;
675                         }
676                 }
677         }
678
679         /* free unused space. */
680         rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
681         if (rc >= 0) {
682                 posix_size = rc;
683                 *out = new;
684                 rc = 0;
685         }
686         EXIT;
687
688 _out:
689         if (rc) {
690                 OBD_FREE(new, posix_size);
691                 posix_size = rc;
692         }
693         return posix_size;
694 }
695 EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
696
697 /*
698  * Merge the posix ACL and the extended ACL into new extended ACL.
699  */
700 ext_acl_xattr_header *
701 lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
702                            ext_acl_xattr_header *ext_header)
703 {
704         int ori_ext_count, posix_count, ext_count, ext_size;
705         int i, j, pos = 0, rc = 0;
706         posix_acl_xattr_entry pae;
707         ext_acl_xattr_header *new;
708         ext_acl_xattr_entry *ee, eae;
709         ENTRY;
710
711         if (unlikely(size < 0))
712                 RETURN(ERR_PTR(-EINVAL));
713         else if (!size)
714                 posix_count = 0;
715         else
716                 posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
717         ori_ext_count = le32_to_cpu(ext_header->a_count);
718         ext_count = posix_count + ori_ext_count;
719         ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
720
721         OBD_ALLOC(new, ext_size);
722         if (unlikely(new == NULL))
723                 RETURN(ERR_PTR(-ENOMEM));
724
725         for (i = 0, j = 0; i < posix_count; i++) {
726                 lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
727                 switch (pae.e_tag) {
728                 case ACL_USER_OBJ:
729                 case ACL_GROUP_OBJ:
730                 case ACL_MASK:
731                 case ACL_OTHER:
732                         if (pae.e_id != ACL_UNDEFINED_ID)
733                                 GOTO(out, rc = -EIO);
734                 case ACL_USER:
735                         /* ignore "nobody" entry. */
736                         if (pae.e_id == NOBODY_UID)
737                                 break;
738
739                         new->a_entries[j].e_tag =
740                                         posix_header->a_entries[i].e_tag;
741                         new->a_entries[j].e_perm =
742                                         posix_header->a_entries[i].e_perm;
743                         new->a_entries[j].e_id =
744                                         posix_header->a_entries[i].e_id;
745                         ee = lustre_ext_acl_xattr_search(ext_header,
746                                         &posix_header->a_entries[i], &pos);
747                         if (ee) {
748                                 if (posix_header->a_entries[i].e_perm !=
749                                                                 ee->e_perm)
750                                         /* entry modified. */
751                                         ee->e_perm =
752                                         new->a_entries[j++].e_stat =
753                                                         cpu_to_le32(ES_MOD);
754                                 else
755                                         /* entry unchanged. */
756                                         ee->e_perm =
757                                         new->a_entries[j++].e_stat =
758                                                         cpu_to_le32(ES_UNC);
759                         } else {
760                                 /* new entry. */
761                                 new->a_entries[j++].e_stat =
762                                                         cpu_to_le32(ES_ADD);
763                         }
764                         break;
765                 case ACL_GROUP:
766                         /* ignore "nobody" entry. */
767                         if (pae.e_id == NOBODY_GID)
768                                 break;
769                         new->a_entries[j].e_tag =
770                                         posix_header->a_entries[i].e_tag;
771                         new->a_entries[j].e_perm =
772                                         posix_header->a_entries[i].e_perm;
773                         new->a_entries[j].e_id =
774                                         posix_header->a_entries[i].e_id;
775                         ee = lustre_ext_acl_xattr_search(ext_header,
776                                         &posix_header->a_entries[i], &pos);
777                         if (ee) {
778                                 if (posix_header->a_entries[i].e_perm !=
779                                                                 ee->e_perm)
780                                         /* entry modified. */
781                                         ee->e_perm =
782                                         new->a_entries[j++].e_stat =
783                                                         cpu_to_le32(ES_MOD);
784                                 else
785                                         /* entry unchanged. */
786                                         ee->e_perm =
787                                         new->a_entries[j++].e_stat =
788                                                         cpu_to_le32(ES_UNC);
789                         } else {
790                                 /* new entry. */
791                                 new->a_entries[j++].e_stat =
792                                                         cpu_to_le32(ES_ADD);
793                         }
794                         break;
795                 default:
796                         GOTO(out, rc = -EIO);
797                 }
798         }
799
800         /* process deleted entries. */
801         for (i = 0; i < ori_ext_count; i++) {
802                 lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
803                 if (eae.e_stat == ES_UNK) {
804                         /* ignore "nobody" entry. */
805                         if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
806                             (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
807                                 continue;
808
809                         new->a_entries[j].e_tag =
810                                                 ext_header->a_entries[i].e_tag;
811                         new->a_entries[j].e_perm =
812                                                 ext_header->a_entries[i].e_perm;
813                         new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
814                         new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
815                 }
816         }
817
818         new->a_count = cpu_to_le32(j);
819         /* free unused space. */
820         rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
821         EXIT;
822
823 out:
824         if (rc) {
825                 OBD_FREE(new, ext_size);
826                 new = ERR_PTR(rc);
827         }
828         return new;
829 }
830 EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
831
832 #endif