2 * Copyright (c) 2003-2010 Tim Kientzle
3 * Copyright (c) 2016 Martin Matuska
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "archive_platform.h"
28 __FBSDID("$FreeBSD$");
40 #include "archive_acl_private.h"
41 #include "archive_entry.h"
42 #include "archive_private.h"
45 #define max(a, b) ((a)>(b)?(a):(b))
48 /* Good enough for simple equality testing, but not for sorting. */
49 #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
52 static int acl_special(struct archive_acl *acl,
53 int type, int permset, int tag);
54 static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
55 int type, int permset, int tag, int id);
56 static int archive_acl_add_entry_len_l(struct archive_acl *acl,
57 int type, int permset, int tag, int id, const char *name,
58 size_t len, struct archive_string_conv *sc);
59 static int archive_acl_text_want_type(struct archive_acl *acl, int flags);
60 static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type,
61 int flags, int wide, struct archive *a,
62 struct archive_string_conv *sc);
63 static int isint_w(const wchar_t *start, const wchar_t *end, int *result);
64 static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);
65 static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
67 static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
69 static void next_field_w(const wchar_t **wp, const wchar_t **start,
70 const wchar_t **end, wchar_t *sep);
71 static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
72 int tag, int flags, const wchar_t *wname, int perm, int id);
73 static void append_id_w(wchar_t **wp, int id);
74 static int isint(const char *start, const char *end, int *result);
75 static int ismode(const char *start, const char *end, int *result);
76 static int is_nfs4_flags(const char *start, const char *end,
78 static int is_nfs4_perms(const char *start, const char *end,
80 static void next_field(const char **p, const char **start,
81 const char **end, char *sep);
82 static void append_entry(char **p, const char *prefix, int type,
83 int tag, int flags, const char *name, int perm, int id);
84 static void append_id(char **p, int id);
90 } nfsv4_acl_perm_map[] = {
91 { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
93 { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
95 { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
96 { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
98 { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
99 { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
100 { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
101 { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
102 { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
103 { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
104 { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
105 { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
106 { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
107 { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
110 static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
111 sizeof(nfsv4_acl_perm_map[0]));
113 static const struct {
117 } nfsv4_acl_flag_map[] = {
118 { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
119 { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
120 { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
121 { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
122 { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
123 { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
124 { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
127 static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
128 sizeof(nfsv4_acl_flag_map[0]));
131 archive_acl_clear(struct archive_acl *acl)
133 struct archive_acl_entry *ap;
135 while (acl->acl_head != NULL) {
136 ap = acl->acl_head->next;
137 archive_mstring_clean(&acl->acl_head->name);
141 free(acl->acl_text_w);
142 acl->acl_text_w = NULL;
144 acl->acl_text = NULL;
147 acl->acl_state = 0; /* Not counting. */
151 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
153 struct archive_acl_entry *ap, *ap2;
155 archive_acl_clear(dest);
157 dest->mode = src->mode;
160 ap2 = acl_new_entry(dest,
161 ap->type, ap->permset, ap->tag, ap->id);
163 archive_mstring_copy(&ap2->name, &ap->name);
169 archive_acl_add_entry(struct archive_acl *acl,
170 int type, int permset, int tag, int id, const char *name)
172 struct archive_acl_entry *ap;
174 if (acl_special(acl, type, permset, tag) == 0)
176 ap = acl_new_entry(acl, type, permset, tag, id);
179 return ARCHIVE_FAILED;
181 if (name != NULL && *name != '\0')
182 archive_mstring_copy_mbs(&ap->name, name);
184 archive_mstring_clean(&ap->name);
189 archive_acl_add_entry_w_len(struct archive_acl *acl,
190 int type, int permset, int tag, int id, const wchar_t *name, size_t len)
192 struct archive_acl_entry *ap;
194 if (acl_special(acl, type, permset, tag) == 0)
196 ap = acl_new_entry(acl, type, permset, tag, id);
199 return ARCHIVE_FAILED;
201 if (name != NULL && *name != L'\0' && len > 0)
202 archive_mstring_copy_wcs_len(&ap->name, name, len);
204 archive_mstring_clean(&ap->name);
209 archive_acl_add_entry_len_l(struct archive_acl *acl,
210 int type, int permset, int tag, int id, const char *name, size_t len,
211 struct archive_string_conv *sc)
213 struct archive_acl_entry *ap;
216 if (acl_special(acl, type, permset, tag) == 0)
218 ap = acl_new_entry(acl, type, permset, tag, id);
221 return ARCHIVE_FAILED;
223 if (name != NULL && *name != '\0' && len > 0) {
224 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
227 archive_mstring_clean(&ap->name);
231 else if (errno == ENOMEM)
232 return (ARCHIVE_FATAL);
234 return (ARCHIVE_WARN);
238 * If this ACL entry is part of the standard POSIX permissions set,
239 * store the permissions in the stat structure and return zero.
242 acl_special(struct archive_acl *acl, int type, int permset, int tag)
244 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
245 && ((permset & ~007) == 0)) {
247 case ARCHIVE_ENTRY_ACL_USER_OBJ:
249 acl->mode |= (permset & 7) << 6;
251 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
253 acl->mode |= (permset & 7) << 3;
255 case ARCHIVE_ENTRY_ACL_OTHER:
257 acl->mode |= permset & 7;
265 * Allocate and populate a new ACL entry with everything but the
268 static struct archive_acl_entry *
269 acl_new_entry(struct archive_acl *acl,
270 int type, int permset, int tag, int id)
272 struct archive_acl_entry *ap, *aq;
274 /* Type argument must be a valid NFS4 or POSIX.1e type.
275 * The type must agree with anything already set and
276 * the permset must be compatible. */
277 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
278 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
282 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
283 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
286 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
287 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
290 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
297 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
299 case ARCHIVE_ENTRY_ACL_USER:
300 case ARCHIVE_ENTRY_ACL_USER_OBJ:
301 case ARCHIVE_ENTRY_ACL_GROUP:
302 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
303 /* Tags valid in both NFS4 and POSIX.1e */
305 case ARCHIVE_ENTRY_ACL_MASK:
306 case ARCHIVE_ENTRY_ACL_OTHER:
307 /* Tags valid only in POSIX.1e. */
308 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
312 case ARCHIVE_ENTRY_ACL_EVERYONE:
313 /* Tags valid only in NFS4. */
314 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
319 /* No other values are valid. */
323 free(acl->acl_text_w);
324 acl->acl_text_w = NULL;
326 acl->acl_text = NULL;
329 * If there's a matching entry already in the list, overwrite it.
330 * NFSv4 entries may be repeated and are not overwritten.
332 * TODO: compare names of no id is provided (needs more rework)
337 if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
338 ap->type == type && ap->tag == tag && ap->id == id) {
339 if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
340 tag != ARCHIVE_ENTRY_ACL_GROUP)) {
341 ap->permset = permset;
349 /* Add a new entry to the end of the list. */
350 ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
360 ap->permset = permset;
361 acl->acl_types |= type;
366 * Return a count of entries matching "want_type".
369 archive_acl_count(struct archive_acl *acl, int want_type)
372 struct archive_acl_entry *ap;
377 if ((ap->type & want_type) != 0)
382 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
388 * Return a bitmask of stored ACL types in an ACL list
391 archive_acl_types(struct archive_acl *acl)
393 return (acl->acl_types);
397 * Prepare for reading entries from the ACL data. Returns a count
398 * of entries matching "want_type", or zero if there are no
399 * non-extended ACL entries of that type.
402 archive_acl_reset(struct archive_acl *acl, int want_type)
406 count = archive_acl_count(acl, want_type);
409 * If the only entries are the three standard ones,
410 * then don't return any ACL data. (In this case,
411 * client can just use chmod(2) to set permissions.)
413 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
419 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
422 acl->acl_p = acl->acl_head;
428 * Return the next ACL entry in the list. Fake entries for the
429 * standard permissions and include them in the returned list.
432 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
433 int *type, int *permset, int *tag, int *id, const char **name)
439 * The acl_state is either zero (no entries available), -1
440 * (reading from list), or an entry type (retrieve that type
441 * from ae_stat.aest_mode).
443 if (acl->acl_state == 0)
444 return (ARCHIVE_WARN);
446 /* The first three access entries are special. */
447 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
448 switch (acl->acl_state) {
449 case ARCHIVE_ENTRY_ACL_USER_OBJ:
450 *permset = (acl->mode >> 6) & 7;
451 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
452 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
453 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
455 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
456 *permset = (acl->mode >> 3) & 7;
457 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
458 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
459 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
461 case ARCHIVE_ENTRY_ACL_OTHER:
462 *permset = acl->mode & 7;
463 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
464 *tag = ARCHIVE_ENTRY_ACL_OTHER;
466 acl->acl_p = acl->acl_head;
473 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
474 acl->acl_p = acl->acl_p->next;
475 if (acl->acl_p == NULL) {
482 return (ARCHIVE_EOF); /* End of ACL entries. */
484 *type = acl->acl_p->type;
485 *permset = acl->acl_p->permset;
486 *tag = acl->acl_p->tag;
487 *id = acl->acl_p->id;
488 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
490 return (ARCHIVE_FATAL);
493 acl->acl_p = acl->acl_p->next;
498 * Determine what type of ACL do we want
501 archive_acl_text_want_type(struct archive_acl *acl, int flags)
505 /* Check if ACL is NFSv4 */
506 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
507 /* NFSv4 should never mix with POSIX.1e */
508 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
511 return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
514 /* Now deal with POSIX.1e ACLs */
517 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
518 want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
519 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
520 want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
522 /* By default we want both access and default ACLs */
524 return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
530 * Calculate ACL text string length
533 archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
534 int wide, struct archive *a, struct archive_string_conv *sc) {
535 struct archive_acl_entry *ap;
537 const wchar_t *wname;
538 int count, idlen, tmp, r;
544 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
545 if ((ap->type & want_type) == 0)
548 * Filemode-mapping ACL entries are stored exclusively in
549 * ap->mode so they should not be in the list
551 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
552 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
553 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
554 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
557 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
558 && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
559 length += 8; /* "default:" */
561 case ARCHIVE_ENTRY_ACL_USER_OBJ:
562 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
563 length += 6; /* "owner@" */
567 case ARCHIVE_ENTRY_ACL_USER:
568 case ARCHIVE_ENTRY_ACL_MASK:
569 length += 4; /* "user", "mask" */
571 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
572 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
573 length += 6; /* "group@" */
577 case ARCHIVE_ENTRY_ACL_GROUP:
578 case ARCHIVE_ENTRY_ACL_OTHER:
579 length += 5; /* "group", "other" */
581 case ARCHIVE_ENTRY_ACL_EVERYONE:
582 length += 9; /* "everyone@" */
585 length += 1; /* colon after tag */
586 if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
587 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
589 r = archive_mstring_get_wcs(a, &ap->name,
591 if (r == 0 && wname != NULL)
592 length += wcslen(wname);
593 else if (r < 0 && errno == ENOMEM)
596 length += sizeof(uid_t) * 3 + 1;
598 r = archive_mstring_get_mbs_l(a, &ap->name, &name,
602 if (len > 0 && name != NULL)
605 length += sizeof(uid_t) * 3 + 1;
607 length += 1; /* colon after user or group name */
608 } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
609 length += 1; /* 2nd colon empty user,group or other */
611 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
612 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
613 && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
614 || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
615 /* Solaris has no colon after other: and mask: */
619 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
620 /* rwxpdDaARWcCos:fdinSFI:deny */
622 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
623 length += 1; /* allow, alarm, audit */
625 length += 3; /* rwx */
627 if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
628 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
629 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
630 length += 1; /* colon */
640 length ++; /* entry separator */
643 /* Add filemode-mapping access entries to the length */
644 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
645 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
646 /* "user::rwx\ngroup::rwx\nother:rwx\n" */
649 /* "user::rwx\ngroup::rwx\nother::rwx\n" */
652 } else if (count == 0)
655 /* The terminating character is included in count */
660 * Generate a wide text version of the ACL. The flags parameter controls
661 * the type and style of the generated ACL.
664 archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
670 const wchar_t *wname;
671 const wchar_t *prefix;
673 struct archive_acl_entry *ap;
674 int id, r, want_type;
677 want_type = archive_acl_text_want_type(acl, flags);
679 /* Both NFSv4 and POSIX.1 types found */
683 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
684 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
686 length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
691 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
696 /* Now, allocate the string and actually populate it. */
697 wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
700 __archive_errx(1, "No memory");
705 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
706 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
707 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
708 acl->mode & 0700, -1);
710 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
711 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
712 acl->mode & 0070, -1);
714 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
715 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
716 acl->mode & 0007, -1);
720 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
721 if ((ap->type & want_type) == 0)
724 * Filemode-mapping ACL entries are stored exclusively in
725 * ap->mode so they should not be in the list
727 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
728 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
729 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
730 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
732 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
733 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
734 prefix = L"default:";
737 r = archive_mstring_get_wcs(a, &ap->name, &wname);
741 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
745 append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
746 wname, ap->permset, id);
748 } else if (r < 0 && errno == ENOMEM)
752 /* Add terminating character */
757 if ((ssize_t)len > (length - 1))
758 __archive_errx(1, "Buffer overrun");
760 if (text_len != NULL)
767 append_id_w(wchar_t **wp, int id)
772 append_id_w(wp, id / 10);
773 *(*wp)++ = L"0123456789"[id % 10];
777 append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
778 int tag, int flags, const wchar_t *wname, int perm, int id)
782 if (prefix != NULL) {
787 case ARCHIVE_ENTRY_ACL_USER_OBJ:
790 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
791 wcscpy(*wp, L"owner@");
795 case ARCHIVE_ENTRY_ACL_USER:
796 wcscpy(*wp, L"user");
798 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
801 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
802 wcscpy(*wp, L"group@");
806 case ARCHIVE_ENTRY_ACL_GROUP:
807 wcscpy(*wp, L"group");
809 case ARCHIVE_ENTRY_ACL_MASK:
810 wcscpy(*wp, L"mask");
814 case ARCHIVE_ENTRY_ACL_OTHER:
815 wcscpy(*wp, L"other");
819 case ARCHIVE_ENTRY_ACL_EVERYONE:
820 wcscpy(*wp, L"everyone@");
827 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
828 tag == ARCHIVE_ENTRY_ACL_USER ||
829 tag == ARCHIVE_ENTRY_ACL_GROUP) {
833 } else if (tag == ARCHIVE_ENTRY_ACL_USER
834 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
836 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
839 /* Solaris style has no second colon after other and mask */
840 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
841 || (tag != ARCHIVE_ENTRY_ACL_OTHER
842 && tag != ARCHIVE_ENTRY_ACL_MASK))
845 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
846 /* POSIX.1e ACL perms */
847 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
848 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
849 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
851 /* NFSv4 ACL perms */
852 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
853 if (perm & nfsv4_acl_perm_map[i].perm)
854 *(*wp)++ = nfsv4_acl_perm_map[i].wc;
855 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
859 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
860 if (perm & nfsv4_acl_flag_map[i].perm)
861 *(*wp)++ = nfsv4_acl_flag_map[i].wc;
862 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
867 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
868 wcscpy(*wp, L"allow");
870 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
871 wcscpy(*wp, L"deny");
873 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
874 wcscpy(*wp, L"audit");
876 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
877 wcscpy(*wp, L"alarm");
891 * Generate a text version of the ACL. The flags parameter controls
892 * the type and style of the generated ACL.
895 archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
896 struct archive_string_conv *sc)
904 struct archive_acl_entry *ap;
905 int id, r, want_type;
908 want_type = archive_acl_text_want_type(acl, flags);
910 /* Both NFSv4 and POSIX.1 types found */
914 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
915 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
917 length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
922 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
927 /* Now, allocate the string and actually populate it. */
928 p = s = (char *)malloc(length * sizeof(char));
931 __archive_errx(1, "No memory");
936 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
937 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
938 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
939 acl->mode & 0700, -1);
941 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
942 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
943 acl->mode & 0070, -1);
945 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
946 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
947 acl->mode & 0007, -1);
951 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
952 if ((ap->type & want_type) == 0)
955 * Filemode-mapping ACL entries are stored exclusively in
956 * ap->mode so they should not be in the list
958 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
959 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
960 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
961 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
963 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
964 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
968 r = archive_mstring_get_mbs_l(
969 NULL, &ap->name, &name, &len, sc);
975 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
980 append_entry(&p, prefix, ap->type, ap->tag, flags, name,
985 /* Add terminating character */
990 if ((ssize_t)len > (length - 1))
991 __archive_errx(1, "Buffer overrun");
993 if (text_len != NULL)
1000 append_id(char **p, int id)
1005 append_id(p, id / 10);
1006 *(*p)++ = "0123456789"[id % 10];
1010 append_entry(char **p, const char *prefix, int type,
1011 int tag, int flags, const char *name, int perm, int id)
1015 if (prefix != NULL) {
1020 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1023 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1024 strcpy(*p, "owner@");
1028 case ARCHIVE_ENTRY_ACL_USER:
1031 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1034 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1035 strcpy(*p, "group@");
1039 case ARCHIVE_ENTRY_ACL_GROUP:
1040 strcpy(*p, "group");
1042 case ARCHIVE_ENTRY_ACL_MASK:
1047 case ARCHIVE_ENTRY_ACL_OTHER:
1048 strcpy(*p, "other");
1052 case ARCHIVE_ENTRY_ACL_EVERYONE:
1053 strcpy(*p, "everyone@");
1060 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1061 tag == ARCHIVE_ENTRY_ACL_USER ||
1062 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1066 } else if (tag == ARCHIVE_ENTRY_ACL_USER
1067 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1069 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1072 /* Solaris style has no second colon after other and mask */
1073 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1074 || (tag != ARCHIVE_ENTRY_ACL_OTHER
1075 && tag != ARCHIVE_ENTRY_ACL_MASK))
1078 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1079 /* POSIX.1e ACL perms */
1080 *(*p)++ = (perm & 0444) ? 'r' : '-';
1081 *(*p)++ = (perm & 0222) ? 'w' : '-';
1082 *(*p)++ = (perm & 0111) ? 'x' : '-';
1084 /* NFSv4 ACL perms */
1085 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1086 if (perm & nfsv4_acl_perm_map[i].perm)
1087 *(*p)++ = nfsv4_acl_perm_map[i].c;
1088 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1092 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1093 if (perm & nfsv4_acl_flag_map[i].perm)
1094 *(*p)++ = nfsv4_acl_flag_map[i].c;
1095 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1100 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1101 strcpy(*p, "allow");
1103 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1106 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1107 strcpy(*p, "audit");
1109 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1110 strcpy(*p, "alarm");
1122 * Parse a wide ACL text string.
1124 * The want_type argument may be one of the following:
1125 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1126 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1127 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1129 * POSIX.1e ACL entries prefixed with "default:" are treated as
1130 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1133 archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1137 const wchar_t *start;
1141 const wchar_t *s, *st;
1143 int numfields, fields, n, r, sol, ret;
1144 int type, types, tag, permset, id;
1151 switch (want_type) {
1152 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1153 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1155 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1156 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1159 case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1163 return (ARCHIVE_FATAL);
1166 while (text != NULL && *text != L'\0') {
1168 * Parse the fields out of the next entry,
1169 * advance 'text' to start of next entry.
1173 const wchar_t *start, *end;
1174 next_field_w(&text, &start, &end, &sep);
1175 if (fields < numfields) {
1176 field[fields].start = start;
1177 field[fields].end = end;
1180 } while (sep == L':');
1182 /* Set remaining fields to blank. */
1183 for (n = fields; n < numfields; ++n)
1184 field[n].start = field[n].end = NULL;
1186 if (field[0].start != NULL && *(field[0].start) == L'#') {
1187 /* Comment, skip entry */
1195 name.start = name.end = NULL;
1197 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1200 * Default keyword "default:user::rwx"
1201 * if found, we have one more field
1203 * We also support old Solaris extension:
1204 * "defaultuser::rwx" is the default ACL corresponding
1205 * to "user::rwx", etc. valid only for first field
1208 len = field[0].end - field[0].start;
1209 if (*s == L'd' && (len == 1 || (len >= 7
1210 && wmemcmp((s + 1), L"efault", 6) == 0))) {
1211 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1213 field[0].start += 7;
1219 /* Check for a numeric ID in field n+1 or n+3. */
1220 isint_w(field[n + 1].start, field[n + 1].end, &id);
1221 /* Field n+3 is optional. */
1222 if (id == -1 && fields > n+3)
1223 isint_w(field[n + 3].start, field[n + 3].end,
1228 st = field[n].start + 1;
1229 len = field[n].end - field[n].start;
1233 if (len == 1 || (len == 4
1234 && wmemcmp(st, L"ser", 3) == 0))
1235 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1238 if (len == 1 || (len == 5
1239 && wmemcmp(st, L"roup", 4) == 0))
1240 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1243 if (len == 1 || (len == 5
1244 && wmemcmp(st, L"ther", 4) == 0))
1245 tag = ARCHIVE_ENTRY_ACL_OTHER;
1248 if (len == 1 || (len == 4
1249 && wmemcmp(st, L"ask", 3) == 0))
1250 tag = ARCHIVE_ENTRY_ACL_MASK;
1257 case ARCHIVE_ENTRY_ACL_OTHER:
1258 case ARCHIVE_ENTRY_ACL_MASK:
1259 if (fields == (n + 2)
1260 && field[n + 1].start < field[n + 1].end
1261 && ismode_w(field[n + 1].start,
1262 field[n + 1].end, &permset)) {
1263 /* This is Solaris-style "other:rwx" */
1265 } else if (fields == (n + 3) &&
1266 field[n + 1].start < field[n + 1].end) {
1267 /* Invalid mask or other field */
1272 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1273 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1275 field[n + 1].start < field[n + 1].end) {
1276 name = field[n + 1];
1277 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1278 tag = ARCHIVE_ENTRY_ACL_USER;
1280 tag = ARCHIVE_ENTRY_ACL_GROUP;
1284 /* Invalid tag, skip entry */
1290 * Without "default:" we expect mode in field 2
1291 * Exception: Solaris other and mask fields
1293 if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1294 field[n + 2 - sol].end, &permset)) {
1295 /* Invalid mode, skip entry */
1302 len = field[0].end - field[0].start;
1307 if (wmemcmp(s, L"user", 4) == 0)
1308 tag = ARCHIVE_ENTRY_ACL_USER;
1311 if (wmemcmp(s, L"group", 5) == 0)
1312 tag = ARCHIVE_ENTRY_ACL_GROUP;
1315 if (wmemcmp(s, L"owner@", 6) == 0)
1316 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1317 else if (wmemcmp(s, L"group@", len) == 0)
1318 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1321 if (wmemcmp(s, L"everyone@", 9) == 0)
1322 tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1328 /* Invalid tag, skip entry */
1331 } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1332 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1335 isint_w(name.start, name.end, &id);
1339 if (!is_nfs4_perms_w(field[1 + n].start,
1340 field[1 + n].end, &permset)) {
1341 /* Invalid NFSv4 perms, skip entry */
1345 if (!is_nfs4_flags_w(field[2 + n].start,
1346 field[2 + n].end, &permset)) {
1347 /* Invalid NFSv4 flags, skip entry */
1351 s = field[3 + n].start;
1352 len = field[3 + n].end - field[3 + n].start;
1355 if (wmemcmp(s, L"deny", 4) == 0)
1356 type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1357 } else if (len == 5) {
1358 if (wmemcmp(s, L"allow", 5) == 0)
1359 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1360 else if (wmemcmp(s, L"audit", 5) == 0)
1361 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1362 else if (wmemcmp(s, L"alarm", 5) == 0)
1363 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1366 /* Invalid entry type, skip entry */
1370 isint_w(field[4 + n].start, field[4 + n].end, &id);
1373 /* Add entry to the internal list. */
1374 r = archive_acl_add_entry_w_len(acl, type, permset,
1375 tag, id, name.start, name.end - name.start);
1376 if (r < ARCHIVE_WARN)
1378 if (r != ARCHIVE_OK)
1384 archive_acl_reset(acl, types);
1390 * Parse a string to a positive decimal integer. Returns true if
1391 * the string is non-empty and consists only of decimal digits,
1395 isint_w(const wchar_t *start, const wchar_t *end, int *result)
1400 while (start < end) {
1401 if (*start < L'0' || *start > L'9')
1403 if (n > (INT_MAX / 10) ||
1404 (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
1417 * Parse a string as a mode field. Returns true if
1418 * the string is non-empty and consists only of mode characters,
1422 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1432 case L'r': case L'R':
1433 *permset |= ARCHIVE_ENTRY_ACL_READ;
1435 case L'w': case L'W':
1436 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1438 case L'x': case L'X':
1439 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1451 * Parse a string as a NFS4 ACL permission field.
1452 * Returns true if the string is non-empty and consists only of NFS4 ACL
1453 * permission characters, false otherwise
1456 is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1458 const wchar_t *p = start;
1463 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1466 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1469 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1472 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1475 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1478 *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1481 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1484 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1487 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1490 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1493 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1496 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1499 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1502 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1514 * Parse a string as a NFS4 ACL flags field.
1515 * Returns true if the string is non-empty and consists only of NFS4 ACL
1516 * flag characters, false otherwise
1519 is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1521 const wchar_t *p = start;
1526 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1529 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1532 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1536 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1539 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1542 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1545 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1557 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
1558 * to point to just after the separator. *start points to the first
1559 * character of the matched text and *end just after the last
1560 * character of the matched identifier. In particular *end - *start
1561 * is the length of the field body, not including leading or trailing
1565 next_field_w(const wchar_t **wp, const wchar_t **start,
1566 const wchar_t **end, wchar_t *sep)
1568 /* Skip leading whitespace to find start of field. */
1569 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1574 /* Scan for the separator. */
1575 while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1581 /* Trim trailing whitespace to locate end of field. */
1583 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1588 /* Adjust scanner location. */
1594 * Parse an ACL text string.
1596 * The want_type argument may be one of the following:
1597 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1598 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1599 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1601 * POSIX.1e ACL entries prefixed with "default:" are treated as
1602 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1605 archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1606 int want_type, struct archive_string_conv *sc)
1614 int numfields, fields, n, r, sol, ret;
1615 int type, types, tag, permset, id;
1619 switch (want_type) {
1620 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1621 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1623 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1624 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1627 case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1631 return (ARCHIVE_FATAL);
1637 while (text != NULL && *text != '\0') {
1639 * Parse the fields out of the next entry,
1640 * advance 'text' to start of next entry.
1644 const char *start, *end;
1645 next_field(&text, &start, &end, &sep);
1646 if (fields < numfields) {
1647 field[fields].start = start;
1648 field[fields].end = end;
1651 } while (sep == ':');
1653 /* Set remaining fields to blank. */
1654 for (n = fields; n < numfields; ++n)
1655 field[n].start = field[n].end = NULL;
1657 if (field[0].start != NULL && *(field[0].start) == '#') {
1658 /* Comment, skip entry */
1666 name.start = name.end = NULL;
1668 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1671 * Default keyword "default:user::rwx"
1672 * if found, we have one more field
1674 * We also support old Solaris extension:
1675 * "defaultuser::rwx" is the default ACL corresponding
1676 * to "user::rwx", etc. valid only for first field
1679 len = field[0].end - field[0].start;
1680 if (*s == 'd' && (len == 1 || (len >= 7
1681 && memcmp((s + 1), "efault", 6) == 0))) {
1682 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1684 field[0].start += 7;
1690 /* Check for a numeric ID in field n+1 or n+3. */
1691 isint(field[n + 1].start, field[n + 1].end, &id);
1692 /* Field n+3 is optional. */
1693 if (id == -1 && fields > (n + 3))
1694 isint(field[n + 3].start, field[n + 3].end,
1699 st = field[n].start + 1;
1700 len = field[n].end - field[n].start;
1704 if (len == 1 || (len == 4
1705 && memcmp(st, "ser", 3) == 0))
1706 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1709 if (len == 1 || (len == 5
1710 && memcmp(st, "roup", 4) == 0))
1711 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1714 if (len == 1 || (len == 5
1715 && memcmp(st, "ther", 4) == 0))
1716 tag = ARCHIVE_ENTRY_ACL_OTHER;
1719 if (len == 1 || (len == 4
1720 && memcmp(st, "ask", 3) == 0))
1721 tag = ARCHIVE_ENTRY_ACL_MASK;
1728 case ARCHIVE_ENTRY_ACL_OTHER:
1729 case ARCHIVE_ENTRY_ACL_MASK:
1730 if (fields == (n + 2)
1731 && field[n + 1].start < field[n + 1].end
1732 && ismode(field[n + 1].start,
1733 field[n + 1].end, &permset)) {
1734 /* This is Solaris-style "other:rwx" */
1736 } else if (fields == (n + 3) &&
1737 field[n + 1].start < field[n + 1].end) {
1738 /* Invalid mask or other field */
1743 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1744 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1746 field[n + 1].start < field[n + 1].end) {
1747 name = field[n + 1];
1748 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1749 tag = ARCHIVE_ENTRY_ACL_USER;
1751 tag = ARCHIVE_ENTRY_ACL_GROUP;
1755 /* Invalid tag, skip entry */
1761 * Without "default:" we expect mode in field 3
1762 * Exception: Solaris other and mask fields
1764 if (permset == 0 && !ismode(field[n + 2 - sol].start,
1765 field[n + 2 - sol].end, &permset)) {
1766 /* Invalid mode, skip entry */
1773 len = field[0].end - field[0].start;
1778 if (memcmp(s, "user", 4) == 0)
1779 tag = ARCHIVE_ENTRY_ACL_USER;
1782 if (memcmp(s, "group", 5) == 0)
1783 tag = ARCHIVE_ENTRY_ACL_GROUP;
1786 if (memcmp(s, "owner@", 6) == 0)
1787 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1788 else if (memcmp(s, "group@", 6) == 0)
1789 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1792 if (memcmp(s, "everyone@", 9) == 0)
1793 tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1800 /* Invalid tag, skip entry */
1803 } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1804 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1807 isint(name.start, name.end, &id);
1811 if (!is_nfs4_perms(field[1 + n].start,
1812 field[1 + n].end, &permset)) {
1813 /* Invalid NFSv4 perms, skip entry */
1817 if (!is_nfs4_flags(field[2 + n].start,
1818 field[2 + n].end, &permset)) {
1819 /* Invalid NFSv4 flags, skip entry */
1823 s = field[3 + n].start;
1824 len = field[3 + n].end - field[3 + n].start;
1827 if (memcmp(s, "deny", 4) == 0)
1828 type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1829 } else if (len == 5) {
1830 if (memcmp(s, "allow", 5) == 0)
1831 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1832 else if (memcmp(s, "audit", 5) == 0)
1833 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1834 else if (memcmp(s, "alarm", 5) == 0)
1835 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1838 /* Invalid entry type, skip entry */
1842 isint(field[4 + n].start, field[4 + n].end,
1846 /* Add entry to the internal list. */
1847 r = archive_acl_add_entry_len_l(acl, type, permset,
1848 tag, id, name.start, name.end - name.start, sc);
1849 if (r < ARCHIVE_WARN)
1851 if (r != ARCHIVE_OK)
1857 archive_acl_reset(acl, types);
1863 * Parse a string to a positive decimal integer. Returns true if
1864 * the string is non-empty and consists only of decimal digits,
1868 isint(const char *start, const char *end, int *result)
1873 while (start < end) {
1874 if (*start < '0' || *start > '9')
1876 if (n > (INT_MAX / 10) ||
1877 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1890 * Parse a string as a mode field. Returns true if
1891 * the string is non-empty and consists only of mode characters,
1895 ismode(const char *start, const char *end, int *permset)
1906 *permset |= ARCHIVE_ENTRY_ACL_READ;
1909 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1912 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1924 * Parse a string as a NFS4 ACL permission field.
1925 * Returns true if the string is non-empty and consists only of NFS4 ACL
1926 * permission characters, false otherwise
1929 is_nfs4_perms(const char *start, const char *end, int *permset)
1931 const char *p = start;
1936 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1939 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1942 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1945 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1948 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1951 *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1954 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1957 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1960 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1963 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1966 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1969 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1972 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1975 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1987 * Parse a string as a NFS4 ACL flags field.
1988 * Returns true if the string is non-empty and consists only of NFS4 ACL
1989 * flag characters, false otherwise
1992 is_nfs4_flags(const char *start, const char *end, int *permset)
1994 const char *p = start;
1999 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2002 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2005 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2009 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2012 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2015 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2018 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2030 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
2031 * to point to just after the separator. *start points to the first
2032 * character of the matched text and *end just after the last
2033 * character of the matched identifier. In particular *end - *start
2034 * is the length of the field body, not including leading or trailing
2038 next_field(const char **p, const char **start,
2039 const char **end, char *sep)
2041 /* Skip leading whitespace to find start of field. */
2042 while (**p == ' ' || **p == '\t' || **p == '\n') {
2047 /* Scan for the separator. */
2048 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
2053 /* If the field is only whitespace, bail out now. */
2059 /* Trim trailing whitespace to locate end of field. */
2061 while (**end == ' ' || **end == '\t' || **end == '\n') {
2066 /* Adjust scanner location. */