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) {
98 acl->acl_state = 0; /* Not counting. */
102 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
104 struct archive_acl_entry *ap, *ap2;
106 archive_acl_clear(dest);
108 dest->mode = src->mode;
111 ap2 = acl_new_entry(dest,
112 ap->type, ap->permset, ap->tag, ap->id);
114 archive_mstring_copy(&ap2->name, &ap->name);
120 archive_acl_add_entry(struct archive_acl *acl,
121 int type, int permset, int tag, int id, const char *name)
123 struct archive_acl_entry *ap;
125 if (acl_special(acl, type, permset, tag) == 0)
127 ap = acl_new_entry(acl, type, permset, tag, id);
130 return ARCHIVE_FAILED;
132 if (name != NULL && *name != '\0')
133 archive_mstring_copy_mbs(&ap->name, name);
135 archive_mstring_clean(&ap->name);
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)
143 struct archive_acl_entry *ap;
145 if (acl_special(acl, type, permset, tag) == 0)
147 ap = acl_new_entry(acl, type, permset, tag, id);
150 return ARCHIVE_FAILED;
152 if (name != NULL && *name != L'\0' && len > 0)
153 archive_mstring_copy_wcs_len(&ap->name, name, len);
155 archive_mstring_clean(&ap->name);
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)
164 struct archive_acl_entry *ap;
167 if (acl_special(acl, type, permset, tag) == 0)
169 ap = acl_new_entry(acl, type, permset, tag, id);
172 return ARCHIVE_FAILED;
174 if (name != NULL && *name != '\0' && len > 0) {
175 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
178 archive_mstring_clean(&ap->name);
182 else if (errno == ENOMEM)
183 return (ARCHIVE_FATAL);
185 return (ARCHIVE_WARN);
189 * If this ACL entry is part of the standard POSIX permissions set,
190 * store the permissions in the stat structure and return zero.
193 acl_special(struct archive_acl *acl, int type, int permset, int tag)
195 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
196 && ((permset & ~007) == 0)) {
198 case ARCHIVE_ENTRY_ACL_USER_OBJ:
200 acl->mode |= (permset & 7) << 6;
202 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
204 acl->mode |= (permset & 7) << 3;
206 case ARCHIVE_ENTRY_ACL_OTHER:
208 acl->mode |= permset & 7;
216 * Allocate and populate a new ACL entry with everything but the
219 static struct archive_acl_entry *
220 acl_new_entry(struct archive_acl *acl,
221 int type, int permset, int tag, int id)
223 struct archive_acl_entry *ap, *aq;
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) {
233 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
234 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
237 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
238 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
241 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
248 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
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 */
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) {
263 case ARCHIVE_ENTRY_ACL_EVERYONE:
264 /* Tags valid only in NFS4. */
265 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
270 /* No other values are valid. */
274 if (acl->acl_text_w != NULL) {
275 free(acl->acl_text_w);
276 acl->acl_text_w = NULL;
278 if (acl->acl_text != NULL) {
280 acl->acl_text = NULL;
284 * If there's a matching entry already in the list, overwrite it.
285 * NFSv4 entries may be repeated and are not overwritten.
287 * TODO: compare names of no id is provided (needs more rework)
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;
304 /* Add a new entry to the end of the list. */
305 ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
315 ap->permset = permset;
316 acl->acl_types |= type;
321 * Return a count of entries matching "want_type".
324 archive_acl_count(struct archive_acl *acl, int want_type)
327 struct archive_acl_entry *ap;
332 if ((ap->type & want_type) != 0)
337 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
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.
348 archive_acl_reset(struct archive_acl *acl, int want_type)
352 count = archive_acl_count(acl, want_type);
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.)
359 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
365 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
368 acl->acl_p = acl->acl_head;
374 * Return the next ACL entry in the list. Fake entries for the
375 * standard permissions and include them in the returned list.
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)
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).
389 if (acl->acl_state == 0)
390 return (ARCHIVE_WARN);
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;
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;
407 case ARCHIVE_ENTRY_ACL_OTHER:
408 *permset = acl->mode & 7;
409 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
410 *tag = ARCHIVE_ENTRY_ACL_OTHER;
412 acl->acl_p = acl->acl_head;
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) {
428 return (ARCHIVE_EOF); /* End of ACL entries. */
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) {
436 return (ARCHIVE_FATAL);
439 acl->acl_p = acl->acl_p->next;
444 * Generate a text version of the ACL. The flags parameter controls
445 * the style of the generated ACL.
448 archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
452 const wchar_t *wname;
453 const wchar_t *prefix;
455 struct archive_acl_entry *ap;
459 if (acl->acl_text_w != NULL) {
460 free (acl->acl_text_w);
461 acl->acl_text_w = NULL;
469 if ((ap->type & flags) != 0) {
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)
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 */
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" */
501 /* Now, allocate the string and actually populate it. */
502 wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
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);
510 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
511 acl->mode & 0070, -1);
513 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
514 acl->mode & 0007, -1);
519 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
520 r = archive_mstring_get_wcs(a, &ap->name, &wname);
523 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
527 append_entry_w(&wp, NULL, ap->tag, wname,
530 } else if (r < 0 && errno == ENOMEM)
538 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
539 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
540 prefix = L"default:";
546 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
547 r = archive_mstring_get_wcs(a, &ap->name, &wname);
551 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
555 append_entry_w(&wp, prefix, ap->tag,
556 wname, ap->permset, id);
558 } else if (r < 0 && errno == ENOMEM)
565 return (acl->acl_text_w);
570 append_id_w(wchar_t **wp, int id)
575 append_id_w(wp, id / 10);
576 *(*wp)++ = L"0123456789"[id % 10];
580 append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
581 const wchar_t *wname, int perm, int id)
583 if (prefix != NULL) {
588 case ARCHIVE_ENTRY_ACL_USER_OBJ:
592 case ARCHIVE_ENTRY_ACL_USER:
593 wcscpy(*wp, L"user");
595 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
599 case ARCHIVE_ENTRY_ACL_GROUP:
600 wcscpy(*wp, L"group");
602 case ARCHIVE_ENTRY_ACL_MASK:
603 wcscpy(*wp, L"mask");
607 case ARCHIVE_ENTRY_ACL_OTHER:
608 wcscpy(*wp, L"other");
618 } else if (tag == ARCHIVE_ENTRY_ACL_USER
619 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
624 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
625 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
626 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
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)
644 struct archive_acl_entry *ap;
649 if (acl->acl_text != NULL) {
650 free (acl->acl_text);
651 acl->acl_text = NULL;
655 if (acl_text_len != NULL)
662 if ((ap->type & flags) != 0) {
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);
673 if (len > 0 && name != NULL)
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 */
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" */
695 /* Now, allocate the string and actually populate it. */
696 p = acl->acl_text = (char *)malloc(length);
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);
704 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
705 acl->mode & 0070, -1);
707 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
708 acl->mode & 0007, -1);
711 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
712 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
714 r = archive_mstring_get_mbs_l(
715 &ap->name, &name, &len, sc);
719 if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
724 append_entry(&p, NULL, ap->tag, name,
731 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
732 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
737 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
738 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
740 r = archive_mstring_get_mbs_l(
741 &ap->name, &name, &len, sc);
746 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
750 append_entry(&p, prefix, ap->tag,
751 name, ap->permset, id);
756 *acl_text = acl->acl_text;
757 if (acl_text_len != NULL)
758 *acl_text_len = strlen(acl->acl_text);
763 append_id(char **p, int id)
768 append_id(p, id / 10);
769 *(*p)++ = "0123456789"[id % 10];
773 append_entry(char **p, const char *prefix, int tag,
774 const char *name, int perm, int id)
776 if (prefix != NULL) {
781 case ARCHIVE_ENTRY_ACL_USER_OBJ:
785 case ARCHIVE_ENTRY_ACL_USER:
788 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
792 case ARCHIVE_ENTRY_ACL_GROUP:
795 case ARCHIVE_ENTRY_ACL_MASK:
800 case ARCHIVE_ENTRY_ACL_OTHER:
811 } else if (tag == ARCHIVE_ENTRY_ACL_USER
812 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
817 *(*p)++ = (perm & 0444) ? 'r' : '-';
818 *(*p)++ = (perm & 0222) ? 'w' : '-';
819 *(*p)++ = (perm & 0111) ? 'x' : '-';
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:".
834 archive_acl_parse_w(struct archive_acl *acl,
835 const wchar_t *text, int default_type)
838 const wchar_t *start;
843 int type, tag, permset, id;
846 while (text != NULL && *text != L'\0') {
848 * Parse the fields out of the next entry,
849 * advance 'text' to start of next entry.
853 const wchar_t *start, *end;
854 next_field_w(&text, &start, &end, &sep);
856 field[fields].start = start;
857 field[fields].end = end;
860 } while (sep == L':');
862 /* Set remaining fields to blank. */
863 for (n = fields; n < 4; ++n)
864 field[n].start = field[n].end = NULL;
866 /* Check for a numeric ID in field 1 or 3. */
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);
874 * Solaris extension: "defaultuser::rwx" is the
875 * default ACL corresponding to "user::rwx", etc.
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;
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;
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;
900 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
901 } else if (prefix_w(field[0].start, field[0].end, L"other")) {
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" */
912 return (ARCHIVE_WARN);
913 tag = ARCHIVE_ENTRY_ACL_OTHER;
914 } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
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" */
925 return (ARCHIVE_WARN);
926 tag = ARCHIVE_ENTRY_ACL_MASK;
928 return (ARCHIVE_WARN);
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);
938 * Parse a string to a positive decimal integer. Returns true if
939 * the string is non-empty and consists only of decimal digits,
943 isint_w(const wchar_t *start, const wchar_t *end, int *result)
948 while (start < end) {
949 if (*start < '0' || *start > '9')
951 if (n > (INT_MAX / 10) ||
952 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
965 * Parse a string as a mode field. Returns true if
966 * the string is non-empty and consists only of mode characters,
970 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
981 *permset |= ARCHIVE_ENTRY_ACL_READ;
984 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
987 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
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
1007 next_field_w(const wchar_t **wp, const wchar_t **start,
1008 const wchar_t **end, wchar_t *sep)
1010 /* Skip leading whitespace to find start of field. */
1011 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1016 /* Scan for the separator. */
1017 while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1023 /* Trim trailing whitespace to locate end of field. */
1025 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1030 /* Adjust scanner location. */
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.
1040 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1045 if (*start++ != *test++)
1048 while (start < end && *start++ == *test++)
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:".
1064 archive_acl_parse_l(struct archive_acl *acl,
1065 const char *text, int default_type, struct archive_string_conv *sc)
1072 int fields, n, r, ret = ARCHIVE_OK;
1073 int type, tag, permset, id;
1076 while (text != NULL && *text != '\0') {
1078 * Parse the fields out of the next entry,
1079 * advance 'text' to start of next entry.
1083 const char *start, *end;
1084 next_field(&text, &start, &end, &sep);
1086 field[fields].start = start;
1087 field[fields].end = end;
1090 } while (sep == ':');
1092 /* Set remaining fields to blank. */
1093 for (n = fields; n < 4; ++n)
1094 field[n].start = field[n].end = NULL;
1096 /* Check for a numeric ID in field 1 or 3. */
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);
1104 * Solaris extension: "defaultuser::rwx" is the
1105 * default ACL corresponding to "user::rwx", etc.
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;
1112 type = default_type;
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;
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;
1130 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1131 } else if (prefix_c(field[0].start, field[0].end, "other")) {
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" */
1142 return (ARCHIVE_WARN);
1143 tag = ARCHIVE_ENTRY_ACL_OTHER;
1144 } else if (prefix_c(field[0].start, field[0].end, "mask")) {
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" */
1155 return (ARCHIVE_WARN);
1156 tag = ARCHIVE_ENTRY_ACL_MASK;
1158 return (ARCHIVE_WARN);
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)
1165 if (r != ARCHIVE_OK)
1172 * Parse a string to a positive decimal integer. Returns true if
1173 * the string is non-empty and consists only of decimal digits,
1177 isint(const char *start, const char *end, int *result)
1182 while (start < end) {
1183 if (*start < '0' || *start > '9')
1185 if (n > (INT_MAX / 10) ||
1186 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1199 * Parse a string as a mode field. Returns true if
1200 * the string is non-empty and consists only of mode characters,
1204 ismode(const char *start, const char *end, int *permset)
1215 *permset |= ARCHIVE_ENTRY_ACL_READ;
1218 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1221 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
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
1241 next_field(const char **p, const char **start,
1242 const char **end, char *sep)
1244 /* Skip leading whitespace to find start of field. */
1245 while (**p == ' ' || **p == '\t' || **p == '\n') {
1250 /* Scan for the separator. */
1251 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1256 /* Trim trailing whitespace to locate end of field. */
1258 while (**end == ' ' || **end == '\t' || **end == '\n') {
1263 /* Adjust scanner location. */
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.
1273 prefix_c(const char *start, const char *end, const char *test)
1278 if (*start++ != *test++)
1281 while (start < end && *start++ == *test++)