2 * Copyright (c) 2003-2010 Tim Kientzle
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
39 #include "archive_acl_private.h"
40 #include "archive_entry.h"
41 #include "archive_private.h"
44 #define max(a, b) ((a)>(b)?(a):(b))
47 /* Good enough for simple equality testing, but not for sorting. */
48 #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
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 isint_w(const wchar_t *start, const wchar_t *end, int *result);
56 static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);
57 static void next_field_w(const wchar_t **wp, const wchar_t **start,
58 const wchar_t **end, wchar_t *sep);
59 static int prefix_w(const wchar_t *start, const wchar_t *end,
61 static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
62 const wchar_t *wname, int perm, int id);
63 static void append_id_w(wchar_t **wp, int id);
64 static int isint(const char *start, const char *end, int *result);
65 static int ismode(const char *start, const char *end, int *result);
66 static void next_field(const char **p, const char **start,
67 const char **end, char *sep);
68 static int prefix(const char *start, const char *end,
70 static void append_entry(char **p, const char *prefix, int tag,
71 const char *name, int perm, int id);
72 static void append_id(char **p, int id);
75 archive_acl_clear(struct archive_acl *acl)
77 struct archive_acl_entry *ap;
79 while (acl->acl_head != NULL) {
80 ap = acl->acl_head->next;
81 archive_mstring_clean(&acl->acl_head->name);
85 if (acl->acl_text_w != NULL) {
86 free(acl->acl_text_w);
87 acl->acl_text_w = NULL;
89 if (acl->acl_text != NULL) {
94 acl->acl_state = 0; /* Not counting. */
98 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
100 struct archive_acl_entry *ap, *ap2;
102 archive_acl_clear(dest);
104 dest->mode = src->mode;
107 ap2 = acl_new_entry(dest,
108 ap->type, ap->permset, ap->tag, ap->id);
110 archive_mstring_copy(&ap2->name, &ap->name);
116 archive_acl_add_entry(struct archive_acl *acl,
117 int type, int permset, int tag, int id, const char *name)
119 struct archive_acl_entry *ap;
121 if (acl_special(acl, type, permset, tag) == 0)
123 ap = acl_new_entry(acl, type, permset, tag, id);
126 return ARCHIVE_FAILED;
128 if (name != NULL && *name != '\0')
129 archive_mstring_copy_mbs(&ap->name, name);
131 archive_mstring_clean(&ap->name);
136 archive_acl_add_entry_w_len(struct archive_acl *acl,
137 int type, int permset, int tag, int id, const wchar_t *name, size_t len)
139 struct archive_acl_entry *ap;
141 if (acl_special(acl, type, permset, tag) == 0)
143 ap = acl_new_entry(acl, type, permset, tag, id);
146 return ARCHIVE_FAILED;
148 if (name != NULL && *name != L'\0' && len > 0)
149 archive_mstring_copy_wcs_len(&ap->name, name, len);
151 archive_mstring_clean(&ap->name);
156 archive_acl_add_entry_len_l(struct archive_acl *acl,
157 int type, int permset, int tag, int id, const char *name, size_t len,
158 struct archive_string_conv *sc)
160 struct archive_acl_entry *ap;
163 if (acl_special(acl, type, permset, tag) == 0)
165 ap = acl_new_entry(acl, type, permset, tag, id);
168 return ARCHIVE_FAILED;
170 if (name != NULL && *name != '\0' && len > 0) {
171 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
174 archive_mstring_clean(&ap->name);
178 else if (errno == ENOMEM)
179 return (ARCHIVE_FATAL);
181 return (ARCHIVE_WARN);
185 * If this ACL entry is part of the standard POSIX permissions set,
186 * store the permissions in the stat structure and return zero.
189 acl_special(struct archive_acl *acl, int type, int permset, int tag)
191 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
192 && ((permset & ~007) == 0)) {
194 case ARCHIVE_ENTRY_ACL_USER_OBJ:
196 acl->mode |= (permset & 7) << 6;
198 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
200 acl->mode |= (permset & 7) << 3;
202 case ARCHIVE_ENTRY_ACL_OTHER:
204 acl->mode |= permset & 7;
212 * Allocate and populate a new ACL entry with everything but the
215 static struct archive_acl_entry *
216 acl_new_entry(struct archive_acl *acl,
217 int type, int permset, int tag, int id)
219 struct archive_acl_entry *ap, *aq;
221 /* Type argument must be a valid NFS4 or POSIX.1e type.
222 * The type must agree with anything already set and
223 * the permset must be compatible. */
224 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
225 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
229 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
230 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
233 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
234 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
237 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
244 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
246 case ARCHIVE_ENTRY_ACL_USER:
247 case ARCHIVE_ENTRY_ACL_USER_OBJ:
248 case ARCHIVE_ENTRY_ACL_GROUP:
249 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
250 /* Tags valid in both NFS4 and POSIX.1e */
252 case ARCHIVE_ENTRY_ACL_MASK:
253 case ARCHIVE_ENTRY_ACL_OTHER:
254 /* Tags valid only in POSIX.1e. */
255 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
259 case ARCHIVE_ENTRY_ACL_EVERYONE:
260 /* Tags valid only in NFS4. */
261 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
266 /* No other values are valid. */
270 if (acl->acl_text_w != NULL) {
271 free(acl->acl_text_w);
272 acl->acl_text_w = NULL;
274 if (acl->acl_text != NULL) {
276 acl->acl_text = NULL;
279 /* If there's a matching entry already in the list, overwrite it. */
283 if (ap->type == type && ap->tag == tag && ap->id == id) {
284 ap->permset = permset;
291 /* Add a new entry to the end of the list. */
292 ap = (struct archive_acl_entry *)malloc(sizeof(*ap));
295 memset(ap, 0, sizeof(*ap));
303 ap->permset = permset;
304 acl->acl_types |= type;
309 * Return a count of entries matching "want_type".
312 archive_acl_count(struct archive_acl *acl, int want_type)
315 struct archive_acl_entry *ap;
320 if ((ap->type & want_type) != 0)
325 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
331 * Prepare for reading entries from the ACL data. Returns a count
332 * of entries matching "want_type", or zero if there are no
333 * non-extended ACL entries of that type.
336 archive_acl_reset(struct archive_acl *acl, int want_type)
340 count = archive_acl_count(acl, want_type);
343 * If the only entries are the three standard ones,
344 * then don't return any ACL data. (In this case,
345 * client can just use chmod(2) to set permissions.)
347 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
353 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
356 acl->acl_p = acl->acl_head;
362 * Return the next ACL entry in the list. Fake entries for the
363 * standard permissions and include them in the returned list.
366 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
367 int *permset, int *tag, int *id, const char **name)
373 * The acl_state is either zero (no entries available), -1
374 * (reading from list), or an entry type (retrieve that type
375 * from ae_stat.aest_mode).
377 if (acl->acl_state == 0)
378 return (ARCHIVE_WARN);
380 /* The first three access entries are special. */
381 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
382 switch (acl->acl_state) {
383 case ARCHIVE_ENTRY_ACL_USER_OBJ:
384 *permset = (acl->mode >> 6) & 7;
385 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
386 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
387 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
389 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
390 *permset = (acl->mode >> 3) & 7;
391 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
392 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
393 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
395 case ARCHIVE_ENTRY_ACL_OTHER:
396 *permset = acl->mode & 7;
397 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
398 *tag = ARCHIVE_ENTRY_ACL_OTHER;
400 acl->acl_p = acl->acl_head;
407 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
408 acl->acl_p = acl->acl_p->next;
409 if (acl->acl_p == NULL) {
416 return (ARCHIVE_EOF); /* End of ACL entries. */
418 *type = acl->acl_p->type;
419 *permset = acl->acl_p->permset;
420 *tag = acl->acl_p->tag;
421 *id = acl->acl_p->id;
422 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0)
424 acl->acl_p = acl->acl_p->next;
429 * Generate a text version of the ACL. The flags parameter controls
430 * the style of the generated ACL.
433 archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
437 const wchar_t *wname;
438 const wchar_t *prefix;
440 struct archive_acl_entry *ap;
444 if (acl->acl_text_w != NULL) {
445 free (acl->acl_text_w);
446 acl->acl_text_w = NULL;
454 if ((ap->type & flags) != 0) {
456 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
457 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
458 length += 8; /* "default:" */
459 length += 5; /* tag name */
460 length += 1; /* colon */
461 if (archive_mstring_get_wcs(a, &ap->name, &wname) == 0 &&
463 length += wcslen(wname);
465 length += sizeof(uid_t) * 3 + 1;
466 length ++; /* colon */
467 length += 3; /* rwx */
468 length += 1; /* colon */
469 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
470 length ++; /* newline */
475 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
476 length += 10; /* "user::rwx\n" */
477 length += 11; /* "group::rwx\n" */
478 length += 11; /* "other::rwx\n" */
484 /* Now, allocate the string and actually populate it. */
485 wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
487 __archive_errx(1, "No memory to generate the text version of the ACL");
489 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
490 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
491 acl->mode & 0700, -1);
493 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
494 acl->mode & 0070, -1);
496 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
497 acl->mode & 0007, -1);
502 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 &&
503 archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
505 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
509 append_entry_w(&wp, NULL, ap->tag, wname,
518 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
519 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
520 prefix = L"default:";
526 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 &&
527 archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
530 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
534 append_entry_w(&wp, prefix, ap->tag,
535 wname, ap->permset, id);
542 return (acl->acl_text_w);
547 append_id_w(wchar_t **wp, int id)
552 append_id_w(wp, id / 10);
553 *(*wp)++ = L"0123456789"[id % 10];
557 append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
558 const wchar_t *wname, int perm, int id)
560 if (prefix != NULL) {
565 case ARCHIVE_ENTRY_ACL_USER_OBJ:
569 case ARCHIVE_ENTRY_ACL_USER:
570 wcscpy(*wp, L"user");
572 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
576 case ARCHIVE_ENTRY_ACL_GROUP:
577 wcscpy(*wp, L"group");
579 case ARCHIVE_ENTRY_ACL_MASK:
580 wcscpy(*wp, L"mask");
584 case ARCHIVE_ENTRY_ACL_OTHER:
585 wcscpy(*wp, L"other");
595 } else if (tag == ARCHIVE_ENTRY_ACL_USER
596 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
601 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
602 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
603 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
612 archive_acl_text_l(struct archive_acl *acl, int flags,
613 const char **acl_text, size_t *acl_text_len,
614 struct archive_string_conv *sc)
621 struct archive_acl_entry *ap;
626 if (acl->acl_text != NULL) {
627 free (acl->acl_text);
628 acl->acl_text = NULL;
632 if (acl_text_len != NULL)
639 if ((ap->type & flags) != 0) {
641 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
642 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
643 length += 8; /* "default:" */
644 length += 5; /* tag name */
645 length += 1; /* colon */
646 r = archive_mstring_get_mbs_l(
647 &ap->name, &name, &len, sc);
650 if (len > 0 && name != NULL)
653 length += sizeof(uid_t) * 3 + 1;
654 length ++; /* colon */
655 length += 3; /* rwx */
656 length += 1; /* colon */
657 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
658 length ++; /* newline */
663 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
664 length += 10; /* "user::rwx\n" */
665 length += 11; /* "group::rwx\n" */
666 length += 11; /* "other::rwx\n" */
672 /* Now, allocate the string and actually populate it. */
673 p = acl->acl_text = (char *)malloc(length);
675 __archive_errx(1, "No memory to generate the text version of the ACL");
677 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
678 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
679 acl->mode & 0700, -1);
681 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
682 acl->mode & 0070, -1);
684 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
685 acl->mode & 0007, -1);
688 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
689 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
691 r = archive_mstring_get_mbs_l(
692 &ap->name, &name, &len, sc);
696 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
700 append_entry(&p, NULL, ap->tag, name,
707 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
708 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
713 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
714 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
716 r = archive_mstring_get_mbs_l(
717 &ap->name, &name, &len, sc);
722 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
726 append_entry(&p, prefix, ap->tag,
727 name, ap->permset, id);
732 *acl_text = acl->acl_text;
733 if (acl_text_len != NULL)
734 *acl_text_len = strlen(acl->acl_text);
739 append_id(char **p, int id)
744 append_id(p, id / 10);
745 *(*p)++ = "0123456789"[id % 10];
749 append_entry(char **p, const char *prefix, int tag,
750 const char *name, int perm, int id)
752 if (prefix != NULL) {
757 case ARCHIVE_ENTRY_ACL_USER_OBJ:
761 case ARCHIVE_ENTRY_ACL_USER:
764 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
768 case ARCHIVE_ENTRY_ACL_GROUP:
771 case ARCHIVE_ENTRY_ACL_MASK:
776 case ARCHIVE_ENTRY_ACL_OTHER:
787 } else if (tag == ARCHIVE_ENTRY_ACL_USER
788 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
793 *(*p)++ = (perm & 0444) ? 'r' : '-';
794 *(*p)++ = (perm & 0222) ? 'w' : '-';
795 *(*p)++ = (perm & 0111) ? 'x' : '-';
804 * Parse a textual ACL. This automatically recognizes and supports
805 * extensions described above. The 'type' argument is used to
806 * indicate the type that should be used for any entries not
807 * explicitly marked as "default:".
810 archive_acl_parse_w(struct archive_acl *acl,
811 const wchar_t *text, int default_type)
814 const wchar_t *start;
819 int type, tag, permset, id;
822 while (text != NULL && *text != L'\0') {
824 * Parse the fields out of the next entry,
825 * advance 'text' to start of next entry.
829 const wchar_t *start, *end;
830 next_field_w(&text, &start, &end, &sep);
832 field[fields].start = start;
833 field[fields].end = end;
836 } while (sep == L':');
838 /* Set remaining fields to blank. */
839 for (n = fields; n < 4; ++n)
840 field[n].start = field[n].end = NULL;
842 /* Check for a numeric ID in field 1 or 3. */
844 isint_w(field[1].start, field[1].end, &id);
845 /* Field 3 is optional. */
846 if (id == -1 && fields > 3)
847 isint_w(field[3].start, field[3].end, &id);
850 * Solaris extension: "defaultuser::rwx" is the
851 * default ACL corresponding to "user::rwx", etc.
853 if (field[0].end - field[0].start > 7
854 && wmemcmp(field[0].start, L"default", 7) == 0) {
855 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
860 name.start = name.end = NULL;
861 if (prefix_w(field[0].start, field[0].end, L"user")) {
862 if (!ismode_w(field[2].start, field[2].end, &permset))
863 return (ARCHIVE_WARN);
864 if (id != -1 || field[1].start < field[1].end) {
865 tag = ARCHIVE_ENTRY_ACL_USER;
868 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
869 } else if (prefix_w(field[0].start, field[0].end, L"group")) {
870 if (!ismode_w(field[2].start, field[2].end, &permset))
871 return (ARCHIVE_WARN);
872 if (id != -1 || field[1].start < field[1].end) {
873 tag = ARCHIVE_ENTRY_ACL_GROUP;
876 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
877 } else if (prefix_w(field[0].start, field[0].end, L"other")) {
879 && field[1].start < field[1].end
880 && ismode_w(field[1].start, field[1].end, &permset)) {
881 /* This is Solaris-style "other:rwx" */
882 } else if (fields == 3
883 && field[1].start == field[1].end
884 && field[2].start < field[2].end
885 && ismode_w(field[2].start, field[2].end, &permset)) {
886 /* This is FreeBSD-style "other::rwx" */
888 return (ARCHIVE_WARN);
889 tag = ARCHIVE_ENTRY_ACL_OTHER;
890 } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
892 && field[1].start < field[1].end
893 && ismode_w(field[1].start, field[1].end, &permset)) {
894 /* This is Solaris-style "mask:rwx" */
895 } else if (fields == 3
896 && field[1].start == field[1].end
897 && field[2].start < field[2].end
898 && ismode_w(field[2].start, field[2].end, &permset)) {
899 /* This is FreeBSD-style "mask::rwx" */
901 return (ARCHIVE_WARN);
902 tag = ARCHIVE_ENTRY_ACL_MASK;
904 return (ARCHIVE_WARN);
906 /* Add entry to the internal list. */
907 archive_acl_add_entry_w_len(acl, type, permset,
908 tag, id, name.start, name.end - name.start);
914 * Parse a string to a positive decimal integer. Returns true if
915 * the string is non-empty and consists only of decimal digits,
919 isint_w(const wchar_t *start, const wchar_t *end, int *result)
924 while (start < end) {
925 if (*start < '0' || *start > '9')
927 if (n > (INT_MAX / 10) ||
928 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
941 * Parse a string as a mode field. Returns true if
942 * the string is non-empty and consists only of mode characters,
946 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
957 *permset |= ARCHIVE_ENTRY_ACL_READ;
960 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
963 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
975 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
976 * to point to just after the separator. *start points to the first
977 * character of the matched text and *end just after the last
978 * character of the matched identifier. In particular *end - *start
979 * is the length of the field body, not including leading or trailing
983 next_field_w(const wchar_t **wp, const wchar_t **start,
984 const wchar_t **end, wchar_t *sep)
986 /* Skip leading whitespace to find start of field. */
987 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
992 /* Scan for the separator. */
993 while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
999 /* Trim trailing whitespace to locate end of field. */
1001 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1006 /* Adjust scanner location. */
1012 * Return true if the characters [start...end) are a prefix of 'test'.
1013 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1016 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1021 if (*start++ != *test++)
1024 while (start < end && *start++ == *test++)
1034 * Parse a textual ACL. This automatically recognizes and supports
1035 * extensions described above. The 'type' argument is used to
1036 * indicate the type that should be used for any entries not
1037 * explicitly marked as "default:".
1040 archive_acl_parse_l(struct archive_acl *acl,
1041 const char *text, int default_type, struct archive_string_conv *sc)
1048 int fields, n, r, ret = ARCHIVE_OK;
1049 int type, tag, permset, id;
1052 while (text != NULL && *text != '\0') {
1054 * Parse the fields out of the next entry,
1055 * advance 'text' to start of next entry.
1059 const char *start, *end;
1060 next_field(&text, &start, &end, &sep);
1062 field[fields].start = start;
1063 field[fields].end = end;
1066 } while (sep == ':');
1068 /* Set remaining fields to blank. */
1069 for (n = fields; n < 4; ++n)
1070 field[n].start = field[n].end = NULL;
1072 /* Check for a numeric ID in field 1 or 3. */
1074 isint(field[1].start, field[1].end, &id);
1075 /* Field 3 is optional. */
1076 if (id == -1 && fields > 3)
1077 isint(field[3].start, field[3].end, &id);
1080 * Solaris extension: "defaultuser::rwx" is the
1081 * default ACL corresponding to "user::rwx", etc.
1083 if (field[0].end - field[0].start > 7
1084 && memcmp(field[0].start, "default", 7) == 0) {
1085 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1086 field[0].start += 7;
1088 type = default_type;
1090 name.start = name.end = NULL;
1091 if (prefix(field[0].start, field[0].end, "user")) {
1092 if (!ismode(field[2].start, field[2].end, &permset))
1093 return (ARCHIVE_WARN);
1094 if (id != -1 || field[1].start < field[1].end) {
1095 tag = ARCHIVE_ENTRY_ACL_USER;
1098 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1099 } else if (prefix(field[0].start, field[0].end, "group")) {
1100 if (!ismode(field[2].start, field[2].end, &permset))
1101 return (ARCHIVE_WARN);
1102 if (id != -1 || field[1].start < field[1].end) {
1103 tag = ARCHIVE_ENTRY_ACL_GROUP;
1106 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1107 } else if (prefix(field[0].start, field[0].end, "other")) {
1109 && field[1].start < field[1].end
1110 && ismode(field[1].start, field[1].end, &permset)) {
1111 /* This is Solaris-style "other:rwx" */
1112 } else if (fields == 3
1113 && field[1].start == field[1].end
1114 && field[2].start < field[2].end
1115 && ismode(field[2].start, field[2].end, &permset)) {
1116 /* This is FreeBSD-style "other::rwx" */
1118 return (ARCHIVE_WARN);
1119 tag = ARCHIVE_ENTRY_ACL_OTHER;
1120 } else if (prefix(field[0].start, field[0].end, "mask")) {
1122 && field[1].start < field[1].end
1123 && ismode(field[1].start, field[1].end, &permset)) {
1124 /* This is Solaris-style "mask:rwx" */
1125 } else if (fields == 3
1126 && field[1].start == field[1].end
1127 && field[2].start < field[2].end
1128 && ismode(field[2].start, field[2].end, &permset)) {
1129 /* This is FreeBSD-style "mask::rwx" */
1131 return (ARCHIVE_WARN);
1132 tag = ARCHIVE_ENTRY_ACL_MASK;
1134 return (ARCHIVE_WARN);
1136 /* Add entry to the internal list. */
1137 r = archive_acl_add_entry_len_l(acl, type, permset,
1138 tag, id, name.start, name.end - name.start, sc);
1139 if (r < ARCHIVE_WARN)
1141 if (r != ARCHIVE_OK)
1148 * Parse a string to a positive decimal integer. Returns true if
1149 * the string is non-empty and consists only of decimal digits,
1153 isint(const char *start, const char *end, int *result)
1158 while (start < end) {
1159 if (*start < '0' || *start > '9')
1161 if (n > (INT_MAX / 10) ||
1162 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1175 * Parse a string as a mode field. Returns true if
1176 * the string is non-empty and consists only of mode characters,
1180 ismode(const char *start, const char *end, int *permset)
1191 *permset |= ARCHIVE_ENTRY_ACL_READ;
1194 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1197 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1209 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
1210 * to point to just after the separator. *start points to the first
1211 * character of the matched text and *end just after the last
1212 * character of the matched identifier. In particular *end - *start
1213 * is the length of the field body, not including leading or trailing
1217 next_field(const char **p, const char **start,
1218 const char **end, char *sep)
1220 /* Skip leading whitespace to find start of field. */
1221 while (**p == ' ' || **p == '\t' || **p == '\n') {
1226 /* Scan for the separator. */
1227 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1232 /* Trim trailing whitespace to locate end of field. */
1234 while (**end == ' ' || **end == '\t' || **end == '\n') {
1239 /* Adjust scanner location. */
1245 * Return true if the characters [start...end) are a prefix of 'test'.
1246 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1249 prefix(const char *start, const char *end, const char *test)
1254 if (*start++ != *test++)
1257 while (start < end && *start++ == *test++)