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 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,
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,
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);
78 archive_acl_clear(struct archive_acl *acl)
80 struct archive_acl_entry *ap;
82 while (acl->acl_head != NULL) {
83 ap = acl->acl_head->next;
84 archive_mstring_clean(&acl->acl_head->name);
88 if (acl->acl_text_w != NULL) {
89 free(acl->acl_text_w);
90 acl->acl_text_w = NULL;
92 if (acl->acl_text != NULL) {
97 acl->acl_state = 0; /* Not counting. */
101 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
103 struct archive_acl_entry *ap, *ap2;
105 archive_acl_clear(dest);
107 dest->mode = src->mode;
110 ap2 = acl_new_entry(dest,
111 ap->type, ap->permset, ap->tag, ap->id);
113 archive_mstring_copy(&ap2->name, &ap->name);
119 archive_acl_add_entry(struct archive_acl *acl,
120 int type, int permset, int tag, int id, const char *name)
122 struct archive_acl_entry *ap;
124 if (acl_special(acl, type, permset, tag) == 0)
126 ap = acl_new_entry(acl, type, permset, tag, id);
129 return ARCHIVE_FAILED;
131 if (name != NULL && *name != '\0')
132 archive_mstring_copy_mbs(&ap->name, name);
134 archive_mstring_clean(&ap->name);
139 archive_acl_add_entry_w_len(struct archive_acl *acl,
140 int type, int permset, int tag, int id, const wchar_t *name, size_t len)
142 struct archive_acl_entry *ap;
144 if (acl_special(acl, type, permset, tag) == 0)
146 ap = acl_new_entry(acl, type, permset, tag, id);
149 return ARCHIVE_FAILED;
151 if (name != NULL && *name != L'\0' && len > 0)
152 archive_mstring_copy_wcs_len(&ap->name, name, len);
154 archive_mstring_clean(&ap->name);
159 archive_acl_add_entry_len_l(struct archive_acl *acl,
160 int type, int permset, int tag, int id, const char *name, size_t len,
161 struct archive_string_conv *sc)
163 struct archive_acl_entry *ap;
166 if (acl_special(acl, type, permset, tag) == 0)
168 ap = acl_new_entry(acl, type, permset, tag, id);
171 return ARCHIVE_FAILED;
173 if (name != NULL && *name != '\0' && len > 0) {
174 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
177 archive_mstring_clean(&ap->name);
181 else if (errno == ENOMEM)
182 return (ARCHIVE_FATAL);
184 return (ARCHIVE_WARN);
188 * If this ACL entry is part of the standard POSIX permissions set,
189 * store the permissions in the stat structure and return zero.
192 acl_special(struct archive_acl *acl, int type, int permset, int tag)
194 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
195 && ((permset & ~007) == 0)) {
197 case ARCHIVE_ENTRY_ACL_USER_OBJ:
199 acl->mode |= (permset & 7) << 6;
201 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
203 acl->mode |= (permset & 7) << 3;
205 case ARCHIVE_ENTRY_ACL_OTHER:
207 acl->mode |= permset & 7;
215 * Allocate and populate a new ACL entry with everything but the
218 static struct archive_acl_entry *
219 acl_new_entry(struct archive_acl *acl,
220 int type, int permset, int tag, int id)
222 struct archive_acl_entry *ap, *aq;
224 /* Type argument must be a valid NFS4 or POSIX.1e type.
225 * The type must agree with anything already set and
226 * the permset must be compatible. */
227 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
228 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
232 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
233 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
236 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
237 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
240 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
247 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
249 case ARCHIVE_ENTRY_ACL_USER:
250 case ARCHIVE_ENTRY_ACL_USER_OBJ:
251 case ARCHIVE_ENTRY_ACL_GROUP:
252 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
253 /* Tags valid in both NFS4 and POSIX.1e */
255 case ARCHIVE_ENTRY_ACL_MASK:
256 case ARCHIVE_ENTRY_ACL_OTHER:
257 /* Tags valid only in POSIX.1e. */
258 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
262 case ARCHIVE_ENTRY_ACL_EVERYONE:
263 /* Tags valid only in NFS4. */
264 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
269 /* No other values are valid. */
273 if (acl->acl_text_w != NULL) {
274 free(acl->acl_text_w);
275 acl->acl_text_w = NULL;
277 if (acl->acl_text != NULL) {
279 acl->acl_text = NULL;
282 /* If there's a matching entry already in the list, overwrite it. */
286 if (ap->type == type && ap->tag == tag && ap->id == id) {
287 ap->permset = permset;
294 /* Add a new entry to the end of the list. */
295 ap = (struct archive_acl_entry *)malloc(sizeof(*ap));
298 memset(ap, 0, sizeof(*ap));
306 ap->permset = permset;
307 acl->acl_types |= type;
312 * Return a count of entries matching "want_type".
315 archive_acl_count(struct archive_acl *acl, int want_type)
318 struct archive_acl_entry *ap;
323 if ((ap->type & want_type) != 0)
328 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
334 * Prepare for reading entries from the ACL data. Returns a count
335 * of entries matching "want_type", or zero if there are no
336 * non-extended ACL entries of that type.
339 archive_acl_reset(struct archive_acl *acl, int want_type)
343 count = archive_acl_count(acl, want_type);
346 * If the only entries are the three standard ones,
347 * then don't return any ACL data. (In this case,
348 * client can just use chmod(2) to set permissions.)
350 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
356 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
359 acl->acl_p = acl->acl_head;
365 * Return the next ACL entry in the list. Fake entries for the
366 * standard permissions and include them in the returned list.
369 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
370 int *permset, int *tag, int *id, const char **name)
376 * The acl_state is either zero (no entries available), -1
377 * (reading from list), or an entry type (retrieve that type
378 * from ae_stat.aest_mode).
380 if (acl->acl_state == 0)
381 return (ARCHIVE_WARN);
383 /* The first three access entries are special. */
384 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
385 switch (acl->acl_state) {
386 case ARCHIVE_ENTRY_ACL_USER_OBJ:
387 *permset = (acl->mode >> 6) & 7;
388 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
389 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
390 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
392 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
393 *permset = (acl->mode >> 3) & 7;
394 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
395 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
396 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
398 case ARCHIVE_ENTRY_ACL_OTHER:
399 *permset = acl->mode & 7;
400 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
401 *tag = ARCHIVE_ENTRY_ACL_OTHER;
403 acl->acl_p = acl->acl_head;
410 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
411 acl->acl_p = acl->acl_p->next;
412 if (acl->acl_p == NULL) {
419 return (ARCHIVE_EOF); /* End of ACL entries. */
421 *type = acl->acl_p->type;
422 *permset = acl->acl_p->permset;
423 *tag = acl->acl_p->tag;
424 *id = acl->acl_p->id;
425 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
427 return (ARCHIVE_FATAL);
430 acl->acl_p = acl->acl_p->next;
435 * Generate a text version of the ACL. The flags parameter controls
436 * the style of the generated ACL.
439 archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
443 const wchar_t *wname;
444 const wchar_t *prefix;
446 struct archive_acl_entry *ap;
450 if (acl->acl_text_w != NULL) {
451 free (acl->acl_text_w);
452 acl->acl_text_w = NULL;
460 if ((ap->type & flags) != 0) {
462 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
463 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
464 length += 8; /* "default:" */
465 length += 5; /* tag name */
466 length += 1; /* colon */
467 r = archive_mstring_get_wcs(a, &ap->name, &wname);
468 if (r == 0 && wname != NULL)
469 length += wcslen(wname);
470 else if (r < 0 && errno == ENOMEM)
473 length += sizeof(uid_t) * 3 + 1;
474 length ++; /* colon */
475 length += 3; /* rwx */
476 length += 1; /* colon */
477 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
478 length ++; /* newline */
483 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
484 length += 10; /* "user::rwx\n" */
485 length += 11; /* "group::rwx\n" */
486 length += 11; /* "other::rwx\n" */
492 /* Now, allocate the string and actually populate it. */
493 wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
497 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
498 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
499 acl->mode & 0700, -1);
501 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
502 acl->mode & 0070, -1);
504 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
505 acl->mode & 0007, -1);
510 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
511 r = archive_mstring_get_wcs(a, &ap->name, &wname);
514 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
518 append_entry_w(&wp, NULL, ap->tag, wname,
521 } else if (r < 0 && errno == ENOMEM)
529 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
530 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
531 prefix = L"default:";
537 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
538 r = archive_mstring_get_wcs(a, &ap->name, &wname);
542 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
546 append_entry_w(&wp, prefix, ap->tag,
547 wname, ap->permset, id);
549 } else if (r < 0 && errno == ENOMEM)
556 return (acl->acl_text_w);
561 append_id_w(wchar_t **wp, int id)
566 append_id_w(wp, id / 10);
567 *(*wp)++ = L"0123456789"[id % 10];
571 append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
572 const wchar_t *wname, int perm, int id)
574 if (prefix != NULL) {
579 case ARCHIVE_ENTRY_ACL_USER_OBJ:
583 case ARCHIVE_ENTRY_ACL_USER:
584 wcscpy(*wp, L"user");
586 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
590 case ARCHIVE_ENTRY_ACL_GROUP:
591 wcscpy(*wp, L"group");
593 case ARCHIVE_ENTRY_ACL_MASK:
594 wcscpy(*wp, L"mask");
598 case ARCHIVE_ENTRY_ACL_OTHER:
599 wcscpy(*wp, L"other");
609 } else if (tag == ARCHIVE_ENTRY_ACL_USER
610 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
615 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
616 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
617 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
626 archive_acl_text_l(struct archive_acl *acl, int flags,
627 const char **acl_text, size_t *acl_text_len,
628 struct archive_string_conv *sc)
635 struct archive_acl_entry *ap;
640 if (acl->acl_text != NULL) {
641 free (acl->acl_text);
642 acl->acl_text = NULL;
646 if (acl_text_len != NULL)
653 if ((ap->type & flags) != 0) {
655 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
656 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
657 length += 8; /* "default:" */
658 length += 5; /* tag name */
659 length += 1; /* colon */
660 r = archive_mstring_get_mbs_l(
661 &ap->name, &name, &len, sc);
664 if (len > 0 && name != NULL)
667 length += sizeof(uid_t) * 3 + 1;
668 length ++; /* colon */
669 length += 3; /* rwx */
670 length += 1; /* colon */
671 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
672 length ++; /* newline */
677 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
678 length += 10; /* "user::rwx\n" */
679 length += 11; /* "group::rwx\n" */
680 length += 11; /* "other::rwx\n" */
686 /* Now, allocate the string and actually populate it. */
687 p = acl->acl_text = (char *)malloc(length);
691 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
692 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
693 acl->mode & 0700, -1);
695 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
696 acl->mode & 0070, -1);
698 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
699 acl->mode & 0007, -1);
702 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
703 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
705 r = archive_mstring_get_mbs_l(
706 &ap->name, &name, &len, sc);
710 if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
715 append_entry(&p, NULL, ap->tag, name,
722 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
723 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
728 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
729 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
731 r = archive_mstring_get_mbs_l(
732 &ap->name, &name, &len, sc);
737 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
741 append_entry(&p, prefix, ap->tag,
742 name, ap->permset, id);
747 *acl_text = acl->acl_text;
748 if (acl_text_len != NULL)
749 *acl_text_len = strlen(acl->acl_text);
754 append_id(char **p, int id)
759 append_id(p, id / 10);
760 *(*p)++ = "0123456789"[id % 10];
764 append_entry(char **p, const char *prefix, int tag,
765 const char *name, int perm, int id)
767 if (prefix != NULL) {
772 case ARCHIVE_ENTRY_ACL_USER_OBJ:
776 case ARCHIVE_ENTRY_ACL_USER:
779 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
783 case ARCHIVE_ENTRY_ACL_GROUP:
786 case ARCHIVE_ENTRY_ACL_MASK:
791 case ARCHIVE_ENTRY_ACL_OTHER:
802 } else if (tag == ARCHIVE_ENTRY_ACL_USER
803 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
808 *(*p)++ = (perm & 0444) ? 'r' : '-';
809 *(*p)++ = (perm & 0222) ? 'w' : '-';
810 *(*p)++ = (perm & 0111) ? 'x' : '-';
819 * Parse a textual ACL. This automatically recognizes and supports
820 * extensions described above. The 'type' argument is used to
821 * indicate the type that should be used for any entries not
822 * explicitly marked as "default:".
825 archive_acl_parse_w(struct archive_acl *acl,
826 const wchar_t *text, int default_type)
829 const wchar_t *start;
834 int type, tag, permset, id;
837 while (text != NULL && *text != L'\0') {
839 * Parse the fields out of the next entry,
840 * advance 'text' to start of next entry.
844 const wchar_t *start, *end;
845 next_field_w(&text, &start, &end, &sep);
847 field[fields].start = start;
848 field[fields].end = end;
851 } while (sep == L':');
853 /* Set remaining fields to blank. */
854 for (n = fields; n < 4; ++n)
855 field[n].start = field[n].end = NULL;
857 /* Check for a numeric ID in field 1 or 3. */
859 isint_w(field[1].start, field[1].end, &id);
860 /* Field 3 is optional. */
861 if (id == -1 && fields > 3)
862 isint_w(field[3].start, field[3].end, &id);
865 * Solaris extension: "defaultuser::rwx" is the
866 * default ACL corresponding to "user::rwx", etc.
868 if (field[0].end - field[0].start > 7
869 && wmemcmp(field[0].start, L"default", 7) == 0) {
870 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
875 name.start = name.end = NULL;
876 if (prefix_w(field[0].start, field[0].end, L"user")) {
877 if (!ismode_w(field[2].start, field[2].end, &permset))
878 return (ARCHIVE_WARN);
879 if (id != -1 || field[1].start < field[1].end) {
880 tag = ARCHIVE_ENTRY_ACL_USER;
883 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
884 } else if (prefix_w(field[0].start, field[0].end, L"group")) {
885 if (!ismode_w(field[2].start, field[2].end, &permset))
886 return (ARCHIVE_WARN);
887 if (id != -1 || field[1].start < field[1].end) {
888 tag = ARCHIVE_ENTRY_ACL_GROUP;
891 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
892 } else if (prefix_w(field[0].start, field[0].end, L"other")) {
894 && field[1].start < field[1].end
895 && ismode_w(field[1].start, field[1].end, &permset)) {
896 /* This is Solaris-style "other:rwx" */
897 } else if (fields == 3
898 && field[1].start == field[1].end
899 && field[2].start < field[2].end
900 && ismode_w(field[2].start, field[2].end, &permset)) {
901 /* This is FreeBSD-style "other::rwx" */
903 return (ARCHIVE_WARN);
904 tag = ARCHIVE_ENTRY_ACL_OTHER;
905 } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
907 && field[1].start < field[1].end
908 && ismode_w(field[1].start, field[1].end, &permset)) {
909 /* This is Solaris-style "mask:rwx" */
910 } else if (fields == 3
911 && field[1].start == field[1].end
912 && field[2].start < field[2].end
913 && ismode_w(field[2].start, field[2].end, &permset)) {
914 /* This is FreeBSD-style "mask::rwx" */
916 return (ARCHIVE_WARN);
917 tag = ARCHIVE_ENTRY_ACL_MASK;
919 return (ARCHIVE_WARN);
921 /* Add entry to the internal list. */
922 archive_acl_add_entry_w_len(acl, type, permset,
923 tag, id, name.start, name.end - name.start);
929 * Parse a string to a positive decimal integer. Returns true if
930 * the string is non-empty and consists only of decimal digits,
934 isint_w(const wchar_t *start, const wchar_t *end, int *result)
939 while (start < end) {
940 if (*start < '0' || *start > '9')
942 if (n > (INT_MAX / 10) ||
943 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
956 * Parse a string as a mode field. Returns true if
957 * the string is non-empty and consists only of mode characters,
961 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
972 *permset |= ARCHIVE_ENTRY_ACL_READ;
975 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
978 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
990 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
991 * to point to just after the separator. *start points to the first
992 * character of the matched text and *end just after the last
993 * character of the matched identifier. In particular *end - *start
994 * is the length of the field body, not including leading or trailing
998 next_field_w(const wchar_t **wp, const wchar_t **start,
999 const wchar_t **end, wchar_t *sep)
1001 /* Skip leading whitespace to find start of field. */
1002 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1007 /* Scan for the separator. */
1008 while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1014 /* Trim trailing whitespace to locate end of field. */
1016 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1021 /* Adjust scanner location. */
1027 * Return true if the characters [start...end) are a prefix of 'test'.
1028 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1031 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1036 if (*start++ != *test++)
1039 while (start < end && *start++ == *test++)
1049 * Parse a textual ACL. This automatically recognizes and supports
1050 * extensions described above. The 'type' argument is used to
1051 * indicate the type that should be used for any entries not
1052 * explicitly marked as "default:".
1055 archive_acl_parse_l(struct archive_acl *acl,
1056 const char *text, int default_type, struct archive_string_conv *sc)
1063 int fields, n, r, ret = ARCHIVE_OK;
1064 int type, tag, permset, id;
1067 while (text != NULL && *text != '\0') {
1069 * Parse the fields out of the next entry,
1070 * advance 'text' to start of next entry.
1074 const char *start, *end;
1075 next_field(&text, &start, &end, &sep);
1077 field[fields].start = start;
1078 field[fields].end = end;
1081 } while (sep == ':');
1083 /* Set remaining fields to blank. */
1084 for (n = fields; n < 4; ++n)
1085 field[n].start = field[n].end = NULL;
1087 /* Check for a numeric ID in field 1 or 3. */
1089 isint(field[1].start, field[1].end, &id);
1090 /* Field 3 is optional. */
1091 if (id == -1 && fields > 3)
1092 isint(field[3].start, field[3].end, &id);
1095 * Solaris extension: "defaultuser::rwx" is the
1096 * default ACL corresponding to "user::rwx", etc.
1098 if (field[0].end - field[0].start > 7
1099 && memcmp(field[0].start, "default", 7) == 0) {
1100 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1101 field[0].start += 7;
1103 type = default_type;
1105 name.start = name.end = NULL;
1106 if (prefix_c(field[0].start, field[0].end, "user")) {
1107 if (!ismode(field[2].start, field[2].end, &permset))
1108 return (ARCHIVE_WARN);
1109 if (id != -1 || field[1].start < field[1].end) {
1110 tag = ARCHIVE_ENTRY_ACL_USER;
1113 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1114 } else if (prefix_c(field[0].start, field[0].end, "group")) {
1115 if (!ismode(field[2].start, field[2].end, &permset))
1116 return (ARCHIVE_WARN);
1117 if (id != -1 || field[1].start < field[1].end) {
1118 tag = ARCHIVE_ENTRY_ACL_GROUP;
1121 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1122 } else if (prefix_c(field[0].start, field[0].end, "other")) {
1124 && field[1].start < field[1].end
1125 && ismode(field[1].start, field[1].end, &permset)) {
1126 /* This is Solaris-style "other:rwx" */
1127 } else if (fields == 3
1128 && field[1].start == field[1].end
1129 && field[2].start < field[2].end
1130 && ismode(field[2].start, field[2].end, &permset)) {
1131 /* This is FreeBSD-style "other::rwx" */
1133 return (ARCHIVE_WARN);
1134 tag = ARCHIVE_ENTRY_ACL_OTHER;
1135 } else if (prefix_c(field[0].start, field[0].end, "mask")) {
1137 && field[1].start < field[1].end
1138 && ismode(field[1].start, field[1].end, &permset)) {
1139 /* This is Solaris-style "mask:rwx" */
1140 } else if (fields == 3
1141 && field[1].start == field[1].end
1142 && field[2].start < field[2].end
1143 && ismode(field[2].start, field[2].end, &permset)) {
1144 /* This is FreeBSD-style "mask::rwx" */
1146 return (ARCHIVE_WARN);
1147 tag = ARCHIVE_ENTRY_ACL_MASK;
1149 return (ARCHIVE_WARN);
1151 /* Add entry to the internal list. */
1152 r = archive_acl_add_entry_len_l(acl, type, permset,
1153 tag, id, name.start, name.end - name.start, sc);
1154 if (r < ARCHIVE_WARN)
1156 if (r != ARCHIVE_OK)
1163 * Parse a string to a positive decimal integer. Returns true if
1164 * the string is non-empty and consists only of decimal digits,
1168 isint(const char *start, const char *end, int *result)
1173 while (start < end) {
1174 if (*start < '0' || *start > '9')
1176 if (n > (INT_MAX / 10) ||
1177 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1190 * Parse a string as a mode field. Returns true if
1191 * the string is non-empty and consists only of mode characters,
1195 ismode(const char *start, const char *end, int *permset)
1206 *permset |= ARCHIVE_ENTRY_ACL_READ;
1209 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1212 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1224 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
1225 * to point to just after the separator. *start points to the first
1226 * character of the matched text and *end just after the last
1227 * character of the matched identifier. In particular *end - *start
1228 * is the length of the field body, not including leading or trailing
1232 next_field(const char **p, const char **start,
1233 const char **end, char *sep)
1235 /* Skip leading whitespace to find start of field. */
1236 while (**p == ' ' || **p == '\t' || **p == '\n') {
1241 /* Scan for the separator. */
1242 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1247 /* Trim trailing whitespace to locate end of field. */
1249 while (**end == ' ' || **end == '\t' || **end == '\n') {
1254 /* Adjust scanner location. */
1260 * Return true if the characters [start...end) are a prefix of 'test'.
1261 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1264 prefix_c(const char *start, const char *end, const char *test)
1269 if (*start++ != *test++)
1272 while (start < end && *start++ == *test++)