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);
87 archive_acl_clear(struct archive_acl *acl)
89 struct archive_acl_entry *ap;
91 while (acl->acl_head != NULL) {
92 ap = acl->acl_head->next;
93 archive_mstring_clean(&acl->acl_head->name);
97 if (acl->acl_text_w != NULL) {
98 free(acl->acl_text_w);
99 acl->acl_text_w = NULL;
101 if (acl->acl_text != NULL) {
103 acl->acl_text = NULL;
107 acl->acl_state = 0; /* Not counting. */
111 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
113 struct archive_acl_entry *ap, *ap2;
115 archive_acl_clear(dest);
117 dest->mode = src->mode;
120 ap2 = acl_new_entry(dest,
121 ap->type, ap->permset, ap->tag, ap->id);
123 archive_mstring_copy(&ap2->name, &ap->name);
129 archive_acl_add_entry(struct archive_acl *acl,
130 int type, int permset, int tag, int id, const char *name)
132 struct archive_acl_entry *ap;
134 if (acl_special(acl, type, permset, tag) == 0)
136 ap = acl_new_entry(acl, type, permset, tag, id);
139 return ARCHIVE_FAILED;
141 if (name != NULL && *name != '\0')
142 archive_mstring_copy_mbs(&ap->name, name);
144 archive_mstring_clean(&ap->name);
149 archive_acl_add_entry_w_len(struct archive_acl *acl,
150 int type, int permset, int tag, int id, const wchar_t *name, size_t len)
152 struct archive_acl_entry *ap;
154 if (acl_special(acl, type, permset, tag) == 0)
156 ap = acl_new_entry(acl, type, permset, tag, id);
159 return ARCHIVE_FAILED;
161 if (name != NULL && *name != L'\0' && len > 0)
162 archive_mstring_copy_wcs_len(&ap->name, name, len);
164 archive_mstring_clean(&ap->name);
169 archive_acl_add_entry_len_l(struct archive_acl *acl,
170 int type, int permset, int tag, int id, const char *name, size_t len,
171 struct archive_string_conv *sc)
173 struct archive_acl_entry *ap;
176 if (acl_special(acl, type, permset, tag) == 0)
178 ap = acl_new_entry(acl, type, permset, tag, id);
181 return ARCHIVE_FAILED;
183 if (name != NULL && *name != '\0' && len > 0) {
184 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
187 archive_mstring_clean(&ap->name);
191 else if (errno == ENOMEM)
192 return (ARCHIVE_FATAL);
194 return (ARCHIVE_WARN);
198 * If this ACL entry is part of the standard POSIX permissions set,
199 * store the permissions in the stat structure and return zero.
202 acl_special(struct archive_acl *acl, int type, int permset, int tag)
204 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
205 && ((permset & ~007) == 0)) {
207 case ARCHIVE_ENTRY_ACL_USER_OBJ:
209 acl->mode |= (permset & 7) << 6;
211 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
213 acl->mode |= (permset & 7) << 3;
215 case ARCHIVE_ENTRY_ACL_OTHER:
217 acl->mode |= permset & 7;
225 * Allocate and populate a new ACL entry with everything but the
228 static struct archive_acl_entry *
229 acl_new_entry(struct archive_acl *acl,
230 int type, int permset, int tag, int id)
232 struct archive_acl_entry *ap, *aq;
234 /* Type argument must be a valid NFS4 or POSIX.1e type.
235 * The type must agree with anything already set and
236 * the permset must be compatible. */
237 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
238 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
242 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
243 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
246 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
247 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
250 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
257 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
259 case ARCHIVE_ENTRY_ACL_USER:
260 case ARCHIVE_ENTRY_ACL_USER_OBJ:
261 case ARCHIVE_ENTRY_ACL_GROUP:
262 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
263 /* Tags valid in both NFS4 and POSIX.1e */
265 case ARCHIVE_ENTRY_ACL_MASK:
266 case ARCHIVE_ENTRY_ACL_OTHER:
267 /* Tags valid only in POSIX.1e. */
268 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
272 case ARCHIVE_ENTRY_ACL_EVERYONE:
273 /* Tags valid only in NFS4. */
274 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
279 /* No other values are valid. */
283 if (acl->acl_text_w != NULL) {
284 free(acl->acl_text_w);
285 acl->acl_text_w = NULL;
287 if (acl->acl_text != NULL) {
289 acl->acl_text = NULL;
293 * If there's a matching entry already in the list, overwrite it.
294 * NFSv4 entries may be repeated and are not overwritten.
296 * TODO: compare names of no id is provided (needs more rework)
301 if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
302 ap->type == type && ap->tag == tag && ap->id == id) {
303 if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
304 tag != ARCHIVE_ENTRY_ACL_GROUP)) {
305 ap->permset = permset;
313 /* Add a new entry to the end of the list. */
314 ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
324 ap->permset = permset;
325 acl->acl_types |= type;
330 * Return a count of entries matching "want_type".
333 archive_acl_count(struct archive_acl *acl, int want_type)
336 struct archive_acl_entry *ap;
341 if ((ap->type & want_type) != 0)
346 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
352 * Return a bitmask of stored ACL types in an ACL list
355 archive_acl_types(struct archive_acl *acl)
357 return (acl->acl_types);
361 * Prepare for reading entries from the ACL data. Returns a count
362 * of entries matching "want_type", or zero if there are no
363 * non-extended ACL entries of that type.
366 archive_acl_reset(struct archive_acl *acl, int want_type)
370 count = archive_acl_count(acl, want_type);
373 * If the only entries are the three standard ones,
374 * then don't return any ACL data. (In this case,
375 * client can just use chmod(2) to set permissions.)
377 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
383 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
386 acl->acl_p = acl->acl_head;
392 * Return the next ACL entry in the list. Fake entries for the
393 * standard permissions and include them in the returned list.
396 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
397 int *type, int *permset, int *tag, int *id, const char **name)
403 * The acl_state is either zero (no entries available), -1
404 * (reading from list), or an entry type (retrieve that type
405 * from ae_stat.aest_mode).
407 if (acl->acl_state == 0)
408 return (ARCHIVE_WARN);
410 /* The first three access entries are special. */
411 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
412 switch (acl->acl_state) {
413 case ARCHIVE_ENTRY_ACL_USER_OBJ:
414 *permset = (acl->mode >> 6) & 7;
415 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
416 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
417 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
419 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
420 *permset = (acl->mode >> 3) & 7;
421 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
422 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
423 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
425 case ARCHIVE_ENTRY_ACL_OTHER:
426 *permset = acl->mode & 7;
427 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
428 *tag = ARCHIVE_ENTRY_ACL_OTHER;
430 acl->acl_p = acl->acl_head;
437 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
438 acl->acl_p = acl->acl_p->next;
439 if (acl->acl_p == NULL) {
446 return (ARCHIVE_EOF); /* End of ACL entries. */
448 *type = acl->acl_p->type;
449 *permset = acl->acl_p->permset;
450 *tag = acl->acl_p->tag;
451 *id = acl->acl_p->id;
452 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
454 return (ARCHIVE_FATAL);
457 acl->acl_p = acl->acl_p->next;
462 * Determine what type of ACL do we want
465 archive_acl_text_want_type(struct archive_acl *acl, int flags)
469 /* Check if ACL is NFSv4 */
470 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
471 /* NFSv4 should never mix with POSIX.1e */
472 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
475 return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
478 /* Now deal with POSIX.1e ACLs */
481 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
482 want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
483 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
484 want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
486 /* By default we want both access and default ACLs */
488 return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
494 * Calculate ACL text string length
497 archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
498 int wide, struct archive *a, struct archive_string_conv *sc) {
499 struct archive_acl_entry *ap;
501 const wchar_t *wname;
502 int count, idlen, tmp, r;
508 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
509 if ((ap->type & want_type) == 0)
512 * Filemode-mapping ACL entries are stored exclusively in
513 * ap->mode so they should not be in the list
515 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
516 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
517 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
518 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
521 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
522 && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
523 length += 8; /* "default:" */
525 case ARCHIVE_ENTRY_ACL_USER_OBJ:
526 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
527 length += 6; /* "owner@" */
531 case ARCHIVE_ENTRY_ACL_USER:
532 case ARCHIVE_ENTRY_ACL_MASK:
533 length += 4; /* "user", "mask" */
535 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
536 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
537 length += 6; /* "group@" */
541 case ARCHIVE_ENTRY_ACL_GROUP:
542 case ARCHIVE_ENTRY_ACL_OTHER:
543 length += 5; /* "group", "other" */
545 case ARCHIVE_ENTRY_ACL_EVERYONE:
546 length += 9; /* "everyone@" */
549 length += 1; /* colon after tag */
550 if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
551 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
553 r = archive_mstring_get_wcs(a, &ap->name,
555 if (r == 0 && wname != NULL)
556 length += wcslen(wname);
557 else if (r < 0 && errno == ENOMEM)
560 length += sizeof(uid_t) * 3 + 1;
562 r = archive_mstring_get_mbs_l(&ap->name, &name,
566 if (len > 0 && name != NULL)
569 length += sizeof(uid_t) * 3 + 1;
571 length += 1; /* colon after user or group name */
572 } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
573 length += 1; /* 2nd colon empty user,group or other */
575 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
576 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
577 && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
578 || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
579 /* Solaris has no colon after other: and mask: */
583 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
584 /* rwxpdDaARWcCos:fdinSFI:deny */
586 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
587 length += 1; /* allow, alarm, audit */
589 length += 3; /* rwx */
591 if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
592 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
593 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
594 length += 1; /* colon */
604 length ++; /* entry separator */
607 /* Add filemode-mapping access entries to the length */
608 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
609 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
610 /* "user::rwx\ngroup::rwx\nother:rwx\n" */
613 /* "user::rwx\ngroup::rwx\nother::rwx\n" */
616 } else if (count == 0)
619 /* The terminating character is included in count */
624 * Generate a wide text version of the ACL. The flags parameter controls
625 * the type and style of the generated ACL.
628 archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
634 const wchar_t *wname;
635 const wchar_t *prefix;
637 struct archive_acl_entry *ap;
638 int id, r, want_type;
641 want_type = archive_acl_text_want_type(acl, flags);
643 /* Both NFSv4 and POSIX.1 types found */
647 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
648 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
650 length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
655 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
660 /* Now, allocate the string and actually populate it. */
661 wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
664 __archive_errx(1, "No memory");
669 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
670 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
671 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
672 acl->mode & 0700, -1);
674 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
675 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
676 acl->mode & 0070, -1);
678 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
679 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
680 acl->mode & 0007, -1);
684 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
685 if ((ap->type & want_type) == 0)
688 * Filemode-mapping ACL entries are stored exclusively in
689 * ap->mode so they should not be in the list
691 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
692 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
693 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
694 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
696 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
697 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
698 prefix = L"default:";
701 r = archive_mstring_get_wcs(a, &ap->name, &wname);
705 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
709 append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
710 wname, ap->permset, id);
712 } else if (r < 0 && errno == ENOMEM)
716 /* Add terminating character */
721 if ((ssize_t)len > (length - 1))
722 __archive_errx(1, "Buffer overrun");
724 if (text_len != NULL)
731 append_id_w(wchar_t **wp, int id)
736 append_id_w(wp, id / 10);
737 *(*wp)++ = L"0123456789"[id % 10];
741 append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
742 int tag, int flags, const wchar_t *wname, int perm, int id)
744 if (prefix != NULL) {
749 case ARCHIVE_ENTRY_ACL_USER_OBJ:
752 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
753 wcscpy(*wp, L"owner@");
757 case ARCHIVE_ENTRY_ACL_USER:
758 wcscpy(*wp, L"user");
760 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
763 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
764 wcscpy(*wp, L"group@");
768 case ARCHIVE_ENTRY_ACL_GROUP:
769 wcscpy(*wp, L"group");
771 case ARCHIVE_ENTRY_ACL_MASK:
772 wcscpy(*wp, L"mask");
776 case ARCHIVE_ENTRY_ACL_OTHER:
777 wcscpy(*wp, L"other");
781 case ARCHIVE_ENTRY_ACL_EVERYONE:
782 wcscpy(*wp, L"everyone@");
789 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
790 tag == ARCHIVE_ENTRY_ACL_USER ||
791 tag == ARCHIVE_ENTRY_ACL_GROUP) {
795 } else if (tag == ARCHIVE_ENTRY_ACL_USER
796 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
798 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
801 /* Solaris style has no second colon after other and mask */
802 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
803 || (tag != ARCHIVE_ENTRY_ACL_OTHER
804 && tag != ARCHIVE_ENTRY_ACL_MASK))
807 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
808 /* POSIX.1e ACL perms */
809 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
810 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
811 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
814 *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA |
815 ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? L'r' : L'-';
816 *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA |
817 ARCHIVE_ENTRY_ACL_ADD_FILE)) ? L'w' : L'-';
818 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_EXECUTE) ? L'x' : L'-';
819 *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA |
820 ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? L'p' : L'-';
821 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? L'd' : L'-';
823 ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? L'D' : L'-';
825 ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? L'a' : L'-';
827 ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? L'A' : L'-';
829 ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? L'R' : L'-';
831 ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? L'W' : L'-';
832 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ACL) ? L'c' : L'-';
833 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ACL) ? L'C' : L'-';
835 ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? L'o' : L'-';
837 ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? L's' : L'-';
840 ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? L'f' : L'-';
842 ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? L'd' : L'-';
844 ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? L'i' : L'-';
846 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? L'n' : L'-';
848 ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? L'S' : L'-';
850 ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? L'F' : L'-';
852 ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? L'I' : L'-';
855 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
856 wcscpy(*wp, L"allow");
858 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
859 wcscpy(*wp, L"deny");
861 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
862 wcscpy(*wp, L"audit");
864 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
865 wcscpy(*wp, L"alarm");
879 * Generate a text version of the ACL. The flags parameter controls
880 * the type and style of the generated ACL.
883 archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
884 struct archive_string_conv *sc)
892 struct archive_acl_entry *ap;
893 int id, r, want_type;
896 want_type = archive_acl_text_want_type(acl, flags);
898 /* Both NFSv4 and POSIX.1 types found */
902 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
903 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
905 length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
910 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
915 /* Now, allocate the string and actually populate it. */
916 p = s = (char *)malloc(length * sizeof(char));
919 __archive_errx(1, "No memory");
924 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
925 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
926 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
927 acl->mode & 0700, -1);
929 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
930 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
931 acl->mode & 0070, -1);
933 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
934 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
935 acl->mode & 0007, -1);
939 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
940 if ((ap->type & want_type) == 0)
943 * Filemode-mapping ACL entries are stored exclusively in
944 * ap->mode so they should not be in the list
946 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
947 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
948 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
949 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
951 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
952 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
956 r = archive_mstring_get_mbs_l(
957 &ap->name, &name, &len, sc);
963 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
968 append_entry(&p, prefix, ap->type, ap->tag, flags, name,
973 /* Add terminating character */
978 if ((ssize_t)len > (length - 1))
979 __archive_errx(1, "Buffer overrun");
981 if (text_len != NULL)
988 append_id(char **p, int id)
993 append_id(p, id / 10);
994 *(*p)++ = "0123456789"[id % 10];
998 append_entry(char **p, const char *prefix, int type,
999 int tag, int flags, const char *name, int perm, int id)
1001 if (prefix != NULL) {
1006 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1009 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1010 strcpy(*p, "owner@");
1014 case ARCHIVE_ENTRY_ACL_USER:
1017 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1020 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1021 strcpy(*p, "group@");
1025 case ARCHIVE_ENTRY_ACL_GROUP:
1026 strcpy(*p, "group");
1028 case ARCHIVE_ENTRY_ACL_MASK:
1033 case ARCHIVE_ENTRY_ACL_OTHER:
1034 strcpy(*p, "other");
1038 case ARCHIVE_ENTRY_ACL_EVERYONE:
1039 strcpy(*p, "everyone@");
1046 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1047 tag == ARCHIVE_ENTRY_ACL_USER ||
1048 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1052 } else if (tag == ARCHIVE_ENTRY_ACL_USER
1053 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1055 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1058 /* Solaris style has no second colon after other and mask */
1059 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1060 || (tag != ARCHIVE_ENTRY_ACL_OTHER
1061 && tag != ARCHIVE_ENTRY_ACL_MASK))
1064 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1065 /* POSIX.1e ACL perms */
1066 *(*p)++ = (perm & 0444) ? 'r' : '-';
1067 *(*p)++ = (perm & 0222) ? 'w' : '-';
1068 *(*p)++ = (perm & 0111) ? 'x' : '-';
1070 /* NFS4 ACL perms */
1071 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA |
1072 ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? 'r' : '-';
1073 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA |
1074 ARCHIVE_ENTRY_ACL_ADD_FILE)) ? 'w' : '-';
1075 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_EXECUTE)) ? 'x' : '-';
1076 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA |
1077 ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? 'p' : '-';
1078 *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? 'd' : '-';
1079 *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? 'D' : '-';
1081 ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? 'a' : '-';
1083 ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? 'A' : '-';
1085 ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? 'R' : '-';
1087 ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? 'W' : '-';
1089 ARCHIVE_ENTRY_ACL_READ_ACL) ? 'c' : '-';
1091 ARCHIVE_ENTRY_ACL_WRITE_ACL) ? 'C' : '-';
1093 ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? 'o' : '-';
1095 ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? 's' : '-';
1098 ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? 'f' : '-';
1100 ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? 'd' : '-';
1102 ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? 'i' : '-';
1104 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? 'n' : '-';
1106 ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? 'S' : '-';
1108 ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? 'F' : '-';
1110 ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? 'I' : '-';
1113 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1114 strcpy(*p, "allow");
1116 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1119 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1120 strcpy(*p, "audit");
1122 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1123 strcpy(*p, "alarm");
1135 * Parse a wide ACL text string.
1137 * The want_type argument may be one of the following:
1138 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1139 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1140 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1142 * POSIX.1e ACL entries prefixed with "default:" are treated as
1143 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1146 archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1150 const wchar_t *start;
1154 const wchar_t *s, *st;
1156 int numfields, fields, n, r, sol, ret;
1157 int type, types, tag, permset, id;
1164 switch (want_type) {
1165 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1166 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1167 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1168 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1171 case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1175 return (ARCHIVE_FATAL);
1178 while (text != NULL && *text != L'\0') {
1180 * Parse the fields out of the next entry,
1181 * advance 'text' to start of next entry.
1185 const wchar_t *start, *end;
1186 next_field_w(&text, &start, &end, &sep);
1187 if (fields < numfields) {
1188 field[fields].start = start;
1189 field[fields].end = end;
1192 } while (sep == L':');
1194 /* Set remaining fields to blank. */
1195 for (n = fields; n < numfields; ++n)
1196 field[n].start = field[n].end = NULL;
1198 if (field[0].start != NULL && *(field[0].start) == L'#') {
1199 /* Comment, skip entry */
1207 name.start = name.end = NULL;
1209 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1212 * Default keyword "default:user::rwx"
1213 * if found, we have one more field
1215 * We also support old Solaris extension:
1216 * "defaultuser::rwx" is the default ACL corresponding
1217 * to "user::rwx", etc. valid only for first field
1220 len = field[0].end - field[0].start;
1221 if (*s == L'd' && (len == 1 || (len >= 7
1222 && wmemcmp((s + 1), L"efault", 6) == 0))) {
1223 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1225 field[0].start += 7;
1231 /* Check for a numeric ID in field n+1 or n+3. */
1232 isint_w(field[n + 1].start, field[n + 1].end, &id);
1233 /* Field n+3 is optional. */
1234 if (id == -1 && fields > n+3)
1235 isint_w(field[n + 3].start, field[n + 3].end,
1240 st = field[n].start + 1;
1241 len = field[n].end - field[n].start;
1245 if (len == 1 || (len == 4
1246 && wmemcmp(st, L"ser", 3) == 0))
1247 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1250 if (len == 1 || (len == 5
1251 && wmemcmp(st, L"roup", 4) == 0))
1252 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1255 if (len == 1 || (len == 5
1256 && wmemcmp(st, L"ther", 4) == 0))
1257 tag = ARCHIVE_ENTRY_ACL_OTHER;
1260 if (len == 1 || (len == 4
1261 && wmemcmp(st, L"ask", 3) == 0))
1262 tag = ARCHIVE_ENTRY_ACL_MASK;
1269 case ARCHIVE_ENTRY_ACL_OTHER:
1270 case ARCHIVE_ENTRY_ACL_MASK:
1271 if (fields == (n + 2)
1272 && field[n + 1].start < field[n + 1].end
1273 && ismode_w(field[n + 1].start,
1274 field[n + 1].end, &permset)) {
1275 /* This is Solaris-style "other:rwx" */
1277 } else if (fields == (n + 3) &&
1278 field[n + 1].start < field[n + 1].end) {
1279 /* Invalid mask or other field */
1284 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1285 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1287 field[n + 1].start < field[n + 1].end) {
1288 name = field[n + 1];
1289 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1290 tag = ARCHIVE_ENTRY_ACL_USER;
1292 tag = ARCHIVE_ENTRY_ACL_GROUP;
1296 /* Invalid tag, skip entry */
1302 * Without "default:" we expect mode in field 2
1303 * Exception: Solaris other and mask fields
1305 if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1306 field[n + 2 - sol].end, &permset)) {
1307 /* Invalid mode, skip entry */
1314 len = field[0].end - field[0].start;
1319 if (wmemcmp(s, L"user", 4) == 0)
1320 tag = ARCHIVE_ENTRY_ACL_USER;
1323 if (wmemcmp(s, L"group", 5) == 0)
1324 tag = ARCHIVE_ENTRY_ACL_GROUP;
1327 if (wmemcmp(s, L"owner@", 6) == 0)
1328 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1329 else if (wmemcmp(s, L"group@", len) == 0)
1330 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1333 if (wmemcmp(s, L"everyone@", 9) == 0)
1334 tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1340 /* Invalid tag, skip entry */
1343 } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1344 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1347 isint_w(name.start, name.end, &id);
1351 if (!is_nfs4_perms_w(field[1 + n].start,
1352 field[1 + n].end, &permset)) {
1353 /* Invalid NFSv4 perms, skip entry */
1357 if (!is_nfs4_flags_w(field[2 + n].start,
1358 field[2 + n].end, &permset)) {
1359 /* Invalid NFSv4 flags, skip entry */
1363 s = field[3 + n].start;
1364 len = field[3 + n].end - field[3 + n].start;
1367 if (wmemcmp(s, L"deny", 4) == 0)
1368 type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1369 } else if (len == 5) {
1370 if (wmemcmp(s, L"allow", 5) == 0)
1371 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1372 else if (wmemcmp(s, L"audit", 5) == 0)
1373 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1374 else if (wmemcmp(s, L"alarm", 5) == 0)
1375 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1378 /* Invalid entry type, skip entry */
1382 isint_w(field[4 + n].start, field[4 + n].end, &id);
1385 /* Add entry to the internal list. */
1386 r = archive_acl_add_entry_w_len(acl, type, permset,
1387 tag, id, name.start, name.end - name.start);
1388 if (r < ARCHIVE_WARN)
1390 if (r != ARCHIVE_OK)
1396 archive_acl_reset(acl, types);
1402 * Parse a string to a positive decimal integer. Returns true if
1403 * the string is non-empty and consists only of decimal digits,
1407 isint_w(const wchar_t *start, const wchar_t *end, int *result)
1412 while (start < end) {
1413 if (*start < '0' || *start > '9')
1415 if (n > (INT_MAX / 10) ||
1416 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1429 * Parse a string as a mode field. Returns true if
1430 * the string is non-empty and consists only of mode characters,
1434 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1444 case L'r': case L'R':
1445 *permset |= ARCHIVE_ENTRY_ACL_READ;
1447 case L'w': case L'W':
1448 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1450 case L'x': case L'X':
1451 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1463 * Parse a string as a NFS4 ACL permission field.
1464 * Returns true if the string is non-empty and consists only of NFS4 ACL
1465 * permission characters, false otherwise
1468 is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1478 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1481 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1484 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1487 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1490 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1493 *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1496 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1499 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1502 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1505 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1508 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1511 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1514 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1517 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1529 * Parse a string as a NFS4 ACL flags field.
1530 * Returns true if the string is non-empty and consists only of NFS4 ACL
1531 * flag characters, false otherwise
1534 is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1544 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1547 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1550 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1554 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1557 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1560 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1563 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1575 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
1576 * to point to just after the separator. *start points to the first
1577 * character of the matched text and *end just after the last
1578 * character of the matched identifier. In particular *end - *start
1579 * is the length of the field body, not including leading or trailing
1583 next_field_w(const wchar_t **wp, const wchar_t **start,
1584 const wchar_t **end, wchar_t *sep)
1586 /* Skip leading whitespace to find start of field. */
1587 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1592 /* Scan for the separator. */
1593 while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1599 /* Trim trailing whitespace to locate end of field. */
1601 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1606 /* Adjust scanner location. */
1612 * Parse an ACL text string.
1614 * The want_type argument may be one of the following:
1615 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1616 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1617 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1619 * POSIX.1e ACL entries prefixed with "default:" are treated as
1620 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1623 archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1624 int want_type, struct archive_string_conv *sc)
1632 int numfields, fields, n, r, sol, ret;
1633 int type, types, tag, permset, id;
1637 switch (want_type) {
1638 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1639 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1640 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1641 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1644 case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1648 return (ARCHIVE_FATAL);
1654 while (text != NULL && *text != '\0') {
1656 * Parse the fields out of the next entry,
1657 * advance 'text' to start of next entry.
1661 const char *start, *end;
1662 next_field(&text, &start, &end, &sep);
1663 if (fields < numfields) {
1664 field[fields].start = start;
1665 field[fields].end = end;
1668 } while (sep == ':');
1670 /* Set remaining fields to blank. */
1671 for (n = fields; n < numfields; ++n)
1672 field[n].start = field[n].end = NULL;
1674 if (field[0].start != NULL && *(field[0].start) == '#') {
1675 /* Comment, skip entry */
1683 name.start = name.end = NULL;
1685 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1688 * Default keyword "default:user::rwx"
1689 * if found, we have one more field
1691 * We also support old Solaris extension:
1692 * "defaultuser::rwx" is the default ACL corresponding
1693 * to "user::rwx", etc. valid only for first field
1696 len = field[0].end - field[0].start;
1697 if (*s == 'd' && (len == 1 || (len >= 7
1698 && memcmp((s + 1), "efault", 6) == 0))) {
1699 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1701 field[0].start += 7;
1707 /* Check for a numeric ID in field n+1 or n+3. */
1708 isint(field[n + 1].start, field[n + 1].end, &id);
1709 /* Field n+3 is optional. */
1710 if (id == -1 && fields > (n + 3))
1711 isint(field[n + 3].start, field[n + 3].end,
1716 st = field[n].start + 1;
1717 len = field[n].end - field[n].start;
1721 if (len == 1 || (len == 4
1722 && memcmp(st, "ser", 3) == 0))
1723 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1726 if (len == 1 || (len == 5
1727 && memcmp(st, "roup", 4) == 0))
1728 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1731 if (len == 1 || (len == 5
1732 && memcmp(st, "ther", 4) == 0))
1733 tag = ARCHIVE_ENTRY_ACL_OTHER;
1736 if (len == 1 || (len == 4
1737 && memcmp(st, "ask", 3) == 0))
1738 tag = ARCHIVE_ENTRY_ACL_MASK;
1745 case ARCHIVE_ENTRY_ACL_OTHER:
1746 case ARCHIVE_ENTRY_ACL_MASK:
1747 if (fields == (n + 2)
1748 && field[n + 1].start < field[n + 1].end
1749 && ismode(field[n + 1].start,
1750 field[n + 1].end, &permset)) {
1751 /* This is Solaris-style "other:rwx" */
1753 } else if (fields == (n + 3) &&
1754 field[n + 1].start < field[n + 1].end) {
1755 /* Invalid mask or other field */
1760 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1761 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1763 field[n + 1].start < field[n + 1].end) {
1764 name = field[n + 1];
1765 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1766 tag = ARCHIVE_ENTRY_ACL_USER;
1768 tag = ARCHIVE_ENTRY_ACL_GROUP;
1772 /* Invalid tag, skip entry */
1778 * Without "default:" we expect mode in field 3
1779 * Exception: Solaris other and mask fields
1781 if (permset == 0 && !ismode(field[n + 2 - sol].start,
1782 field[n + 2 - sol].end, &permset)) {
1783 /* Invalid mode, skip entry */
1790 len = field[0].end - field[0].start;
1795 if (memcmp(s, "user", 4) == 0)
1796 tag = ARCHIVE_ENTRY_ACL_USER;
1799 if (memcmp(s, "group", 5) == 0)
1800 tag = ARCHIVE_ENTRY_ACL_GROUP;
1803 if (memcmp(s, "owner@", 6) == 0)
1804 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1805 else if (memcmp(s, "group@", 6) == 0)
1806 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1809 if (memcmp(s, "everyone@", 9) == 0)
1810 tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1817 /* Invalid tag, skip entry */
1820 } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1821 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1824 isint(name.start, name.end, &id);
1828 if (!is_nfs4_perms(field[1 + n].start,
1829 field[1 + n].end, &permset)) {
1830 /* Invalid NFSv4 perms, skip entry */
1834 if (!is_nfs4_flags(field[2 + n].start,
1835 field[2 + n].end, &permset)) {
1836 /* Invalid NFSv4 flags, skip entry */
1840 s = field[3 + n].start;
1841 len = field[3 + n].end - field[3 + n].start;
1844 if (memcmp(s, "deny", 4) == 0)
1845 type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1846 } else if (len == 5) {
1847 if (memcmp(s, "allow", 5) == 0)
1848 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1849 else if (memcmp(s, "audit", 5) == 0)
1850 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1851 else if (memcmp(s, "alarm", 5) == 0)
1852 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1855 /* Invalid entry type, skip entry */
1859 isint(field[4 + n].start, field[4 + n].end,
1863 /* Add entry to the internal list. */
1864 r = archive_acl_add_entry_len_l(acl, type, permset,
1865 tag, id, name.start, name.end - name.start, sc);
1866 if (r < ARCHIVE_WARN)
1868 if (r != ARCHIVE_OK)
1874 archive_acl_reset(acl, types);
1880 * Parse a string to a positive decimal integer. Returns true if
1881 * the string is non-empty and consists only of decimal digits,
1885 isint(const char *start, const char *end, int *result)
1890 while (start < end) {
1891 if (*start < '0' || *start > '9')
1893 if (n > (INT_MAX / 10) ||
1894 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1907 * Parse a string as a mode field. Returns true if
1908 * the string is non-empty and consists only of mode characters,
1912 ismode(const char *start, const char *end, int *permset)
1923 *permset |= ARCHIVE_ENTRY_ACL_READ;
1926 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1929 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1941 * Parse a string as a NFS4 ACL permission field.
1942 * Returns true if the string is non-empty and consists only of NFS4 ACL
1943 * permission characters, false otherwise
1946 is_nfs4_perms(const char *start, const char *end, int *permset)
1956 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1959 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1962 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1965 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1968 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1971 *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1974 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1977 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1980 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1983 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1986 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1989 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1992 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1995 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
2007 * Parse a string as a NFS4 ACL flags field.
2008 * Returns true if the string is non-empty and consists only of NFS4 ACL
2009 * flag characters, false otherwise
2012 is_nfs4_flags(const char *start, const char *end, int *permset)
2022 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2025 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2028 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2032 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2035 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2038 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2041 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2053 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
2054 * to point to just after the separator. *start points to the first
2055 * character of the matched text and *end just after the last
2056 * character of the matched identifier. In particular *end - *start
2057 * is the length of the field body, not including leading or trailing
2061 next_field(const char **p, const char **start,
2062 const char **end, char *sep)
2064 /* Skip leading whitespace to find start of field. */
2065 while (**p == ' ' || **p == '\t' || **p == '\n') {
2070 /* Scan for the separator. */
2071 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
2076 /* Trim trailing whitespace to locate end of field. */
2078 while (**end == ' ' || **end == '\t' || **end == '\n') {
2083 /* Adjust scanner location. */