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 if (acl->acl_text_w != NULL) {
142 free(acl->acl_text_w);
143 acl->acl_text_w = NULL;
145 if (acl->acl_text != NULL) {
147 acl->acl_text = NULL;
151 acl->acl_state = 0; /* Not counting. */
155 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
157 struct archive_acl_entry *ap, *ap2;
159 archive_acl_clear(dest);
161 dest->mode = src->mode;
164 ap2 = acl_new_entry(dest,
165 ap->type, ap->permset, ap->tag, ap->id);
167 archive_mstring_copy(&ap2->name, &ap->name);
173 archive_acl_add_entry(struct archive_acl *acl,
174 int type, int permset, int tag, int id, const char *name)
176 struct archive_acl_entry *ap;
178 if (acl_special(acl, type, permset, tag) == 0)
180 ap = acl_new_entry(acl, type, permset, tag, id);
183 return ARCHIVE_FAILED;
185 if (name != NULL && *name != '\0')
186 archive_mstring_copy_mbs(&ap->name, name);
188 archive_mstring_clean(&ap->name);
193 archive_acl_add_entry_w_len(struct archive_acl *acl,
194 int type, int permset, int tag, int id, const wchar_t *name, size_t len)
196 struct archive_acl_entry *ap;
198 if (acl_special(acl, type, permset, tag) == 0)
200 ap = acl_new_entry(acl, type, permset, tag, id);
203 return ARCHIVE_FAILED;
205 if (name != NULL && *name != L'\0' && len > 0)
206 archive_mstring_copy_wcs_len(&ap->name, name, len);
208 archive_mstring_clean(&ap->name);
213 archive_acl_add_entry_len_l(struct archive_acl *acl,
214 int type, int permset, int tag, int id, const char *name, size_t len,
215 struct archive_string_conv *sc)
217 struct archive_acl_entry *ap;
220 if (acl_special(acl, type, permset, tag) == 0)
222 ap = acl_new_entry(acl, type, permset, tag, id);
225 return ARCHIVE_FAILED;
227 if (name != NULL && *name != '\0' && len > 0) {
228 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
231 archive_mstring_clean(&ap->name);
235 else if (errno == ENOMEM)
236 return (ARCHIVE_FATAL);
238 return (ARCHIVE_WARN);
242 * If this ACL entry is part of the standard POSIX permissions set,
243 * store the permissions in the stat structure and return zero.
246 acl_special(struct archive_acl *acl, int type, int permset, int tag)
248 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
249 && ((permset & ~007) == 0)) {
251 case ARCHIVE_ENTRY_ACL_USER_OBJ:
253 acl->mode |= (permset & 7) << 6;
255 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
257 acl->mode |= (permset & 7) << 3;
259 case ARCHIVE_ENTRY_ACL_OTHER:
261 acl->mode |= permset & 7;
269 * Allocate and populate a new ACL entry with everything but the
272 static struct archive_acl_entry *
273 acl_new_entry(struct archive_acl *acl,
274 int type, int permset, int tag, int id)
276 struct archive_acl_entry *ap, *aq;
278 /* Type argument must be a valid NFS4 or POSIX.1e type.
279 * The type must agree with anything already set and
280 * the permset must be compatible. */
281 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
282 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
286 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
287 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
290 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
291 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
294 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
301 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
303 case ARCHIVE_ENTRY_ACL_USER:
304 case ARCHIVE_ENTRY_ACL_USER_OBJ:
305 case ARCHIVE_ENTRY_ACL_GROUP:
306 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
307 /* Tags valid in both NFS4 and POSIX.1e */
309 case ARCHIVE_ENTRY_ACL_MASK:
310 case ARCHIVE_ENTRY_ACL_OTHER:
311 /* Tags valid only in POSIX.1e. */
312 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
316 case ARCHIVE_ENTRY_ACL_EVERYONE:
317 /* Tags valid only in NFS4. */
318 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
323 /* No other values are valid. */
327 if (acl->acl_text_w != NULL) {
328 free(acl->acl_text_w);
329 acl->acl_text_w = NULL;
331 if (acl->acl_text != NULL) {
333 acl->acl_text = NULL;
337 * If there's a matching entry already in the list, overwrite it.
338 * NFSv4 entries may be repeated and are not overwritten.
340 * TODO: compare names of no id is provided (needs more rework)
345 if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
346 ap->type == type && ap->tag == tag && ap->id == id) {
347 if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
348 tag != ARCHIVE_ENTRY_ACL_GROUP)) {
349 ap->permset = permset;
357 /* Add a new entry to the end of the list. */
358 ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
368 ap->permset = permset;
369 acl->acl_types |= type;
374 * Return a count of entries matching "want_type".
377 archive_acl_count(struct archive_acl *acl, int want_type)
380 struct archive_acl_entry *ap;
385 if ((ap->type & want_type) != 0)
390 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
396 * Return a bitmask of stored ACL types in an ACL list
399 archive_acl_types(struct archive_acl *acl)
401 return (acl->acl_types);
405 * Prepare for reading entries from the ACL data. Returns a count
406 * of entries matching "want_type", or zero if there are no
407 * non-extended ACL entries of that type.
410 archive_acl_reset(struct archive_acl *acl, int want_type)
414 count = archive_acl_count(acl, want_type);
417 * If the only entries are the three standard ones,
418 * then don't return any ACL data. (In this case,
419 * client can just use chmod(2) to set permissions.)
421 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
427 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
430 acl->acl_p = acl->acl_head;
436 * Return the next ACL entry in the list. Fake entries for the
437 * standard permissions and include them in the returned list.
440 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
441 int *type, int *permset, int *tag, int *id, const char **name)
447 * The acl_state is either zero (no entries available), -1
448 * (reading from list), or an entry type (retrieve that type
449 * from ae_stat.aest_mode).
451 if (acl->acl_state == 0)
452 return (ARCHIVE_WARN);
454 /* The first three access entries are special. */
455 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
456 switch (acl->acl_state) {
457 case ARCHIVE_ENTRY_ACL_USER_OBJ:
458 *permset = (acl->mode >> 6) & 7;
459 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
460 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
461 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
463 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
464 *permset = (acl->mode >> 3) & 7;
465 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
466 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
467 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
469 case ARCHIVE_ENTRY_ACL_OTHER:
470 *permset = acl->mode & 7;
471 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
472 *tag = ARCHIVE_ENTRY_ACL_OTHER;
474 acl->acl_p = acl->acl_head;
481 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
482 acl->acl_p = acl->acl_p->next;
483 if (acl->acl_p == NULL) {
490 return (ARCHIVE_EOF); /* End of ACL entries. */
492 *type = acl->acl_p->type;
493 *permset = acl->acl_p->permset;
494 *tag = acl->acl_p->tag;
495 *id = acl->acl_p->id;
496 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
498 return (ARCHIVE_FATAL);
501 acl->acl_p = acl->acl_p->next;
506 * Determine what type of ACL do we want
509 archive_acl_text_want_type(struct archive_acl *acl, int flags)
513 /* Check if ACL is NFSv4 */
514 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
515 /* NFSv4 should never mix with POSIX.1e */
516 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
519 return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
522 /* Now deal with POSIX.1e ACLs */
525 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
526 want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
527 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
528 want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
530 /* By default we want both access and default ACLs */
532 return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
538 * Calculate ACL text string length
541 archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
542 int wide, struct archive *a, struct archive_string_conv *sc) {
543 struct archive_acl_entry *ap;
545 const wchar_t *wname;
546 int count, idlen, tmp, r;
552 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
553 if ((ap->type & want_type) == 0)
556 * Filemode-mapping ACL entries are stored exclusively in
557 * ap->mode so they should not be in the list
559 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
560 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
561 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
562 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
565 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
566 && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
567 length += 8; /* "default:" */
569 case ARCHIVE_ENTRY_ACL_USER_OBJ:
570 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
571 length += 6; /* "owner@" */
575 case ARCHIVE_ENTRY_ACL_USER:
576 case ARCHIVE_ENTRY_ACL_MASK:
577 length += 4; /* "user", "mask" */
579 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
580 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
581 length += 6; /* "group@" */
585 case ARCHIVE_ENTRY_ACL_GROUP:
586 case ARCHIVE_ENTRY_ACL_OTHER:
587 length += 5; /* "group", "other" */
589 case ARCHIVE_ENTRY_ACL_EVERYONE:
590 length += 9; /* "everyone@" */
593 length += 1; /* colon after tag */
594 if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
595 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
597 r = archive_mstring_get_wcs(a, &ap->name,
599 if (r == 0 && wname != NULL)
600 length += wcslen(wname);
601 else if (r < 0 && errno == ENOMEM)
604 length += sizeof(uid_t) * 3 + 1;
606 r = archive_mstring_get_mbs_l(&ap->name, &name,
610 if (len > 0 && name != NULL)
613 length += sizeof(uid_t) * 3 + 1;
615 length += 1; /* colon after user or group name */
616 } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
617 length += 1; /* 2nd colon empty user,group or other */
619 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
620 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
621 && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
622 || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
623 /* Solaris has no colon after other: and mask: */
627 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
628 /* rwxpdDaARWcCos:fdinSFI:deny */
630 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
631 length += 1; /* allow, alarm, audit */
633 length += 3; /* rwx */
635 if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
636 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
637 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
638 length += 1; /* colon */
648 length ++; /* entry separator */
651 /* Add filemode-mapping access entries to the length */
652 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
653 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
654 /* "user::rwx\ngroup::rwx\nother:rwx\n" */
657 /* "user::rwx\ngroup::rwx\nother::rwx\n" */
660 } else if (count == 0)
663 /* The terminating character is included in count */
668 * Generate a wide text version of the ACL. The flags parameter controls
669 * the type and style of the generated ACL.
672 archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
678 const wchar_t *wname;
679 const wchar_t *prefix;
681 struct archive_acl_entry *ap;
682 int id, r, want_type;
685 want_type = archive_acl_text_want_type(acl, flags);
687 /* Both NFSv4 and POSIX.1 types found */
691 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
692 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
694 length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
699 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
704 /* Now, allocate the string and actually populate it. */
705 wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
708 __archive_errx(1, "No memory");
713 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
714 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
715 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
716 acl->mode & 0700, -1);
718 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
719 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
720 acl->mode & 0070, -1);
722 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
723 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
724 acl->mode & 0007, -1);
728 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
729 if ((ap->type & want_type) == 0)
732 * Filemode-mapping ACL entries are stored exclusively in
733 * ap->mode so they should not be in the list
735 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
736 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
737 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
738 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
740 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
741 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
742 prefix = L"default:";
745 r = archive_mstring_get_wcs(a, &ap->name, &wname);
749 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
753 append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
754 wname, ap->permset, id);
756 } else if (r < 0 && errno == ENOMEM)
760 /* Add terminating character */
765 if ((ssize_t)len > (length - 1))
766 __archive_errx(1, "Buffer overrun");
768 if (text_len != NULL)
775 append_id_w(wchar_t **wp, int id)
780 append_id_w(wp, id / 10);
781 *(*wp)++ = L"0123456789"[id % 10];
785 append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
786 int tag, int flags, const wchar_t *wname, int perm, int id)
790 if (prefix != NULL) {
795 case ARCHIVE_ENTRY_ACL_USER_OBJ:
798 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
799 wcscpy(*wp, L"owner@");
803 case ARCHIVE_ENTRY_ACL_USER:
804 wcscpy(*wp, L"user");
806 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
809 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
810 wcscpy(*wp, L"group@");
814 case ARCHIVE_ENTRY_ACL_GROUP:
815 wcscpy(*wp, L"group");
817 case ARCHIVE_ENTRY_ACL_MASK:
818 wcscpy(*wp, L"mask");
822 case ARCHIVE_ENTRY_ACL_OTHER:
823 wcscpy(*wp, L"other");
827 case ARCHIVE_ENTRY_ACL_EVERYONE:
828 wcscpy(*wp, L"everyone@");
835 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
836 tag == ARCHIVE_ENTRY_ACL_USER ||
837 tag == ARCHIVE_ENTRY_ACL_GROUP) {
841 } else if (tag == ARCHIVE_ENTRY_ACL_USER
842 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
844 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
847 /* Solaris style has no second colon after other and mask */
848 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
849 || (tag != ARCHIVE_ENTRY_ACL_OTHER
850 && tag != ARCHIVE_ENTRY_ACL_MASK))
853 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
854 /* POSIX.1e ACL perms */
855 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
856 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
857 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
859 /* NFSv4 ACL perms */
860 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
861 if (perm & nfsv4_acl_perm_map[i].perm)
862 *(*wp)++ = nfsv4_acl_perm_map[i].wc;
863 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
867 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
868 if (perm & nfsv4_acl_flag_map[i].perm)
869 *(*wp)++ = nfsv4_acl_flag_map[i].wc;
870 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
875 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
876 wcscpy(*wp, L"allow");
878 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
879 wcscpy(*wp, L"deny");
881 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
882 wcscpy(*wp, L"audit");
884 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
885 wcscpy(*wp, L"alarm");
899 * Generate a text version of the ACL. The flags parameter controls
900 * the type and style of the generated ACL.
903 archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
904 struct archive_string_conv *sc)
912 struct archive_acl_entry *ap;
913 int id, r, want_type;
916 want_type = archive_acl_text_want_type(acl, flags);
918 /* Both NFSv4 and POSIX.1 types found */
922 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
923 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
925 length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
930 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
935 /* Now, allocate the string and actually populate it. */
936 p = s = (char *)malloc(length * sizeof(char));
939 __archive_errx(1, "No memory");
944 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
945 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
946 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
947 acl->mode & 0700, -1);
949 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
950 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
951 acl->mode & 0070, -1);
953 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
954 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
955 acl->mode & 0007, -1);
959 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
960 if ((ap->type & want_type) == 0)
963 * Filemode-mapping ACL entries are stored exclusively in
964 * ap->mode so they should not be in the list
966 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
967 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
968 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
969 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
971 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
972 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
976 r = archive_mstring_get_mbs_l(
977 &ap->name, &name, &len, sc);
983 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
988 append_entry(&p, prefix, ap->type, ap->tag, flags, name,
993 /* Add terminating character */
998 if ((ssize_t)len > (length - 1))
999 __archive_errx(1, "Buffer overrun");
1001 if (text_len != NULL)
1008 append_id(char **p, int id)
1013 append_id(p, id / 10);
1014 *(*p)++ = "0123456789"[id % 10];
1018 append_entry(char **p, const char *prefix, int type,
1019 int tag, int flags, const char *name, int perm, int id)
1023 if (prefix != NULL) {
1028 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1031 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1032 strcpy(*p, "owner@");
1036 case ARCHIVE_ENTRY_ACL_USER:
1039 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1042 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1043 strcpy(*p, "group@");
1047 case ARCHIVE_ENTRY_ACL_GROUP:
1048 strcpy(*p, "group");
1050 case ARCHIVE_ENTRY_ACL_MASK:
1055 case ARCHIVE_ENTRY_ACL_OTHER:
1056 strcpy(*p, "other");
1060 case ARCHIVE_ENTRY_ACL_EVERYONE:
1061 strcpy(*p, "everyone@");
1068 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1069 tag == ARCHIVE_ENTRY_ACL_USER ||
1070 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1074 } else if (tag == ARCHIVE_ENTRY_ACL_USER
1075 || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1077 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1080 /* Solaris style has no second colon after other and mask */
1081 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1082 || (tag != ARCHIVE_ENTRY_ACL_OTHER
1083 && tag != ARCHIVE_ENTRY_ACL_MASK))
1086 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1087 /* POSIX.1e ACL perms */
1088 *(*p)++ = (perm & 0444) ? 'r' : '-';
1089 *(*p)++ = (perm & 0222) ? 'w' : '-';
1090 *(*p)++ = (perm & 0111) ? 'x' : '-';
1092 /* NFSv4 ACL perms */
1093 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1094 if (perm & nfsv4_acl_perm_map[i].perm)
1095 *(*p)++ = nfsv4_acl_perm_map[i].c;
1096 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1100 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1101 if (perm & nfsv4_acl_flag_map[i].perm)
1102 *(*p)++ = nfsv4_acl_flag_map[i].c;
1103 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1108 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1109 strcpy(*p, "allow");
1111 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1114 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1115 strcpy(*p, "audit");
1117 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1118 strcpy(*p, "alarm");
1130 * Parse a wide ACL text string.
1132 * The want_type argument may be one of the following:
1133 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1134 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1135 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1137 * POSIX.1e ACL entries prefixed with "default:" are treated as
1138 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1141 archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1145 const wchar_t *start;
1149 const wchar_t *s, *st;
1151 int numfields, fields, n, r, sol, ret;
1152 int type, types, tag, permset, id;
1159 switch (want_type) {
1160 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1161 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1163 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1164 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1167 case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1171 return (ARCHIVE_FATAL);
1174 while (text != NULL && *text != L'\0') {
1176 * Parse the fields out of the next entry,
1177 * advance 'text' to start of next entry.
1181 const wchar_t *start, *end;
1182 next_field_w(&text, &start, &end, &sep);
1183 if (fields < numfields) {
1184 field[fields].start = start;
1185 field[fields].end = end;
1188 } while (sep == L':');
1190 /* Set remaining fields to blank. */
1191 for (n = fields; n < numfields; ++n)
1192 field[n].start = field[n].end = NULL;
1194 if (field[0].start != NULL && *(field[0].start) == L'#') {
1195 /* Comment, skip entry */
1203 name.start = name.end = NULL;
1205 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1208 * Default keyword "default:user::rwx"
1209 * if found, we have one more field
1211 * We also support old Solaris extension:
1212 * "defaultuser::rwx" is the default ACL corresponding
1213 * to "user::rwx", etc. valid only for first field
1216 len = field[0].end - field[0].start;
1217 if (*s == L'd' && (len == 1 || (len >= 7
1218 && wmemcmp((s + 1), L"efault", 6) == 0))) {
1219 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1221 field[0].start += 7;
1227 /* Check for a numeric ID in field n+1 or n+3. */
1228 isint_w(field[n + 1].start, field[n + 1].end, &id);
1229 /* Field n+3 is optional. */
1230 if (id == -1 && fields > n+3)
1231 isint_w(field[n + 3].start, field[n + 3].end,
1236 st = field[n].start + 1;
1237 len = field[n].end - field[n].start;
1241 if (len == 1 || (len == 4
1242 && wmemcmp(st, L"ser", 3) == 0))
1243 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1246 if (len == 1 || (len == 5
1247 && wmemcmp(st, L"roup", 4) == 0))
1248 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1251 if (len == 1 || (len == 5
1252 && wmemcmp(st, L"ther", 4) == 0))
1253 tag = ARCHIVE_ENTRY_ACL_OTHER;
1256 if (len == 1 || (len == 4
1257 && wmemcmp(st, L"ask", 3) == 0))
1258 tag = ARCHIVE_ENTRY_ACL_MASK;
1265 case ARCHIVE_ENTRY_ACL_OTHER:
1266 case ARCHIVE_ENTRY_ACL_MASK:
1267 if (fields == (n + 2)
1268 && field[n + 1].start < field[n + 1].end
1269 && ismode_w(field[n + 1].start,
1270 field[n + 1].end, &permset)) {
1271 /* This is Solaris-style "other:rwx" */
1273 } else if (fields == (n + 3) &&
1274 field[n + 1].start < field[n + 1].end) {
1275 /* Invalid mask or other field */
1280 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1281 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1283 field[n + 1].start < field[n + 1].end) {
1284 name = field[n + 1];
1285 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1286 tag = ARCHIVE_ENTRY_ACL_USER;
1288 tag = ARCHIVE_ENTRY_ACL_GROUP;
1292 /* Invalid tag, skip entry */
1298 * Without "default:" we expect mode in field 2
1299 * Exception: Solaris other and mask fields
1301 if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1302 field[n + 2 - sol].end, &permset)) {
1303 /* Invalid mode, skip entry */
1310 len = field[0].end - field[0].start;
1315 if (wmemcmp(s, L"user", 4) == 0)
1316 tag = ARCHIVE_ENTRY_ACL_USER;
1319 if (wmemcmp(s, L"group", 5) == 0)
1320 tag = ARCHIVE_ENTRY_ACL_GROUP;
1323 if (wmemcmp(s, L"owner@", 6) == 0)
1324 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1325 else if (wmemcmp(s, L"group@", len) == 0)
1326 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1329 if (wmemcmp(s, L"everyone@", 9) == 0)
1330 tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1336 /* Invalid tag, skip entry */
1339 } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1340 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1343 isint_w(name.start, name.end, &id);
1347 if (!is_nfs4_perms_w(field[1 + n].start,
1348 field[1 + n].end, &permset)) {
1349 /* Invalid NFSv4 perms, skip entry */
1353 if (!is_nfs4_flags_w(field[2 + n].start,
1354 field[2 + n].end, &permset)) {
1355 /* Invalid NFSv4 flags, skip entry */
1359 s = field[3 + n].start;
1360 len = field[3 + n].end - field[3 + n].start;
1363 if (wmemcmp(s, L"deny", 4) == 0)
1364 type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1365 } else if (len == 5) {
1366 if (wmemcmp(s, L"allow", 5) == 0)
1367 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1368 else if (wmemcmp(s, L"audit", 5) == 0)
1369 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1370 else if (wmemcmp(s, L"alarm", 5) == 0)
1371 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1374 /* Invalid entry type, skip entry */
1378 isint_w(field[4 + n].start, field[4 + n].end, &id);
1381 /* Add entry to the internal list. */
1382 r = archive_acl_add_entry_w_len(acl, type, permset,
1383 tag, id, name.start, name.end - name.start);
1384 if (r < ARCHIVE_WARN)
1386 if (r != ARCHIVE_OK)
1392 archive_acl_reset(acl, types);
1398 * Parse a string to a positive decimal integer. Returns true if
1399 * the string is non-empty and consists only of decimal digits,
1403 isint_w(const wchar_t *start, const wchar_t *end, int *result)
1408 while (start < end) {
1409 if (*start < '0' || *start > '9')
1411 if (n > (INT_MAX / 10) ||
1412 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1425 * Parse a string as a mode field. Returns true if
1426 * the string is non-empty and consists only of mode characters,
1430 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1440 case L'r': case L'R':
1441 *permset |= ARCHIVE_ENTRY_ACL_READ;
1443 case L'w': case L'W':
1444 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1446 case L'x': case L'X':
1447 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1459 * Parse a string as a NFS4 ACL permission field.
1460 * Returns true if the string is non-empty and consists only of NFS4 ACL
1461 * permission characters, false otherwise
1464 is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1466 const wchar_t *p = start;
1471 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1474 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1477 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1480 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1483 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1486 *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1489 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1492 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1495 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1498 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1501 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1504 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1507 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1510 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1522 * Parse a string as a NFS4 ACL flags field.
1523 * Returns true if the string is non-empty and consists only of NFS4 ACL
1524 * flag characters, false otherwise
1527 is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1529 const wchar_t *p = start;
1534 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1537 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1540 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1544 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1547 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1550 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1553 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1565 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
1566 * to point to just after the separator. *start points to the first
1567 * character of the matched text and *end just after the last
1568 * character of the matched identifier. In particular *end - *start
1569 * is the length of the field body, not including leading or trailing
1573 next_field_w(const wchar_t **wp, const wchar_t **start,
1574 const wchar_t **end, wchar_t *sep)
1576 /* Skip leading whitespace to find start of field. */
1577 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1582 /* Scan for the separator. */
1583 while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1589 /* Trim trailing whitespace to locate end of field. */
1591 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1596 /* Adjust scanner location. */
1602 * Parse an ACL text string.
1604 * The want_type argument may be one of the following:
1605 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1606 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1607 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1609 * POSIX.1e ACL entries prefixed with "default:" are treated as
1610 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1613 archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1614 int want_type, struct archive_string_conv *sc)
1622 int numfields, fields, n, r, sol, ret;
1623 int type, types, tag, permset, id;
1627 switch (want_type) {
1628 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1629 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1631 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1632 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1635 case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1639 return (ARCHIVE_FATAL);
1645 while (text != NULL && *text != '\0') {
1647 * Parse the fields out of the next entry,
1648 * advance 'text' to start of next entry.
1652 const char *start, *end;
1653 next_field(&text, &start, &end, &sep);
1654 if (fields < numfields) {
1655 field[fields].start = start;
1656 field[fields].end = end;
1659 } while (sep == ':');
1661 /* Set remaining fields to blank. */
1662 for (n = fields; n < numfields; ++n)
1663 field[n].start = field[n].end = NULL;
1665 if (field[0].start != NULL && *(field[0].start) == '#') {
1666 /* Comment, skip entry */
1674 name.start = name.end = NULL;
1676 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1679 * Default keyword "default:user::rwx"
1680 * if found, we have one more field
1682 * We also support old Solaris extension:
1683 * "defaultuser::rwx" is the default ACL corresponding
1684 * to "user::rwx", etc. valid only for first field
1687 len = field[0].end - field[0].start;
1688 if (*s == 'd' && (len == 1 || (len >= 7
1689 && memcmp((s + 1), "efault", 6) == 0))) {
1690 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1692 field[0].start += 7;
1698 /* Check for a numeric ID in field n+1 or n+3. */
1699 isint(field[n + 1].start, field[n + 1].end, &id);
1700 /* Field n+3 is optional. */
1701 if (id == -1 && fields > (n + 3))
1702 isint(field[n + 3].start, field[n + 3].end,
1707 st = field[n].start + 1;
1708 len = field[n].end - field[n].start;
1712 if (len == 1 || (len == 4
1713 && memcmp(st, "ser", 3) == 0))
1714 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1717 if (len == 1 || (len == 5
1718 && memcmp(st, "roup", 4) == 0))
1719 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1722 if (len == 1 || (len == 5
1723 && memcmp(st, "ther", 4) == 0))
1724 tag = ARCHIVE_ENTRY_ACL_OTHER;
1727 if (len == 1 || (len == 4
1728 && memcmp(st, "ask", 3) == 0))
1729 tag = ARCHIVE_ENTRY_ACL_MASK;
1736 case ARCHIVE_ENTRY_ACL_OTHER:
1737 case ARCHIVE_ENTRY_ACL_MASK:
1738 if (fields == (n + 2)
1739 && field[n + 1].start < field[n + 1].end
1740 && ismode(field[n + 1].start,
1741 field[n + 1].end, &permset)) {
1742 /* This is Solaris-style "other:rwx" */
1744 } else if (fields == (n + 3) &&
1745 field[n + 1].start < field[n + 1].end) {
1746 /* Invalid mask or other field */
1751 case ARCHIVE_ENTRY_ACL_USER_OBJ:
1752 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1754 field[n + 1].start < field[n + 1].end) {
1755 name = field[n + 1];
1756 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1757 tag = ARCHIVE_ENTRY_ACL_USER;
1759 tag = ARCHIVE_ENTRY_ACL_GROUP;
1763 /* Invalid tag, skip entry */
1769 * Without "default:" we expect mode in field 3
1770 * Exception: Solaris other and mask fields
1772 if (permset == 0 && !ismode(field[n + 2 - sol].start,
1773 field[n + 2 - sol].end, &permset)) {
1774 /* Invalid mode, skip entry */
1781 len = field[0].end - field[0].start;
1786 if (memcmp(s, "user", 4) == 0)
1787 tag = ARCHIVE_ENTRY_ACL_USER;
1790 if (memcmp(s, "group", 5) == 0)
1791 tag = ARCHIVE_ENTRY_ACL_GROUP;
1794 if (memcmp(s, "owner@", 6) == 0)
1795 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1796 else if (memcmp(s, "group@", 6) == 0)
1797 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1800 if (memcmp(s, "everyone@", 9) == 0)
1801 tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1808 /* Invalid tag, skip entry */
1811 } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1812 tag == ARCHIVE_ENTRY_ACL_GROUP) {
1815 isint(name.start, name.end, &id);
1819 if (!is_nfs4_perms(field[1 + n].start,
1820 field[1 + n].end, &permset)) {
1821 /* Invalid NFSv4 perms, skip entry */
1825 if (!is_nfs4_flags(field[2 + n].start,
1826 field[2 + n].end, &permset)) {
1827 /* Invalid NFSv4 flags, skip entry */
1831 s = field[3 + n].start;
1832 len = field[3 + n].end - field[3 + n].start;
1835 if (memcmp(s, "deny", 4) == 0)
1836 type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1837 } else if (len == 5) {
1838 if (memcmp(s, "allow", 5) == 0)
1839 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1840 else if (memcmp(s, "audit", 5) == 0)
1841 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1842 else if (memcmp(s, "alarm", 5) == 0)
1843 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1846 /* Invalid entry type, skip entry */
1850 isint(field[4 + n].start, field[4 + n].end,
1854 /* Add entry to the internal list. */
1855 r = archive_acl_add_entry_len_l(acl, type, permset,
1856 tag, id, name.start, name.end - name.start, sc);
1857 if (r < ARCHIVE_WARN)
1859 if (r != ARCHIVE_OK)
1865 archive_acl_reset(acl, types);
1871 * Parse a string to a positive decimal integer. Returns true if
1872 * the string is non-empty and consists only of decimal digits,
1876 isint(const char *start, const char *end, int *result)
1881 while (start < end) {
1882 if (*start < '0' || *start > '9')
1884 if (n > (INT_MAX / 10) ||
1885 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1898 * Parse a string as a mode field. Returns true if
1899 * the string is non-empty and consists only of mode characters,
1903 ismode(const char *start, const char *end, int *permset)
1914 *permset |= ARCHIVE_ENTRY_ACL_READ;
1917 *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1920 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1932 * Parse a string as a NFS4 ACL permission field.
1933 * Returns true if the string is non-empty and consists only of NFS4 ACL
1934 * permission characters, false otherwise
1937 is_nfs4_perms(const char *start, const char *end, int *permset)
1939 const char *p = start;
1944 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1947 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1950 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1953 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1956 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1959 *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1962 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1965 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1968 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1971 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1974 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1977 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1980 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1983 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1995 * Parse a string as a NFS4 ACL flags field.
1996 * Returns true if the string is non-empty and consists only of NFS4 ACL
1997 * flag characters, false otherwise
2000 is_nfs4_flags(const char *start, const char *end, int *permset)
2002 const char *p = start;
2007 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2010 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2013 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2017 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2020 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2023 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2026 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2038 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
2039 * to point to just after the separator. *start points to the first
2040 * character of the matched text and *end just after the last
2041 * character of the matched identifier. In particular *end - *start
2042 * is the length of the field body, not including leading or trailing
2046 next_field(const char **p, const char **start,
2047 const char **end, char *sep)
2049 /* Skip leading whitespace to find start of field. */
2050 while (**p == ' ' || **p == '\t' || **p == '\n') {
2055 /* Scan for the separator. */
2056 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
2061 /* Trim trailing whitespace to locate end of field. */
2063 while (**end == ' ' || **end == '\t' || **end == '\n') {
2068 /* Adjust scanner location. */