]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_acl.c
MFC r309300,r309363,r309405,r309523,r309590,r310185,r310623:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_acl.c
1 /*-
2  * Copyright (c) 2003-2010 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
28
29 #ifdef HAVE_ERRNO_H
30 #include <errno.h>
31 #endif
32 #ifdef HAVE_LIMITS_H
33 #include <limits.h>
34 #endif
35 #ifdef HAVE_WCHAR_H
36 #include <wchar.h>
37 #endif
38
39 #include "archive_acl_private.h"
40 #include "archive_entry.h"
41 #include "archive_private.h"
42
43 #undef max
44 #define max(a, b)       ((a)>(b)?(a):(b))
45
46 #ifndef HAVE_WMEMCMP
47 /* Good enough for simple equality testing, but not for sorting. */
48 #define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
49 #endif
50
51 static int      acl_special(struct archive_acl *acl,
52                     int type, int permset, int tag);
53 static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
54                     int type, int permset, int tag, int id);
55 static int      archive_acl_add_entry_len_l(struct archive_acl *acl,
56                     int type, int permset, int tag, int id, const char *name,
57                     size_t len, struct archive_string_conv *sc);
58 static int      isint_w(const wchar_t *start, const wchar_t *end, int *result);
59 static int      ismode_w(const wchar_t *start, const wchar_t *end, int *result);
60 static void     next_field_w(const wchar_t **wp, const wchar_t **start,
61                     const wchar_t **end, wchar_t *sep);
62 static int      prefix_w(const wchar_t *start, const wchar_t *end,
63                     const wchar_t *test);
64 static void     append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
65                     const wchar_t *wname, int perm, int id);
66 static void     append_id_w(wchar_t **wp, int id);
67 static int      isint(const char *start, const char *end, int *result);
68 static int      ismode(const char *start, const char *end, int *result);
69 static void     next_field(const char **p, const char **start,
70                     const char **end, char *sep);
71 static int      prefix_c(const char *start, const char *end,
72                     const char *test);
73 static void     append_entry(char **p, const char *prefix, int tag,
74                     const char *name, int perm, int id);
75 static void     append_id(char **p, int id);
76
77 void
78 archive_acl_clear(struct archive_acl *acl)
79 {
80         struct archive_acl_entry *ap;
81
82         while (acl->acl_head != NULL) {
83                 ap = acl->acl_head->next;
84                 archive_mstring_clean(&acl->acl_head->name);
85                 free(acl->acl_head);
86                 acl->acl_head = ap;
87         }
88         if (acl->acl_text_w != NULL) {
89                 free(acl->acl_text_w);
90                 acl->acl_text_w = NULL;
91         }
92         if (acl->acl_text != NULL) {
93                 free(acl->acl_text);
94                 acl->acl_text = NULL;
95         }
96         acl->acl_p = NULL;
97         acl->acl_types = 0;
98         acl->acl_state = 0; /* Not counting. */
99 }
100
101 void
102 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
103 {
104         struct archive_acl_entry *ap, *ap2;
105
106         archive_acl_clear(dest);
107
108         dest->mode = src->mode;
109         ap = src->acl_head;
110         while (ap != NULL) {
111                 ap2 = acl_new_entry(dest,
112                     ap->type, ap->permset, ap->tag, ap->id);
113                 if (ap2 != NULL)
114                         archive_mstring_copy(&ap2->name, &ap->name);
115                 ap = ap->next;
116         }
117 }
118
119 int
120 archive_acl_add_entry(struct archive_acl *acl,
121     int type, int permset, int tag, int id, const char *name)
122 {
123         struct archive_acl_entry *ap;
124
125         if (acl_special(acl, type, permset, tag) == 0)
126                 return ARCHIVE_OK;
127         ap = acl_new_entry(acl, type, permset, tag, id);
128         if (ap == NULL) {
129                 /* XXX Error XXX */
130                 return ARCHIVE_FAILED;
131         }
132         if (name != NULL  &&  *name != '\0')
133                 archive_mstring_copy_mbs(&ap->name, name);
134         else
135                 archive_mstring_clean(&ap->name);
136         return ARCHIVE_OK;
137 }
138
139 int
140 archive_acl_add_entry_w_len(struct archive_acl *acl,
141     int type, int permset, int tag, int id, const wchar_t *name, size_t len)
142 {
143         struct archive_acl_entry *ap;
144
145         if (acl_special(acl, type, permset, tag) == 0)
146                 return ARCHIVE_OK;
147         ap = acl_new_entry(acl, type, permset, tag, id);
148         if (ap == NULL) {
149                 /* XXX Error XXX */
150                 return ARCHIVE_FAILED;
151         }
152         if (name != NULL  &&  *name != L'\0' && len > 0)
153                 archive_mstring_copy_wcs_len(&ap->name, name, len);
154         else
155                 archive_mstring_clean(&ap->name);
156         return ARCHIVE_OK;
157 }
158
159 static int
160 archive_acl_add_entry_len_l(struct archive_acl *acl,
161     int type, int permset, int tag, int id, const char *name, size_t len,
162     struct archive_string_conv *sc)
163 {
164         struct archive_acl_entry *ap;
165         int r;
166
167         if (acl_special(acl, type, permset, tag) == 0)
168                 return ARCHIVE_OK;
169         ap = acl_new_entry(acl, type, permset, tag, id);
170         if (ap == NULL) {
171                 /* XXX Error XXX */
172                 return ARCHIVE_FAILED;
173         }
174         if (name != NULL  &&  *name != '\0' && len > 0) {
175                 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
176         } else {
177                 r = 0;
178                 archive_mstring_clean(&ap->name);
179         }
180         if (r == 0)
181                 return (ARCHIVE_OK);
182         else if (errno == ENOMEM)
183                 return (ARCHIVE_FATAL);
184         else
185                 return (ARCHIVE_WARN);
186 }
187
188 /*
189  * If this ACL entry is part of the standard POSIX permissions set,
190  * store the permissions in the stat structure and return zero.
191  */
192 static int
193 acl_special(struct archive_acl *acl, int type, int permset, int tag)
194 {
195         if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
196             && ((permset & ~007) == 0)) {
197                 switch (tag) {
198                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
199                         acl->mode &= ~0700;
200                         acl->mode |= (permset & 7) << 6;
201                         return (0);
202                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
203                         acl->mode &= ~0070;
204                         acl->mode |= (permset & 7) << 3;
205                         return (0);
206                 case ARCHIVE_ENTRY_ACL_OTHER:
207                         acl->mode &= ~0007;
208                         acl->mode |= permset & 7;
209                         return (0);
210                 }
211         }
212         return (1);
213 }
214
215 /*
216  * Allocate and populate a new ACL entry with everything but the
217  * name.
218  */
219 static struct archive_acl_entry *
220 acl_new_entry(struct archive_acl *acl,
221     int type, int permset, int tag, int id)
222 {
223         struct archive_acl_entry *ap, *aq;
224
225         /* Type argument must be a valid NFS4 or POSIX.1e type.
226          * The type must agree with anything already set and
227          * the permset must be compatible. */
228         if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
229                 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
230                         return (NULL);
231                 }
232                 if (permset &
233                     ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
234                         | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
235                         return (NULL);
236                 }
237         } else  if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
238                 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
239                         return (NULL);
240                 }
241                 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
242                         return (NULL);
243                 }
244         } else {
245                 return (NULL);
246         }
247
248         /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
249         switch (tag) {
250         case ARCHIVE_ENTRY_ACL_USER:
251         case ARCHIVE_ENTRY_ACL_USER_OBJ:
252         case ARCHIVE_ENTRY_ACL_GROUP:
253         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
254                 /* Tags valid in both NFS4 and POSIX.1e */
255                 break;
256         case ARCHIVE_ENTRY_ACL_MASK:
257         case ARCHIVE_ENTRY_ACL_OTHER:
258                 /* Tags valid only in POSIX.1e. */
259                 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
260                         return (NULL);
261                 }
262                 break;
263         case ARCHIVE_ENTRY_ACL_EVERYONE:
264                 /* Tags valid only in NFS4. */
265                 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
266                         return (NULL);
267                 }
268                 break;
269         default:
270                 /* No other values are valid. */
271                 return (NULL);
272         }
273
274         if (acl->acl_text_w != NULL) {
275                 free(acl->acl_text_w);
276                 acl->acl_text_w = NULL;
277         }
278         if (acl->acl_text != NULL) {
279                 free(acl->acl_text);
280                 acl->acl_text = NULL;
281         }
282
283         /*
284          * If there's a matching entry already in the list, overwrite it.
285          * NFSv4 entries may be repeated and are not overwritten.
286          *
287          * TODO: compare names of no id is provided (needs more rework)
288          */
289         ap = acl->acl_head;
290         aq = NULL;
291         while (ap != NULL) {
292                 if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
293                     ap->type == type && ap->tag == tag && ap->id == id) {
294                         if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
295                             tag != ARCHIVE_ENTRY_ACL_GROUP)) {
296                                 ap->permset = permset;
297                                 return (ap);
298                         }
299                 }
300                 aq = ap;
301                 ap = ap->next;
302         }
303
304         /* Add a new entry to the end of the list. */
305         ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
306         if (ap == NULL)
307                 return (NULL);
308         if (aq == NULL)
309                 acl->acl_head = ap;
310         else
311                 aq->next = ap;
312         ap->type = type;
313         ap->tag = tag;
314         ap->id = id;
315         ap->permset = permset;
316         acl->acl_types |= type;
317         return (ap);
318 }
319
320 /*
321  * Return a count of entries matching "want_type".
322  */
323 int
324 archive_acl_count(struct archive_acl *acl, int want_type)
325 {
326         int count;
327         struct archive_acl_entry *ap;
328
329         count = 0;
330         ap = acl->acl_head;
331         while (ap != NULL) {
332                 if ((ap->type & want_type) != 0)
333                         count++;
334                 ap = ap->next;
335         }
336
337         if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
338                 count += 3;
339         return (count);
340 }
341
342 /*
343  * Prepare for reading entries from the ACL data.  Returns a count
344  * of entries matching "want_type", or zero if there are no
345  * non-extended ACL entries of that type.
346  */
347 int
348 archive_acl_reset(struct archive_acl *acl, int want_type)
349 {
350         int count, cutoff;
351
352         count = archive_acl_count(acl, want_type);
353
354         /*
355          * If the only entries are the three standard ones,
356          * then don't return any ACL data.  (In this case,
357          * client can just use chmod(2) to set permissions.)
358          */
359         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
360                 cutoff = 3;
361         else
362                 cutoff = 0;
363
364         if (count > cutoff)
365                 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
366         else
367                 acl->acl_state = 0;
368         acl->acl_p = acl->acl_head;
369         return (count);
370 }
371
372
373 /*
374  * Return the next ACL entry in the list.  Fake entries for the
375  * standard permissions and include them in the returned list.
376  */
377 int
378 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
379     int *permset, int *tag, int *id, const char **name)
380 {
381         *name = NULL;
382         *id = -1;
383
384         /*
385          * The acl_state is either zero (no entries available), -1
386          * (reading from list), or an entry type (retrieve that type
387          * from ae_stat.aest_mode).
388          */
389         if (acl->acl_state == 0)
390                 return (ARCHIVE_WARN);
391
392         /* The first three access entries are special. */
393         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
394                 switch (acl->acl_state) {
395                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
396                         *permset = (acl->mode >> 6) & 7;
397                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
398                         *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
399                         acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
400                         return (ARCHIVE_OK);
401                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
402                         *permset = (acl->mode >> 3) & 7;
403                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
404                         *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
405                         acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
406                         return (ARCHIVE_OK);
407                 case ARCHIVE_ENTRY_ACL_OTHER:
408                         *permset = acl->mode & 7;
409                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
410                         *tag = ARCHIVE_ENTRY_ACL_OTHER;
411                         acl->acl_state = -1;
412                         acl->acl_p = acl->acl_head;
413                         return (ARCHIVE_OK);
414                 default:
415                         break;
416                 }
417         }
418
419         while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
420                 acl->acl_p = acl->acl_p->next;
421         if (acl->acl_p == NULL) {
422                 acl->acl_state = 0;
423                 *type = 0;
424                 *permset = 0;
425                 *tag = 0;
426                 *id = -1;
427                 *name = NULL;
428                 return (ARCHIVE_EOF); /* End of ACL entries. */
429         }
430         *type = acl->acl_p->type;
431         *permset = acl->acl_p->permset;
432         *tag = acl->acl_p->tag;
433         *id = acl->acl_p->id;
434         if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
435                 if (errno == ENOMEM)
436                         return (ARCHIVE_FATAL);
437                 *name = NULL;
438         }
439         acl->acl_p = acl->acl_p->next;
440         return (ARCHIVE_OK);
441 }
442
443 /*
444  * Generate a text version of the ACL.  The flags parameter controls
445  * the style of the generated ACL.
446  */
447 const wchar_t *
448 archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
449 {
450         int count;
451         size_t length;
452         const wchar_t *wname;
453         const wchar_t *prefix;
454         wchar_t separator;
455         struct archive_acl_entry *ap;
456         int id, r;
457         wchar_t *wp;
458
459         if (acl->acl_text_w != NULL) {
460                 free (acl->acl_text_w);
461                 acl->acl_text_w = NULL;
462         }
463
464         separator = L',';
465         count = 0;
466         length = 0;
467         ap = acl->acl_head;
468         while (ap != NULL) {
469                 if ((ap->type & flags) != 0) {
470                         count++;
471                         if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
472                             (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
473                                 length += 8; /* "default:" */
474                         length += 5; /* tag name */
475                         length += 1; /* colon */
476                         r = archive_mstring_get_wcs(a, &ap->name, &wname);
477                         if (r == 0 && wname != NULL)
478                                 length += wcslen(wname);
479                         else if (r < 0 && errno == ENOMEM)
480                                 return (NULL);
481                         else
482                                 length += sizeof(uid_t) * 3 + 1;
483                         length ++; /* colon */
484                         length += 3; /* rwx */
485                         length += 1; /* colon */
486                         length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
487                         length ++; /* newline */
488                 }
489                 ap = ap->next;
490         }
491
492         if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
493                 length += 10; /* "user::rwx\n" */
494                 length += 11; /* "group::rwx\n" */
495                 length += 11; /* "other::rwx\n" */
496         }
497
498         if (count == 0)
499                 return (NULL);
500
501         /* Now, allocate the string and actually populate it. */
502         wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
503         if (wp == NULL)
504                 return (NULL);
505         count = 0;
506         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
507                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
508                     acl->mode & 0700, -1);
509                 *wp++ = ',';
510                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
511                     acl->mode & 0070, -1);
512                 *wp++ = ',';
513                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
514                     acl->mode & 0007, -1);
515                 count += 3;
516
517                 ap = acl->acl_head;
518                 while (ap != NULL) {
519                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
520                                 r = archive_mstring_get_wcs(a, &ap->name, &wname);
521                                 if (r == 0) {
522                                         *wp++ = separator;
523                                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
524                                                 id = ap->id;
525                                         else
526                                                 id = -1;
527                                         append_entry_w(&wp, NULL, ap->tag, wname,
528                                             ap->permset, id);
529                                         count++;
530                                 } else if (r < 0 && errno == ENOMEM)
531                                         return (NULL);
532                         }
533                         ap = ap->next;
534                 }
535         }
536
537
538         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
539                 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
540                         prefix = L"default:";
541                 else
542                         prefix = NULL;
543                 ap = acl->acl_head;
544                 count = 0;
545                 while (ap != NULL) {
546                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
547                                 r = archive_mstring_get_wcs(a, &ap->name, &wname);
548                                 if (r == 0) {
549                                         if (count > 0)
550                                                 *wp++ = separator;
551                                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
552                                                 id = ap->id;
553                                         else
554                                                 id = -1;
555                                         append_entry_w(&wp, prefix, ap->tag,
556                                             wname, ap->permset, id);
557                                         count ++;
558                                 } else if (r < 0 && errno == ENOMEM)
559                                         return (NULL);
560                         }
561                         ap = ap->next;
562                 }
563         }
564
565         return (acl->acl_text_w);
566 }
567
568
569 static void
570 append_id_w(wchar_t **wp, int id)
571 {
572         if (id < 0)
573                 id = 0;
574         if (id > 9)
575                 append_id_w(wp, id / 10);
576         *(*wp)++ = L"0123456789"[id % 10];
577 }
578
579 static void
580 append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
581     const wchar_t *wname, int perm, int id)
582 {
583         if (prefix != NULL) {
584                 wcscpy(*wp, prefix);
585                 *wp += wcslen(*wp);
586         }
587         switch (tag) {
588         case ARCHIVE_ENTRY_ACL_USER_OBJ:
589                 wname = NULL;
590                 id = -1;
591                 /* FALLTHROUGH */
592         case ARCHIVE_ENTRY_ACL_USER:
593                 wcscpy(*wp, L"user");
594                 break;
595         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
596                 wname = NULL;
597                 id = -1;
598                 /* FALLTHROUGH */
599         case ARCHIVE_ENTRY_ACL_GROUP:
600                 wcscpy(*wp, L"group");
601                 break;
602         case ARCHIVE_ENTRY_ACL_MASK:
603                 wcscpy(*wp, L"mask");
604                 wname = NULL;
605                 id = -1;
606                 break;
607         case ARCHIVE_ENTRY_ACL_OTHER:
608                 wcscpy(*wp, L"other");
609                 wname = NULL;
610                 id = -1;
611                 break;
612         }
613         *wp += wcslen(*wp);
614         *(*wp)++ = L':';
615         if (wname != NULL) {
616                 wcscpy(*wp, wname);
617                 *wp += wcslen(*wp);
618         } else if (tag == ARCHIVE_ENTRY_ACL_USER
619             || tag == ARCHIVE_ENTRY_ACL_GROUP) {
620                 append_id_w(wp, id);
621                 id = -1;
622         }
623         *(*wp)++ = L':';
624         *(*wp)++ = (perm & 0444) ? L'r' : L'-';
625         *(*wp)++ = (perm & 0222) ? L'w' : L'-';
626         *(*wp)++ = (perm & 0111) ? L'x' : L'-';
627         if (id != -1) {
628                 *(*wp)++ = L':';
629                 append_id_w(wp, id);
630         }
631         **wp = L'\0';
632 }
633
634 int
635 archive_acl_text_l(struct archive_acl *acl, int flags,
636     const char **acl_text, size_t *acl_text_len,
637     struct archive_string_conv *sc)
638 {
639         int count;
640         size_t length;
641         const char *name;
642         const char *prefix;
643         char separator;
644         struct archive_acl_entry *ap;
645         size_t len;
646         int id, r;
647         char *p;
648
649         if (acl->acl_text != NULL) {
650                 free (acl->acl_text);
651                 acl->acl_text = NULL;
652         }
653
654         *acl_text = NULL;
655         if (acl_text_len != NULL)
656                 *acl_text_len = 0;
657         separator = ',';
658         count = 0;
659         length = 0;
660         ap = acl->acl_head;
661         while (ap != NULL) {
662                 if ((ap->type & flags) != 0) {
663                         count++;
664                         if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
665                             (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
666                                 length += 8; /* "default:" */
667                         length += 5; /* tag name */
668                         length += 1; /* colon */
669                         r = archive_mstring_get_mbs_l(
670                             &ap->name, &name, &len, sc);
671                         if (r != 0)
672                                 return (-1);
673                         if (len > 0 && name != NULL)
674                                 length += len;
675                         else
676                                 length += sizeof(uid_t) * 3 + 1;
677                         length ++; /* colon */
678                         length += 3; /* rwx */
679                         length += 1; /* colon */
680                         length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
681                         length ++; /* newline */
682                 }
683                 ap = ap->next;
684         }
685
686         if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
687                 length += 10; /* "user::rwx\n" */
688                 length += 11; /* "group::rwx\n" */
689                 length += 11; /* "other::rwx\n" */
690         }
691
692         if (count == 0)
693                 return (0);
694
695         /* Now, allocate the string and actually populate it. */
696         p = acl->acl_text = (char *)malloc(length);
697         if (p == NULL)
698                 return (-1);
699         count = 0;
700         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
701                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
702                     acl->mode & 0700, -1);
703                 *p++ = ',';
704                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
705                     acl->mode & 0070, -1);
706                 *p++ = ',';
707                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
708                     acl->mode & 0007, -1);
709                 count += 3;
710
711                 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
712                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
713                                 continue;
714                         r = archive_mstring_get_mbs_l(
715                             &ap->name, &name, &len, sc);
716                         if (r != 0)
717                                 return (-1);
718                         *p++ = separator;
719                         if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
720                                 id = ap->id;
721                         } else {
722                                 id = -1;
723                         }
724                         append_entry(&p, NULL, ap->tag, name,
725                             ap->permset, id);
726                         count++;
727                 }
728         }
729
730
731         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
732                 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
733                         prefix = "default:";
734                 else
735                         prefix = NULL;
736                 count = 0;
737                 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
738                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
739                                 continue;
740                         r = archive_mstring_get_mbs_l(
741                             &ap->name, &name, &len, sc);
742                         if (r != 0)
743                                 return (-1);
744                         if (count > 0)
745                                 *p++ = separator;
746                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
747                                 id = ap->id;
748                         else
749                                 id = -1;
750                         append_entry(&p, prefix, ap->tag,
751                             name, ap->permset, id);
752                         count ++;
753                 }
754         }
755
756         *acl_text = acl->acl_text;
757         if (acl_text_len != NULL)
758                 *acl_text_len = strlen(acl->acl_text);
759         return (0);
760 }
761
762 static void
763 append_id(char **p, int id)
764 {
765         if (id < 0)
766                 id = 0;
767         if (id > 9)
768                 append_id(p, id / 10);
769         *(*p)++ = "0123456789"[id % 10];
770 }
771
772 static void
773 append_entry(char **p, const char *prefix, int tag,
774     const char *name, int perm, int id)
775 {
776         if (prefix != NULL) {
777                 strcpy(*p, prefix);
778                 *p += strlen(*p);
779         }
780         switch (tag) {
781         case ARCHIVE_ENTRY_ACL_USER_OBJ:
782                 name = NULL;
783                 id = -1;
784                 /* FALLTHROUGH */
785         case ARCHIVE_ENTRY_ACL_USER:
786                 strcpy(*p, "user");
787                 break;
788         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
789                 name = NULL;
790                 id = -1;
791                 /* FALLTHROUGH */
792         case ARCHIVE_ENTRY_ACL_GROUP:
793                 strcpy(*p, "group");
794                 break;
795         case ARCHIVE_ENTRY_ACL_MASK:
796                 strcpy(*p, "mask");
797                 name = NULL;
798                 id = -1;
799                 break;
800         case ARCHIVE_ENTRY_ACL_OTHER:
801                 strcpy(*p, "other");
802                 name = NULL;
803                 id = -1;
804                 break;
805         }
806         *p += strlen(*p);
807         *(*p)++ = ':';
808         if (name != NULL) {
809                 strcpy(*p, name);
810                 *p += strlen(*p);
811         } else if (tag == ARCHIVE_ENTRY_ACL_USER
812             || tag == ARCHIVE_ENTRY_ACL_GROUP) {
813                 append_id(p, id);
814                 id = -1;
815         }
816         *(*p)++ = ':';
817         *(*p)++ = (perm & 0444) ? 'r' : '-';
818         *(*p)++ = (perm & 0222) ? 'w' : '-';
819         *(*p)++ = (perm & 0111) ? 'x' : '-';
820         if (id != -1) {
821                 *(*p)++ = ':';
822                 append_id(p, id);
823         }
824         **p = '\0';
825 }
826
827 /*
828  * Parse a textual ACL.  This automatically recognizes and supports
829  * extensions described above.  The 'type' argument is used to
830  * indicate the type that should be used for any entries not
831  * explicitly marked as "default:".
832  */
833 int
834 archive_acl_parse_w(struct archive_acl *acl,
835     const wchar_t *text, int default_type)
836 {
837         struct {
838                 const wchar_t *start;
839                 const wchar_t *end;
840         } field[4], name;
841
842         int fields, n;
843         int type, tag, permset, id;
844         wchar_t sep;
845
846         while (text != NULL  &&  *text != L'\0') {
847                 /*
848                  * Parse the fields out of the next entry,
849                  * advance 'text' to start of next entry.
850                  */
851                 fields = 0;
852                 do {
853                         const wchar_t *start, *end;
854                         next_field_w(&text, &start, &end, &sep);
855                         if (fields < 4) {
856                                 field[fields].start = start;
857                                 field[fields].end = end;
858                         }
859                         ++fields;
860                 } while (sep == L':');
861
862                 /* Set remaining fields to blank. */
863                 for (n = fields; n < 4; ++n)
864                         field[n].start = field[n].end = NULL;
865
866                 /* Check for a numeric ID in field 1 or 3. */
867                 id = -1;
868                 isint_w(field[1].start, field[1].end, &id);
869                 /* Field 3 is optional. */
870                 if (id == -1 && fields > 3)
871                         isint_w(field[3].start, field[3].end, &id);
872
873                 /*
874                  * Solaris extension:  "defaultuser::rwx" is the
875                  * default ACL corresponding to "user::rwx", etc.
876                  */
877                 if (field[0].end - field[0].start > 7
878                     && wmemcmp(field[0].start, L"default", 7) == 0) {
879                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
880                         field[0].start += 7;
881                 } else
882                         type = default_type;
883
884                 name.start = name.end = NULL;
885                 if (prefix_w(field[0].start, field[0].end, L"user")) {
886                         if (!ismode_w(field[2].start, field[2].end, &permset))
887                                 return (ARCHIVE_WARN);
888                         if (id != -1 || field[1].start < field[1].end) {
889                                 tag = ARCHIVE_ENTRY_ACL_USER;
890                                 name = field[1];
891                         } else
892                                 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
893                 } else if (prefix_w(field[0].start, field[0].end, L"group")) {
894                         if (!ismode_w(field[2].start, field[2].end, &permset))
895                                 return (ARCHIVE_WARN);
896                         if (id != -1 || field[1].start < field[1].end) {
897                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
898                                 name = field[1];
899                         } else
900                                 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
901                 } else if (prefix_w(field[0].start, field[0].end, L"other")) {
902                         if (fields == 2
903                             && field[1].start < field[1].end
904                             && ismode_w(field[1].start, field[1].end, &permset)) {
905                                 /* This is Solaris-style "other:rwx" */
906                         } else if (fields == 3
907                             && field[1].start == field[1].end
908                             && field[2].start < field[2].end
909                             && ismode_w(field[2].start, field[2].end, &permset)) {
910                                 /* This is FreeBSD-style "other::rwx" */
911                         } else
912                                 return (ARCHIVE_WARN);
913                         tag = ARCHIVE_ENTRY_ACL_OTHER;
914                 } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
915                         if (fields == 2
916                             && field[1].start < field[1].end
917                             && ismode_w(field[1].start, field[1].end, &permset)) {
918                                 /* This is Solaris-style "mask:rwx" */
919                         } else if (fields == 3
920                             && field[1].start == field[1].end
921                             && field[2].start < field[2].end
922                             && ismode_w(field[2].start, field[2].end, &permset)) {
923                                 /* This is FreeBSD-style "mask::rwx" */
924                         } else
925                                 return (ARCHIVE_WARN);
926                         tag = ARCHIVE_ENTRY_ACL_MASK;
927                 } else
928                         return (ARCHIVE_WARN);
929
930                 /* Add entry to the internal list. */
931                 archive_acl_add_entry_w_len(acl, type, permset,
932                     tag, id, name.start, name.end - name.start);
933         }
934         return (ARCHIVE_OK);
935 }
936
937 /*
938  * Parse a string to a positive decimal integer.  Returns true if
939  * the string is non-empty and consists only of decimal digits,
940  * false otherwise.
941  */
942 static int
943 isint_w(const wchar_t *start, const wchar_t *end, int *result)
944 {
945         int n = 0;
946         if (start >= end)
947                 return (0);
948         while (start < end) {
949                 if (*start < '0' || *start > '9')
950                         return (0);
951                 if (n > (INT_MAX / 10) ||
952                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
953                         n = INT_MAX;
954                 } else {
955                         n *= 10;
956                         n += *start - '0';
957                 }
958                 start++;
959         }
960         *result = n;
961         return (1);
962 }
963
964 /*
965  * Parse a string as a mode field.  Returns true if
966  * the string is non-empty and consists only of mode characters,
967  * false otherwise.
968  */
969 static int
970 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
971 {
972         const wchar_t *p;
973
974         if (start >= end)
975                 return (0);
976         p = start;
977         *permset = 0;
978         while (p < end) {
979                 switch (*p++) {
980                 case 'r': case 'R':
981                         *permset |= ARCHIVE_ENTRY_ACL_READ;
982                         break;
983                 case 'w': case 'W':
984                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
985                         break;
986                 case 'x': case 'X':
987                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
988                         break;
989                 case '-':
990                         break;
991                 default:
992                         return (0);
993                 }
994         }
995         return (1);
996 }
997
998 /*
999  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1000  * to point to just after the separator.  *start points to the first
1001  * character of the matched text and *end just after the last
1002  * character of the matched identifier.  In particular *end - *start
1003  * is the length of the field body, not including leading or trailing
1004  * whitespace.
1005  */
1006 static void
1007 next_field_w(const wchar_t **wp, const wchar_t **start,
1008     const wchar_t **end, wchar_t *sep)
1009 {
1010         /* Skip leading whitespace to find start of field. */
1011         while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1012                 (*wp)++;
1013         }
1014         *start = *wp;
1015
1016         /* Scan for the separator. */
1017         while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1018             **wp != L'\n') {
1019                 (*wp)++;
1020         }
1021         *sep = **wp;
1022
1023         /* Trim trailing whitespace to locate end of field. */
1024         *end = *wp - 1;
1025         while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1026                 (*end)--;
1027         }
1028         (*end)++;
1029
1030         /* Adjust scanner location. */
1031         if (**wp != L'\0')
1032                 (*wp)++;
1033 }
1034
1035 /*
1036  * Return true if the characters [start...end) are a prefix of 'test'.
1037  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1038  */
1039 static int
1040 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1041 {
1042         if (start == end)
1043                 return (0);
1044
1045         if (*start++ != *test++)
1046                 return (0);
1047
1048         while (start < end  &&  *start++ == *test++)
1049                 ;
1050
1051         if (start < end)
1052                 return (0);
1053
1054         return (1);
1055 }
1056
1057 /*
1058  * Parse a textual ACL.  This automatically recognizes and supports
1059  * extensions described above.  The 'type' argument is used to
1060  * indicate the type that should be used for any entries not
1061  * explicitly marked as "default:".
1062  */
1063 int
1064 archive_acl_parse_l(struct archive_acl *acl,
1065     const char *text, int default_type, struct archive_string_conv *sc)
1066 {
1067         struct {
1068                 const char *start;
1069                 const char *end;
1070         } field[4], name;
1071
1072         int fields, n, r, ret = ARCHIVE_OK;
1073         int type, tag, permset, id;
1074         char sep;
1075
1076         while (text != NULL  &&  *text != '\0') {
1077                 /*
1078                  * Parse the fields out of the next entry,
1079                  * advance 'text' to start of next entry.
1080                  */
1081                 fields = 0;
1082                 do {
1083                         const char *start, *end;
1084                         next_field(&text, &start, &end, &sep);
1085                         if (fields < 4) {
1086                                 field[fields].start = start;
1087                                 field[fields].end = end;
1088                         }
1089                         ++fields;
1090                 } while (sep == ':');
1091
1092                 /* Set remaining fields to blank. */
1093                 for (n = fields; n < 4; ++n)
1094                         field[n].start = field[n].end = NULL;
1095
1096                 /* Check for a numeric ID in field 1 or 3. */
1097                 id = -1;
1098                 isint(field[1].start, field[1].end, &id);
1099                 /* Field 3 is optional. */
1100                 if (id == -1 && fields > 3)
1101                         isint(field[3].start, field[3].end, &id);
1102
1103                 /*
1104                  * Solaris extension:  "defaultuser::rwx" is the
1105                  * default ACL corresponding to "user::rwx", etc.
1106                  */
1107                 if (field[0].end - field[0].start > 7
1108                     && memcmp(field[0].start, "default", 7) == 0) {
1109                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1110                         field[0].start += 7;
1111                 } else
1112                         type = default_type;
1113
1114                 name.start = name.end = NULL;
1115                 if (prefix_c(field[0].start, field[0].end, "user")) {
1116                         if (!ismode(field[2].start, field[2].end, &permset))
1117                                 return (ARCHIVE_WARN);
1118                         if (id != -1 || field[1].start < field[1].end) {
1119                                 tag = ARCHIVE_ENTRY_ACL_USER;
1120                                 name = field[1];
1121                         } else
1122                                 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1123                 } else if (prefix_c(field[0].start, field[0].end, "group")) {
1124                         if (!ismode(field[2].start, field[2].end, &permset))
1125                                 return (ARCHIVE_WARN);
1126                         if (id != -1 || field[1].start < field[1].end) {
1127                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
1128                                 name = field[1];
1129                         } else
1130                                 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1131                 } else if (prefix_c(field[0].start, field[0].end, "other")) {
1132                         if (fields == 2
1133                             && field[1].start < field[1].end
1134                             && ismode(field[1].start, field[1].end, &permset)) {
1135                                 /* This is Solaris-style "other:rwx" */
1136                         } else if (fields == 3
1137                             && field[1].start == field[1].end
1138                             && field[2].start < field[2].end
1139                             && ismode(field[2].start, field[2].end, &permset)) {
1140                                 /* This is FreeBSD-style "other::rwx" */
1141                         } else
1142                                 return (ARCHIVE_WARN);
1143                         tag = ARCHIVE_ENTRY_ACL_OTHER;
1144                 } else if (prefix_c(field[0].start, field[0].end, "mask")) {
1145                         if (fields == 2
1146                             && field[1].start < field[1].end
1147                             && ismode(field[1].start, field[1].end, &permset)) {
1148                                 /* This is Solaris-style "mask:rwx" */
1149                         } else if (fields == 3
1150                             && field[1].start == field[1].end
1151                             && field[2].start < field[2].end
1152                             && ismode(field[2].start, field[2].end, &permset)) {
1153                                 /* This is FreeBSD-style "mask::rwx" */
1154                         } else
1155                                 return (ARCHIVE_WARN);
1156                         tag = ARCHIVE_ENTRY_ACL_MASK;
1157                 } else
1158                         return (ARCHIVE_WARN);
1159
1160                 /* Add entry to the internal list. */
1161                 r = archive_acl_add_entry_len_l(acl, type, permset,
1162                     tag, id, name.start, name.end - name.start, sc);
1163                 if (r < ARCHIVE_WARN)
1164                         return (r);
1165                 if (r != ARCHIVE_OK)
1166                         ret = ARCHIVE_WARN;
1167         }
1168         return (ret);
1169 }
1170
1171 /*
1172  * Parse a string to a positive decimal integer.  Returns true if
1173  * the string is non-empty and consists only of decimal digits,
1174  * false otherwise.
1175  */
1176 static int
1177 isint(const char *start, const char *end, int *result)
1178 {
1179         int n = 0;
1180         if (start >= end)
1181                 return (0);
1182         while (start < end) {
1183                 if (*start < '0' || *start > '9')
1184                         return (0);
1185                 if (n > (INT_MAX / 10) ||
1186                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1187                         n = INT_MAX;
1188                 } else {
1189                         n *= 10;
1190                         n += *start - '0';
1191                 }
1192                 start++;
1193         }
1194         *result = n;
1195         return (1);
1196 }
1197
1198 /*
1199  * Parse a string as a mode field.  Returns true if
1200  * the string is non-empty and consists only of mode characters,
1201  * false otherwise.
1202  */
1203 static int
1204 ismode(const char *start, const char *end, int *permset)
1205 {
1206         const char *p;
1207
1208         if (start >= end)
1209                 return (0);
1210         p = start;
1211         *permset = 0;
1212         while (p < end) {
1213                 switch (*p++) {
1214                 case 'r': case 'R':
1215                         *permset |= ARCHIVE_ENTRY_ACL_READ;
1216                         break;
1217                 case 'w': case 'W':
1218                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1219                         break;
1220                 case 'x': case 'X':
1221                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1222                         break;
1223                 case '-':
1224                         break;
1225                 default:
1226                         return (0);
1227                 }
1228         }
1229         return (1);
1230 }
1231
1232 /*
1233  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1234  * to point to just after the separator.  *start points to the first
1235  * character of the matched text and *end just after the last
1236  * character of the matched identifier.  In particular *end - *start
1237  * is the length of the field body, not including leading or trailing
1238  * whitespace.
1239  */
1240 static void
1241 next_field(const char **p, const char **start,
1242     const char **end, char *sep)
1243 {
1244         /* Skip leading whitespace to find start of field. */
1245         while (**p == ' ' || **p == '\t' || **p == '\n') {
1246                 (*p)++;
1247         }
1248         *start = *p;
1249
1250         /* Scan for the separator. */
1251         while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1252                 (*p)++;
1253         }
1254         *sep = **p;
1255
1256         /* Trim trailing whitespace to locate end of field. */
1257         *end = *p - 1;
1258         while (**end == ' ' || **end == '\t' || **end == '\n') {
1259                 (*end)--;
1260         }
1261         (*end)++;
1262
1263         /* Adjust scanner location. */
1264         if (**p != '\0')
1265                 (*p)++;
1266 }
1267
1268 /*
1269  * Return true if the characters [start...end) are a prefix of 'test'.
1270  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1271  */
1272 static int
1273 prefix_c(const char *start, const char *end, const char *test)
1274 {
1275         if (start == end)
1276                 return (0);
1277
1278         if (*start++ != *test++)
1279                 return (0);
1280
1281         while (start < end  &&  *start++ == *test++)
1282                 ;
1283
1284         if (start < end)
1285                 return (0);
1286
1287         return (1);
1288 }