xref: /freebsd-src/contrib/libarchive/libarchive/archive_acl.c (revision bd66c1b43e33540205dbc1187c2f2a15c58b57ba)
16c95142eSMartin Matuska /*-
26c95142eSMartin Matuska  * Copyright (c) 2003-2010 Tim Kientzle
3e9ed7ea4SMartin Matuska  * Copyright (c) 2016 Martin Matuska
46c95142eSMartin Matuska  * All rights reserved.
56c95142eSMartin Matuska  *
66c95142eSMartin Matuska  * Redistribution and use in source and binary forms, with or without
76c95142eSMartin Matuska  * modification, are permitted provided that the following conditions
86c95142eSMartin Matuska  * are met:
96c95142eSMartin Matuska  * 1. Redistributions of source code must retain the above copyright
106c95142eSMartin Matuska  *    notice, this list of conditions and the following disclaimer.
116c95142eSMartin Matuska  * 2. Redistributions in binary form must reproduce the above copyright
126c95142eSMartin Matuska  *    notice, this list of conditions and the following disclaimer in the
136c95142eSMartin Matuska  *    documentation and/or other materials provided with the distribution.
146c95142eSMartin Matuska  *
156c95142eSMartin Matuska  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
166c95142eSMartin Matuska  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
176c95142eSMartin Matuska  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
186c95142eSMartin Matuska  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
196c95142eSMartin Matuska  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
206c95142eSMartin Matuska  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
216c95142eSMartin Matuska  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
226c95142eSMartin Matuska  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
236c95142eSMartin Matuska  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
246c95142eSMartin Matuska  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
256c95142eSMartin Matuska  */
266c95142eSMartin Matuska 
276c95142eSMartin Matuska #include "archive_platform.h"
286c95142eSMartin Matuska 
296c95142eSMartin Matuska #ifdef HAVE_ERRNO_H
306c95142eSMartin Matuska #include <errno.h>
316c95142eSMartin Matuska #endif
326c95142eSMartin Matuska #ifdef HAVE_LIMITS_H
336c95142eSMartin Matuska #include <limits.h>
346c95142eSMartin Matuska #endif
356c95142eSMartin Matuska #ifdef HAVE_WCHAR_H
366c95142eSMartin Matuska #include <wchar.h>
376c95142eSMartin Matuska #endif
386c95142eSMartin Matuska 
396c95142eSMartin Matuska #include "archive_acl_private.h"
406c95142eSMartin Matuska #include "archive_entry.h"
416c95142eSMartin Matuska #include "archive_private.h"
426c95142eSMartin Matuska 
436c95142eSMartin Matuska #undef max
446c95142eSMartin Matuska #define	max(a, b)	((a)>(b)?(a):(b))
456c95142eSMartin Matuska 
466c95142eSMartin Matuska #ifndef HAVE_WMEMCMP
476c95142eSMartin Matuska /* Good enough for simple equality testing, but not for sorting. */
486c95142eSMartin Matuska #define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
496c95142eSMartin Matuska #endif
506c95142eSMartin Matuska 
516c95142eSMartin Matuska static int	acl_special(struct archive_acl *acl,
526c95142eSMartin Matuska 		    int type, int permset, int tag);
536c95142eSMartin Matuska static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
546c95142eSMartin Matuska 		    int type, int permset, int tag, int id);
556c95142eSMartin Matuska static int	archive_acl_add_entry_len_l(struct archive_acl *acl,
566c95142eSMartin Matuska 		    int type, int permset, int tag, int id, const char *name,
576c95142eSMartin Matuska 		    size_t len, struct archive_string_conv *sc);
58e9ed7ea4SMartin Matuska static int	archive_acl_text_want_type(struct archive_acl *acl, int flags);
59*bd66c1b4SMartin Matuska static size_t	archive_acl_text_len(struct archive_acl *acl, int want_type,
60e9ed7ea4SMartin Matuska 		    int flags, int wide, struct archive *a,
61e9ed7ea4SMartin Matuska 		    struct archive_string_conv *sc);
626c95142eSMartin Matuska static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
636c95142eSMartin Matuska static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
64e9ed7ea4SMartin Matuska static int	is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
65e9ed7ea4SMartin Matuska 		    int *result);
66e9ed7ea4SMartin Matuska static int	is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
67e9ed7ea4SMartin Matuska 		    int *result);
686c95142eSMartin Matuska static void	next_field_w(const wchar_t **wp, const wchar_t **start,
696c95142eSMartin Matuska 		    const wchar_t **end, wchar_t *sep);
70e9ed7ea4SMartin Matuska static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
71e9ed7ea4SMartin Matuska 		    int tag, int flags, const wchar_t *wname, int perm, int id);
726c95142eSMartin Matuska static void	append_id_w(wchar_t **wp, int id);
736c95142eSMartin Matuska static int	isint(const char *start, const char *end, int *result);
746c95142eSMartin Matuska static int	ismode(const char *start, const char *end, int *result);
75e9ed7ea4SMartin Matuska static int	is_nfs4_flags(const char *start, const char *end,
76e9ed7ea4SMartin Matuska 		    int *result);
77e9ed7ea4SMartin Matuska static int	is_nfs4_perms(const char *start, const char *end,
78e9ed7ea4SMartin Matuska 		    int *result);
79*bd66c1b4SMartin Matuska static void	next_field(const char **p, size_t *l, const char **start,
806c95142eSMartin Matuska 		    const char **end, char *sep);
81e9ed7ea4SMartin Matuska static void	append_entry(char **p, const char *prefix, int type,
82e9ed7ea4SMartin Matuska 		    int tag, int flags, const char *name, int perm, int id);
836c95142eSMartin Matuska static void	append_id(char **p, int id);
846c95142eSMartin Matuska 
855d6770bdSMartin Matuska static const struct {
865d6770bdSMartin Matuska 	const int perm;
875d6770bdSMartin Matuska 	const char c;
885d6770bdSMartin Matuska 	const wchar_t wc;
895d6770bdSMartin Matuska } nfsv4_acl_perm_map[] = {
905d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
915d6770bdSMartin Matuska 	    L'r' },
925d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
935d6770bdSMartin Matuska 	    L'w' },
945d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
955d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
965d6770bdSMartin Matuska 	    'p', L'p' },
975d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
985d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
995d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
1005d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
1015d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
1025d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
1035d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
1045d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
1055d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
1065d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
1075d6770bdSMartin Matuska };
1085d6770bdSMartin Matuska 
1095d6770bdSMartin Matuska static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
1105d6770bdSMartin Matuska     sizeof(nfsv4_acl_perm_map[0]));
1115d6770bdSMartin Matuska 
1125d6770bdSMartin Matuska static const struct {
1135d6770bdSMartin Matuska 	const int perm;
1145d6770bdSMartin Matuska 	const char c;
1155d6770bdSMartin Matuska 	const wchar_t wc;
1165d6770bdSMartin Matuska } nfsv4_acl_flag_map[] = {
1175d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
1185d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
1195d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
1205d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
1215d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
1225d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
1235d6770bdSMartin Matuska 	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
1245d6770bdSMartin Matuska };
1255d6770bdSMartin Matuska 
1265d6770bdSMartin Matuska static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
1275d6770bdSMartin Matuska     sizeof(nfsv4_acl_flag_map[0]));
1285d6770bdSMartin Matuska 
1296c95142eSMartin Matuska void
1306c95142eSMartin Matuska archive_acl_clear(struct archive_acl *acl)
1316c95142eSMartin Matuska {
1326c95142eSMartin Matuska 	struct archive_acl_entry *ap;
1336c95142eSMartin Matuska 
1346c95142eSMartin Matuska 	while (acl->acl_head != NULL) {
1356c95142eSMartin Matuska 		ap = acl->acl_head->next;
1366c95142eSMartin Matuska 		archive_mstring_clean(&acl->acl_head->name);
1376c95142eSMartin Matuska 		free(acl->acl_head);
1386c95142eSMartin Matuska 		acl->acl_head = ap;
1396c95142eSMartin Matuska 	}
1406c95142eSMartin Matuska 	free(acl->acl_text_w);
1416c95142eSMartin Matuska 	acl->acl_text_w = NULL;
1426c95142eSMartin Matuska 	free(acl->acl_text);
1436c95142eSMartin Matuska 	acl->acl_text = NULL;
1446c95142eSMartin Matuska 	acl->acl_p = NULL;
145d5d08d29SMartin Matuska 	acl->acl_types = 0;
1466c95142eSMartin Matuska 	acl->acl_state = 0; /* Not counting. */
1476c95142eSMartin Matuska }
1486c95142eSMartin Matuska 
1496c95142eSMartin Matuska void
1506c95142eSMartin Matuska archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
1516c95142eSMartin Matuska {
1526c95142eSMartin Matuska 	struct archive_acl_entry *ap, *ap2;
1536c95142eSMartin Matuska 
1546c95142eSMartin Matuska 	archive_acl_clear(dest);
1556c95142eSMartin Matuska 
1566c95142eSMartin Matuska 	dest->mode = src->mode;
1576c95142eSMartin Matuska 	ap = src->acl_head;
1586c95142eSMartin Matuska 	while (ap != NULL) {
1596c95142eSMartin Matuska 		ap2 = acl_new_entry(dest,
1606c95142eSMartin Matuska 		    ap->type, ap->permset, ap->tag, ap->id);
1616c95142eSMartin Matuska 		if (ap2 != NULL)
1626c95142eSMartin Matuska 			archive_mstring_copy(&ap2->name, &ap->name);
1636c95142eSMartin Matuska 		ap = ap->next;
1646c95142eSMartin Matuska 	}
1656c95142eSMartin Matuska }
1666c95142eSMartin Matuska 
1676c95142eSMartin Matuska int
1686c95142eSMartin Matuska archive_acl_add_entry(struct archive_acl *acl,
1696c95142eSMartin Matuska     int type, int permset, int tag, int id, const char *name)
1706c95142eSMartin Matuska {
1716c95142eSMartin Matuska 	struct archive_acl_entry *ap;
1726c95142eSMartin Matuska 
1736c95142eSMartin Matuska 	if (acl_special(acl, type, permset, tag) == 0)
1746c95142eSMartin Matuska 		return ARCHIVE_OK;
1756c95142eSMartin Matuska 	ap = acl_new_entry(acl, type, permset, tag, id);
1766c95142eSMartin Matuska 	if (ap == NULL) {
1776c95142eSMartin Matuska 		/* XXX Error XXX */
1786c95142eSMartin Matuska 		return ARCHIVE_FAILED;
1796c95142eSMartin Matuska 	}
1806c95142eSMartin Matuska 	if (name != NULL  &&  *name != '\0')
1816c95142eSMartin Matuska 		archive_mstring_copy_mbs(&ap->name, name);
1826c95142eSMartin Matuska 	else
1836c95142eSMartin Matuska 		archive_mstring_clean(&ap->name);
1846c95142eSMartin Matuska 	return ARCHIVE_OK;
1856c95142eSMartin Matuska }
1866c95142eSMartin Matuska 
1876c95142eSMartin Matuska int
1886c95142eSMartin Matuska archive_acl_add_entry_w_len(struct archive_acl *acl,
1896c95142eSMartin Matuska     int type, int permset, int tag, int id, const wchar_t *name, size_t len)
1906c95142eSMartin Matuska {
1916c95142eSMartin Matuska 	struct archive_acl_entry *ap;
1926c95142eSMartin Matuska 
1936c95142eSMartin Matuska 	if (acl_special(acl, type, permset, tag) == 0)
1946c95142eSMartin Matuska 		return ARCHIVE_OK;
1956c95142eSMartin Matuska 	ap = acl_new_entry(acl, type, permset, tag, id);
1966c95142eSMartin Matuska 	if (ap == NULL) {
1976c95142eSMartin Matuska 		/* XXX Error XXX */
1986c95142eSMartin Matuska 		return ARCHIVE_FAILED;
1996c95142eSMartin Matuska 	}
2006c95142eSMartin Matuska 	if (name != NULL  &&  *name != L'\0' && len > 0)
2016c95142eSMartin Matuska 		archive_mstring_copy_wcs_len(&ap->name, name, len);
2026c95142eSMartin Matuska 	else
2036c95142eSMartin Matuska 		archive_mstring_clean(&ap->name);
2046c95142eSMartin Matuska 	return ARCHIVE_OK;
2056c95142eSMartin Matuska }
2066c95142eSMartin Matuska 
2076c95142eSMartin Matuska static int
2086c95142eSMartin Matuska archive_acl_add_entry_len_l(struct archive_acl *acl,
2096c95142eSMartin Matuska     int type, int permset, int tag, int id, const char *name, size_t len,
2106c95142eSMartin Matuska     struct archive_string_conv *sc)
2116c95142eSMartin Matuska {
2126c95142eSMartin Matuska 	struct archive_acl_entry *ap;
2136c95142eSMartin Matuska 	int r;
2146c95142eSMartin Matuska 
2156c95142eSMartin Matuska 	if (acl_special(acl, type, permset, tag) == 0)
2166c95142eSMartin Matuska 		return ARCHIVE_OK;
2176c95142eSMartin Matuska 	ap = acl_new_entry(acl, type, permset, tag, id);
2186c95142eSMartin Matuska 	if (ap == NULL) {
2196c95142eSMartin Matuska 		/* XXX Error XXX */
2206c95142eSMartin Matuska 		return ARCHIVE_FAILED;
2216c95142eSMartin Matuska 	}
2226c95142eSMartin Matuska 	if (name != NULL  &&  *name != '\0' && len > 0) {
2236c95142eSMartin Matuska 		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
2246c95142eSMartin Matuska 	} else {
2256c95142eSMartin Matuska 		r = 0;
2266c95142eSMartin Matuska 		archive_mstring_clean(&ap->name);
2276c95142eSMartin Matuska 	}
2286c95142eSMartin Matuska 	if (r == 0)
2296c95142eSMartin Matuska 		return (ARCHIVE_OK);
2306c95142eSMartin Matuska 	else if (errno == ENOMEM)
2316c95142eSMartin Matuska 		return (ARCHIVE_FATAL);
2326c95142eSMartin Matuska 	else
2336c95142eSMartin Matuska 		return (ARCHIVE_WARN);
2346c95142eSMartin Matuska }
2356c95142eSMartin Matuska 
2366c95142eSMartin Matuska /*
2376c95142eSMartin Matuska  * If this ACL entry is part of the standard POSIX permissions set,
2386c95142eSMartin Matuska  * store the permissions in the stat structure and return zero.
2396c95142eSMartin Matuska  */
2406c95142eSMartin Matuska static int
2416c95142eSMartin Matuska acl_special(struct archive_acl *acl, int type, int permset, int tag)
2426c95142eSMartin Matuska {
2436c95142eSMartin Matuska 	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
2446c95142eSMartin Matuska 	    && ((permset & ~007) == 0)) {
2456c95142eSMartin Matuska 		switch (tag) {
2466c95142eSMartin Matuska 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
2476c95142eSMartin Matuska 			acl->mode &= ~0700;
2486c95142eSMartin Matuska 			acl->mode |= (permset & 7) << 6;
2496c95142eSMartin Matuska 			return (0);
2506c95142eSMartin Matuska 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
2516c95142eSMartin Matuska 			acl->mode &= ~0070;
2526c95142eSMartin Matuska 			acl->mode |= (permset & 7) << 3;
2536c95142eSMartin Matuska 			return (0);
2546c95142eSMartin Matuska 		case ARCHIVE_ENTRY_ACL_OTHER:
2556c95142eSMartin Matuska 			acl->mode &= ~0007;
2566c95142eSMartin Matuska 			acl->mode |= permset & 7;
2576c95142eSMartin Matuska 			return (0);
2586c95142eSMartin Matuska 		}
2596c95142eSMartin Matuska 	}
2606c95142eSMartin Matuska 	return (1);
2616c95142eSMartin Matuska }
2626c95142eSMartin Matuska 
2636c95142eSMartin Matuska /*
2646c95142eSMartin Matuska  * Allocate and populate a new ACL entry with everything but the
2656c95142eSMartin Matuska  * name.
2666c95142eSMartin Matuska  */
2676c95142eSMartin Matuska static struct archive_acl_entry *
2686c95142eSMartin Matuska acl_new_entry(struct archive_acl *acl,
2696c95142eSMartin Matuska     int type, int permset, int tag, int id)
2706c95142eSMartin Matuska {
2716c95142eSMartin Matuska 	struct archive_acl_entry *ap, *aq;
2726c95142eSMartin Matuska 
2736c95142eSMartin Matuska 	/* Type argument must be a valid NFS4 or POSIX.1e type.
2746c95142eSMartin Matuska 	 * The type must agree with anything already set and
2756c95142eSMartin Matuska 	 * the permset must be compatible. */
2766c95142eSMartin Matuska 	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
2776c95142eSMartin Matuska 		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
2786c95142eSMartin Matuska 			return (NULL);
2796c95142eSMartin Matuska 		}
2806c95142eSMartin Matuska 		if (permset &
2816c95142eSMartin Matuska 		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
2826c95142eSMartin Matuska 			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
2836c95142eSMartin Matuska 			return (NULL);
2846c95142eSMartin Matuska 		}
2856c95142eSMartin Matuska 	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
2866c95142eSMartin Matuska 		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
2876c95142eSMartin Matuska 			return (NULL);
2886c95142eSMartin Matuska 		}
2896c95142eSMartin Matuska 		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
2906c95142eSMartin Matuska 			return (NULL);
2916c95142eSMartin Matuska 		}
2926c95142eSMartin Matuska 	} else {
2936c95142eSMartin Matuska 		return (NULL);
2946c95142eSMartin Matuska 	}
2956c95142eSMartin Matuska 
2966c95142eSMartin Matuska 	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
2976c95142eSMartin Matuska 	switch (tag) {
2986c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_USER:
2996c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
3006c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_GROUP:
3016c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
3026c95142eSMartin Matuska 		/* Tags valid in both NFS4 and POSIX.1e */
3036c95142eSMartin Matuska 		break;
3046c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_MASK:
3056c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_OTHER:
3066c95142eSMartin Matuska 		/* Tags valid only in POSIX.1e. */
3076c95142eSMartin Matuska 		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
3086c95142eSMartin Matuska 			return (NULL);
3096c95142eSMartin Matuska 		}
3106c95142eSMartin Matuska 		break;
3116c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_EVERYONE:
3126c95142eSMartin Matuska 		/* Tags valid only in NFS4. */
3136c95142eSMartin Matuska 		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
3146c95142eSMartin Matuska 			return (NULL);
3156c95142eSMartin Matuska 		}
3166c95142eSMartin Matuska 		break;
3176c95142eSMartin Matuska 	default:
3186c95142eSMartin Matuska 		/* No other values are valid. */
3196c95142eSMartin Matuska 		return (NULL);
3206c95142eSMartin Matuska 	}
3216c95142eSMartin Matuska 
3226c95142eSMartin Matuska 	free(acl->acl_text_w);
3236c95142eSMartin Matuska 	acl->acl_text_w = NULL;
3246c95142eSMartin Matuska 	free(acl->acl_text);
3256c95142eSMartin Matuska 	acl->acl_text = NULL;
3266c95142eSMartin Matuska 
3276a414569SMartin Matuska 	/*
3286a414569SMartin Matuska 	 * If there's a matching entry already in the list, overwrite it.
3296a414569SMartin Matuska 	 * NFSv4 entries may be repeated and are not overwritten.
3306a414569SMartin Matuska 	 *
3316a414569SMartin Matuska 	 * TODO: compare names of no id is provided (needs more rework)
3326a414569SMartin Matuska 	 */
3336c95142eSMartin Matuska 	ap = acl->acl_head;
3346c95142eSMartin Matuska 	aq = NULL;
3356c95142eSMartin Matuska 	while (ap != NULL) {
3366a414569SMartin Matuska 		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
3376a414569SMartin Matuska 		    ap->type == type && ap->tag == tag && ap->id == id) {
338d5d08d29SMartin Matuska 			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
339d5d08d29SMartin Matuska 			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {
3406c95142eSMartin Matuska 				ap->permset = permset;
3416c95142eSMartin Matuska 				return (ap);
3426c95142eSMartin Matuska 			}
343d5d08d29SMartin Matuska 		}
3446c95142eSMartin Matuska 		aq = ap;
3456c95142eSMartin Matuska 		ap = ap->next;
3466c95142eSMartin Matuska 	}
3476c95142eSMartin Matuska 
3486c95142eSMartin Matuska 	/* Add a new entry to the end of the list. */
349*bd66c1b4SMartin Matuska 	ap = calloc(1, sizeof(*ap));
3506c95142eSMartin Matuska 	if (ap == NULL)
3516c95142eSMartin Matuska 		return (NULL);
3526c95142eSMartin Matuska 	if (aq == NULL)
3536c95142eSMartin Matuska 		acl->acl_head = ap;
3546c95142eSMartin Matuska 	else
3556c95142eSMartin Matuska 		aq->next = ap;
3566c95142eSMartin Matuska 	ap->type = type;
3576c95142eSMartin Matuska 	ap->tag = tag;
3586c95142eSMartin Matuska 	ap->id = id;
3596c95142eSMartin Matuska 	ap->permset = permset;
3606c95142eSMartin Matuska 	acl->acl_types |= type;
3616c95142eSMartin Matuska 	return (ap);
3626c95142eSMartin Matuska }
3636c95142eSMartin Matuska 
3646c95142eSMartin Matuska /*
3656c95142eSMartin Matuska  * Return a count of entries matching "want_type".
3666c95142eSMartin Matuska  */
3676c95142eSMartin Matuska int
3686c95142eSMartin Matuska archive_acl_count(struct archive_acl *acl, int want_type)
3696c95142eSMartin Matuska {
3706c95142eSMartin Matuska 	int count;
3716c95142eSMartin Matuska 	struct archive_acl_entry *ap;
3726c95142eSMartin Matuska 
3736c95142eSMartin Matuska 	count = 0;
3746c95142eSMartin Matuska 	ap = acl->acl_head;
3756c95142eSMartin Matuska 	while (ap != NULL) {
3766c95142eSMartin Matuska 		if ((ap->type & want_type) != 0)
3776c95142eSMartin Matuska 			count++;
3786c95142eSMartin Matuska 		ap = ap->next;
3796c95142eSMartin Matuska 	}
3806c95142eSMartin Matuska 
3816c95142eSMartin Matuska 	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
3826c95142eSMartin Matuska 		count += 3;
3836c95142eSMartin Matuska 	return (count);
3846c95142eSMartin Matuska }
3856c95142eSMartin Matuska 
3866c95142eSMartin Matuska /*
3879f3de9e2SMartin Matuska  * Return a bitmask of stored ACL types in an ACL list
3889f3de9e2SMartin Matuska  */
3899f3de9e2SMartin Matuska int
3909f3de9e2SMartin Matuska archive_acl_types(struct archive_acl *acl)
3919f3de9e2SMartin Matuska {
3929f3de9e2SMartin Matuska 	return (acl->acl_types);
3939f3de9e2SMartin Matuska }
3949f3de9e2SMartin Matuska 
3959f3de9e2SMartin Matuska /*
3966c95142eSMartin Matuska  * Prepare for reading entries from the ACL data.  Returns a count
3976c95142eSMartin Matuska  * of entries matching "want_type", or zero if there are no
3986c95142eSMartin Matuska  * non-extended ACL entries of that type.
3996c95142eSMartin Matuska  */
4006c95142eSMartin Matuska int
4016c95142eSMartin Matuska archive_acl_reset(struct archive_acl *acl, int want_type)
4026c95142eSMartin Matuska {
4036c95142eSMartin Matuska 	int count, cutoff;
4046c95142eSMartin Matuska 
4056c95142eSMartin Matuska 	count = archive_acl_count(acl, want_type);
4066c95142eSMartin Matuska 
4076c95142eSMartin Matuska 	/*
4086c95142eSMartin Matuska 	 * If the only entries are the three standard ones,
4096c95142eSMartin Matuska 	 * then don't return any ACL data.  (In this case,
4106c95142eSMartin Matuska 	 * client can just use chmod(2) to set permissions.)
4116c95142eSMartin Matuska 	 */
4126c95142eSMartin Matuska 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
4136c95142eSMartin Matuska 		cutoff = 3;
4146c95142eSMartin Matuska 	else
4156c95142eSMartin Matuska 		cutoff = 0;
4166c95142eSMartin Matuska 
4176c95142eSMartin Matuska 	if (count > cutoff)
4186c95142eSMartin Matuska 		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
4196c95142eSMartin Matuska 	else
4206c95142eSMartin Matuska 		acl->acl_state = 0;
4216c95142eSMartin Matuska 	acl->acl_p = acl->acl_head;
4226c95142eSMartin Matuska 	return (count);
4236c95142eSMartin Matuska }
4246c95142eSMartin Matuska 
4256c95142eSMartin Matuska 
4266c95142eSMartin Matuska /*
4276c95142eSMartin Matuska  * Return the next ACL entry in the list.  Fake entries for the
4286c95142eSMartin Matuska  * standard permissions and include them in the returned list.
4296c95142eSMartin Matuska  */
4306c95142eSMartin Matuska int
431e9ed7ea4SMartin Matuska archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
432e9ed7ea4SMartin Matuska     int *type, int *permset, int *tag, int *id, const char **name)
4336c95142eSMartin Matuska {
4346c95142eSMartin Matuska 	*name = NULL;
4356c95142eSMartin Matuska 	*id = -1;
4366c95142eSMartin Matuska 
4376c95142eSMartin Matuska 	/*
4386c95142eSMartin Matuska 	 * The acl_state is either zero (no entries available), -1
4396c95142eSMartin Matuska 	 * (reading from list), or an entry type (retrieve that type
4406c95142eSMartin Matuska 	 * from ae_stat.aest_mode).
4416c95142eSMartin Matuska 	 */
4426c95142eSMartin Matuska 	if (acl->acl_state == 0)
4436c95142eSMartin Matuska 		return (ARCHIVE_WARN);
4446c95142eSMartin Matuska 
4456c95142eSMartin Matuska 	/* The first three access entries are special. */
4466c95142eSMartin Matuska 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
4476c95142eSMartin Matuska 		switch (acl->acl_state) {
4486c95142eSMartin Matuska 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
4496c95142eSMartin Matuska 			*permset = (acl->mode >> 6) & 7;
4506c95142eSMartin Matuska 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
4516c95142eSMartin Matuska 			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
4526c95142eSMartin Matuska 			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
4536c95142eSMartin Matuska 			return (ARCHIVE_OK);
4546c95142eSMartin Matuska 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
4556c95142eSMartin Matuska 			*permset = (acl->mode >> 3) & 7;
4566c95142eSMartin Matuska 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
4576c95142eSMartin Matuska 			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
4586c95142eSMartin Matuska 			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
4596c95142eSMartin Matuska 			return (ARCHIVE_OK);
4606c95142eSMartin Matuska 		case ARCHIVE_ENTRY_ACL_OTHER:
4616c95142eSMartin Matuska 			*permset = acl->mode & 7;
4626c95142eSMartin Matuska 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
4636c95142eSMartin Matuska 			*tag = ARCHIVE_ENTRY_ACL_OTHER;
4646c95142eSMartin Matuska 			acl->acl_state = -1;
4656c95142eSMartin Matuska 			acl->acl_p = acl->acl_head;
4666c95142eSMartin Matuska 			return (ARCHIVE_OK);
4676c95142eSMartin Matuska 		default:
4686c95142eSMartin Matuska 			break;
4696c95142eSMartin Matuska 		}
4706c95142eSMartin Matuska 	}
4716c95142eSMartin Matuska 
4726c95142eSMartin Matuska 	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
4736c95142eSMartin Matuska 		acl->acl_p = acl->acl_p->next;
4746c95142eSMartin Matuska 	if (acl->acl_p == NULL) {
4756c95142eSMartin Matuska 		acl->acl_state = 0;
4766c95142eSMartin Matuska 		*type = 0;
4776c95142eSMartin Matuska 		*permset = 0;
4786c95142eSMartin Matuska 		*tag = 0;
4796c95142eSMartin Matuska 		*id = -1;
4806c95142eSMartin Matuska 		*name = NULL;
4816c95142eSMartin Matuska 		return (ARCHIVE_EOF); /* End of ACL entries. */
4826c95142eSMartin Matuska 	}
4836c95142eSMartin Matuska 	*type = acl->acl_p->type;
4846c95142eSMartin Matuska 	*permset = acl->acl_p->permset;
4856c95142eSMartin Matuska 	*tag = acl->acl_p->tag;
4866c95142eSMartin Matuska 	*id = acl->acl_p->id;
487fd082e96SMartin Matuska 	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
488fd082e96SMartin Matuska 		if (errno == ENOMEM)
489fd082e96SMartin Matuska 			return (ARCHIVE_FATAL);
4906c95142eSMartin Matuska 		*name = NULL;
491fd082e96SMartin Matuska 	}
4926c95142eSMartin Matuska 	acl->acl_p = acl->acl_p->next;
4936c95142eSMartin Matuska 	return (ARCHIVE_OK);
4946c95142eSMartin Matuska }
4956c95142eSMartin Matuska 
4966c95142eSMartin Matuska /*
497e9ed7ea4SMartin Matuska  * Determine what type of ACL do we want
4986c95142eSMartin Matuska  */
499e9ed7ea4SMartin Matuska static int
500e9ed7ea4SMartin Matuska archive_acl_text_want_type(struct archive_acl *acl, int flags)
501e9ed7ea4SMartin Matuska {
502e9ed7ea4SMartin Matuska 	int want_type;
503e9ed7ea4SMartin Matuska 
504e9ed7ea4SMartin Matuska 	/* Check if ACL is NFSv4 */
505e9ed7ea4SMartin Matuska 	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
506e9ed7ea4SMartin Matuska 		/* NFSv4 should never mix with POSIX.1e */
507e9ed7ea4SMartin Matuska 		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
508e9ed7ea4SMartin Matuska 			return (0);
509e9ed7ea4SMartin Matuska 		else
510e9ed7ea4SMartin Matuska 			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
511e9ed7ea4SMartin Matuska 	}
512e9ed7ea4SMartin Matuska 
513e9ed7ea4SMartin Matuska 	/* Now deal with POSIX.1e ACLs */
514e9ed7ea4SMartin Matuska 
515e9ed7ea4SMartin Matuska 	want_type = 0;
516e9ed7ea4SMartin Matuska 	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
517e9ed7ea4SMartin Matuska 		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
518e9ed7ea4SMartin Matuska 	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
519e9ed7ea4SMartin Matuska 		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
520e9ed7ea4SMartin Matuska 
521e9ed7ea4SMartin Matuska 	/* By default we want both access and default ACLs */
522e9ed7ea4SMartin Matuska 	if (want_type == 0)
523e9ed7ea4SMartin Matuska 		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
524e9ed7ea4SMartin Matuska 
525e9ed7ea4SMartin Matuska 	return (want_type);
526e9ed7ea4SMartin Matuska }
527e9ed7ea4SMartin Matuska 
528e9ed7ea4SMartin Matuska /*
529e9ed7ea4SMartin Matuska  * Calculate ACL text string length
530e9ed7ea4SMartin Matuska  */
531*bd66c1b4SMartin Matuska static size_t
532e9ed7ea4SMartin Matuska archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
533e9ed7ea4SMartin Matuska     int wide, struct archive *a, struct archive_string_conv *sc) {
534e9ed7ea4SMartin Matuska 	struct archive_acl_entry *ap;
535e9ed7ea4SMartin Matuska 	const char *name;
536e9ed7ea4SMartin Matuska 	const wchar_t *wname;
537e9ed7ea4SMartin Matuska 	int count, idlen, tmp, r;
538*bd66c1b4SMartin Matuska 	size_t length;
539e9ed7ea4SMartin Matuska 	size_t len;
540e9ed7ea4SMartin Matuska 
541e9ed7ea4SMartin Matuska 	count = 0;
542e9ed7ea4SMartin Matuska 	length = 0;
543e9ed7ea4SMartin Matuska 	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
544e9ed7ea4SMartin Matuska 		if ((ap->type & want_type) == 0)
545e9ed7ea4SMartin Matuska 			continue;
546e9ed7ea4SMartin Matuska 		/*
547e9ed7ea4SMartin Matuska 		 * Filemode-mapping ACL entries are stored exclusively in
548e9ed7ea4SMartin Matuska 		 * ap->mode so they should not be in the list
549e9ed7ea4SMartin Matuska 		 */
550e9ed7ea4SMartin Matuska 		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
551e9ed7ea4SMartin Matuska 		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
552e9ed7ea4SMartin Matuska 		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
553e9ed7ea4SMartin Matuska 		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
554e9ed7ea4SMartin Matuska 			continue;
555e9ed7ea4SMartin Matuska 		count++;
556e9ed7ea4SMartin Matuska 		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
557e9ed7ea4SMartin Matuska 		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
558e9ed7ea4SMartin Matuska 			length += 8; /* "default:" */
559e9ed7ea4SMartin Matuska 		switch (ap->tag) {
560e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
561e9ed7ea4SMartin Matuska 			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
562e9ed7ea4SMartin Matuska 				length += 6; /* "owner@" */
563e9ed7ea4SMartin Matuska 				break;
564e9ed7ea4SMartin Matuska 			}
565e9ed7ea4SMartin Matuska 			/* FALLTHROUGH */
566e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_USER:
567e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_MASK:
568e9ed7ea4SMartin Matuska 			length += 4; /* "user", "mask" */
569e9ed7ea4SMartin Matuska 			break;
570e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
571e9ed7ea4SMartin Matuska 			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
572e9ed7ea4SMartin Matuska 				length += 6; /* "group@" */
573e9ed7ea4SMartin Matuska 				break;
574e9ed7ea4SMartin Matuska 			}
575e9ed7ea4SMartin Matuska 			/* FALLTHROUGH */
576e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_GROUP:
577e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_OTHER:
578e9ed7ea4SMartin Matuska 			length += 5; /* "group", "other" */
579e9ed7ea4SMartin Matuska 			break;
580e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_EVERYONE:
581e9ed7ea4SMartin Matuska 			length += 9; /* "everyone@" */
582e9ed7ea4SMartin Matuska 			break;
583e9ed7ea4SMartin Matuska 		}
584e9ed7ea4SMartin Matuska 		length += 1; /* colon after tag */
585e9ed7ea4SMartin Matuska 		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
586e9ed7ea4SMartin Matuska 		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
587e9ed7ea4SMartin Matuska 			if (wide) {
588e9ed7ea4SMartin Matuska 				r = archive_mstring_get_wcs(a, &ap->name,
589e9ed7ea4SMartin Matuska 				    &wname);
590e9ed7ea4SMartin Matuska 				if (r == 0 && wname != NULL)
591e9ed7ea4SMartin Matuska 					length += wcslen(wname);
592e9ed7ea4SMartin Matuska 				else if (r < 0 && errno == ENOMEM)
593e9ed7ea4SMartin Matuska 					return (0);
594e9ed7ea4SMartin Matuska 				else
595e9ed7ea4SMartin Matuska 					length += sizeof(uid_t) * 3 + 1;
596e9ed7ea4SMartin Matuska 			} else {
597c3afd20fSMartin Matuska 				r = archive_mstring_get_mbs_l(a, &ap->name, &name,
598e9ed7ea4SMartin Matuska 				    &len, sc);
599e9ed7ea4SMartin Matuska 				if (r != 0)
600e9ed7ea4SMartin Matuska 					return (0);
601e9ed7ea4SMartin Matuska 				if (len > 0 && name != NULL)
602e9ed7ea4SMartin Matuska 					length += len;
603e9ed7ea4SMartin Matuska 				else
604e9ed7ea4SMartin Matuska 					length += sizeof(uid_t) * 3 + 1;
605e9ed7ea4SMartin Matuska 			}
606e9ed7ea4SMartin Matuska 			length += 1; /* colon after user or group name */
607e9ed7ea4SMartin Matuska 		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
608e9ed7ea4SMartin Matuska 			length += 1; /* 2nd colon empty user,group or other */
609e9ed7ea4SMartin Matuska 
610e9ed7ea4SMartin Matuska 		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
611e9ed7ea4SMartin Matuska 		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
612e9ed7ea4SMartin Matuska 		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
613e9ed7ea4SMartin Matuska 		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
614e9ed7ea4SMartin Matuska 			/* Solaris has no colon after other: and mask: */
615e9ed7ea4SMartin Matuska 			length = length - 1;
616e9ed7ea4SMartin Matuska 		}
617e9ed7ea4SMartin Matuska 
618e9ed7ea4SMartin Matuska 		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
619e9ed7ea4SMartin Matuska 			/* rwxpdDaARWcCos:fdinSFI:deny */
620e9ed7ea4SMartin Matuska 			length += 27;
621e9ed7ea4SMartin Matuska 			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
622e9ed7ea4SMartin Matuska 				length += 1; /* allow, alarm, audit */
623e9ed7ea4SMartin Matuska 		} else
624e9ed7ea4SMartin Matuska 			length += 3; /* rwx */
625e9ed7ea4SMartin Matuska 
626e9ed7ea4SMartin Matuska 		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
627e9ed7ea4SMartin Matuska 		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
628e9ed7ea4SMartin Matuska 		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
629e9ed7ea4SMartin Matuska 			length += 1; /* colon */
630e9ed7ea4SMartin Matuska 			/* ID digit count */
631e9ed7ea4SMartin Matuska 			idlen = 1;
632e9ed7ea4SMartin Matuska 			tmp = ap->id;
633e9ed7ea4SMartin Matuska 			while (tmp > 9) {
634e9ed7ea4SMartin Matuska 				tmp = tmp / 10;
635e9ed7ea4SMartin Matuska 				idlen++;
636e9ed7ea4SMartin Matuska 			}
637e9ed7ea4SMartin Matuska 			length += idlen;
638e9ed7ea4SMartin Matuska 		}
639e9ed7ea4SMartin Matuska 		length ++; /* entry separator */
640e9ed7ea4SMartin Matuska 	}
641e9ed7ea4SMartin Matuska 
642e9ed7ea4SMartin Matuska 	/* Add filemode-mapping access entries to the length */
643e9ed7ea4SMartin Matuska 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
644e9ed7ea4SMartin Matuska 		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
645e9ed7ea4SMartin Matuska 			/* "user::rwx\ngroup::rwx\nother:rwx\n" */
646e9ed7ea4SMartin Matuska 			length += 31;
647e9ed7ea4SMartin Matuska 		} else {
648e9ed7ea4SMartin Matuska 			/* "user::rwx\ngroup::rwx\nother::rwx\n" */
649e9ed7ea4SMartin Matuska 			length += 32;
650e9ed7ea4SMartin Matuska 		}
651e9ed7ea4SMartin Matuska 	} else if (count == 0)
652e9ed7ea4SMartin Matuska 		return (0);
653e9ed7ea4SMartin Matuska 
654e9ed7ea4SMartin Matuska 	/* The terminating character is included in count */
655e9ed7ea4SMartin Matuska 	return (length);
656e9ed7ea4SMartin Matuska }
657e9ed7ea4SMartin Matuska 
658e9ed7ea4SMartin Matuska /*
659e9ed7ea4SMartin Matuska  * Generate a wide text version of the ACL. The flags parameter controls
660e9ed7ea4SMartin Matuska  * the type and style of the generated ACL.
661e9ed7ea4SMartin Matuska  */
662e9ed7ea4SMartin Matuska wchar_t *
663e9ed7ea4SMartin Matuska archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
664e9ed7ea4SMartin Matuska     struct archive *a)
6656c95142eSMartin Matuska {
6666c95142eSMartin Matuska 	int count;
667*bd66c1b4SMartin Matuska 	size_t length;
668e9ed7ea4SMartin Matuska 	size_t len;
6696c95142eSMartin Matuska 	const wchar_t *wname;
6706c95142eSMartin Matuska 	const wchar_t *prefix;
6716c95142eSMartin Matuska 	wchar_t separator;
6726c95142eSMartin Matuska 	struct archive_acl_entry *ap;
673e9ed7ea4SMartin Matuska 	int id, r, want_type;
674e9ed7ea4SMartin Matuska 	wchar_t *wp, *ws;
6756c95142eSMartin Matuska 
676e9ed7ea4SMartin Matuska 	want_type = archive_acl_text_want_type(acl, flags);
6776c95142eSMartin Matuska 
678e9ed7ea4SMartin Matuska 	/* Both NFSv4 and POSIX.1 types found */
679e9ed7ea4SMartin Matuska 	if (want_type == 0)
680e9ed7ea4SMartin Matuska 		return (NULL);
681e9ed7ea4SMartin Matuska 
682e9ed7ea4SMartin Matuska 	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
683e9ed7ea4SMartin Matuska 		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
684e9ed7ea4SMartin Matuska 
685e9ed7ea4SMartin Matuska 	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
686e9ed7ea4SMartin Matuska 
687e9ed7ea4SMartin Matuska 	if (length == 0)
688e9ed7ea4SMartin Matuska 		return (NULL);
689e9ed7ea4SMartin Matuska 
690e9ed7ea4SMartin Matuska 	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
6916c95142eSMartin Matuska 		separator = L',';
6926c95142eSMartin Matuska 	else
693e9ed7ea4SMartin Matuska 		separator = L'\n';
694c438d384SMartin Matuska 
6956c95142eSMartin Matuska 	/* Now, allocate the string and actually populate it. */
696*bd66c1b4SMartin Matuska 	wp = ws = malloc(length * sizeof(*wp));
697e9ed7ea4SMartin Matuska 	if (wp == NULL) {
698e9ed7ea4SMartin Matuska 		if (errno == ENOMEM)
699e9ed7ea4SMartin Matuska 			__archive_errx(1, "No memory");
700fd082e96SMartin Matuska 		return (NULL);
701e9ed7ea4SMartin Matuska 	}
7026c95142eSMartin Matuska 	count = 0;
703e9ed7ea4SMartin Matuska 
704e9ed7ea4SMartin Matuska 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
705e9ed7ea4SMartin Matuska 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
706e9ed7ea4SMartin Matuska 		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
707c438d384SMartin Matuska 		    acl->mode & 0700, -1);
708e9ed7ea4SMartin Matuska 		*wp++ = separator;
709e9ed7ea4SMartin Matuska 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
710e9ed7ea4SMartin Matuska 		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
711c438d384SMartin Matuska 		    acl->mode & 0070, -1);
712e9ed7ea4SMartin Matuska 		*wp++ = separator;
713e9ed7ea4SMartin Matuska 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
714e9ed7ea4SMartin Matuska 		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
715c438d384SMartin Matuska 		    acl->mode & 0007, -1);
716c4676089SMartin Matuska 		count += 3;
7176c95142eSMartin Matuska 	}
7186c95142eSMartin Matuska 
719e9ed7ea4SMartin Matuska 	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
720e9ed7ea4SMartin Matuska 		if ((ap->type & want_type) == 0)
721e9ed7ea4SMartin Matuska 			continue;
722e9ed7ea4SMartin Matuska 		/*
723e9ed7ea4SMartin Matuska 		 * Filemode-mapping ACL entries are stored exclusively in
724e9ed7ea4SMartin Matuska 		 * ap->mode so they should not be in the list
725e9ed7ea4SMartin Matuska 		 */
726e9ed7ea4SMartin Matuska 		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
727e9ed7ea4SMartin Matuska 		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
728e9ed7ea4SMartin Matuska 		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
729e9ed7ea4SMartin Matuska 		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
730e9ed7ea4SMartin Matuska 			continue;
731e9ed7ea4SMartin Matuska 		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
732e9ed7ea4SMartin Matuska 		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
7336c95142eSMartin Matuska 			prefix = L"default:";
7346c95142eSMartin Matuska 		else
7356c95142eSMartin Matuska 			prefix = NULL;
736fd082e96SMartin Matuska 		r = archive_mstring_get_wcs(a, &ap->name, &wname);
737fd082e96SMartin Matuska 		if (r == 0) {
7386c95142eSMartin Matuska 			if (count > 0)
7396c95142eSMartin Matuska 				*wp++ = separator;
7406c95142eSMartin Matuska 			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
7416c95142eSMartin Matuska 				id = ap->id;
7426c95142eSMartin Matuska 			else
7436c95142eSMartin Matuska 				id = -1;
744e9ed7ea4SMartin Matuska 			append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
745c438d384SMartin Matuska 			    wname, ap->permset, id);
7466c95142eSMartin Matuska 			count++;
747276f481dSMartin Matuska 		} else if (r < 0 && errno == ENOMEM) {
748276f481dSMartin Matuska 			free(ws);
749fd082e96SMartin Matuska 			return (NULL);
7506c95142eSMartin Matuska 		}
751276f481dSMartin Matuska 	}
7526c95142eSMartin Matuska 
753e9ed7ea4SMartin Matuska 	/* Add terminating character */
754e9ed7ea4SMartin Matuska 	*wp++ = L'\0';
7556c95142eSMartin Matuska 
756e9ed7ea4SMartin Matuska 	len = wcslen(ws);
757e9ed7ea4SMartin Matuska 
758*bd66c1b4SMartin Matuska 	if (len > length - 1)
759e9ed7ea4SMartin Matuska 		__archive_errx(1, "Buffer overrun");
760e9ed7ea4SMartin Matuska 
761e9ed7ea4SMartin Matuska 	if (text_len != NULL)
762e9ed7ea4SMartin Matuska 		*text_len = len;
763e9ed7ea4SMartin Matuska 
764e9ed7ea4SMartin Matuska 	return (ws);
765e9ed7ea4SMartin Matuska }
7666c95142eSMartin Matuska 
7676c95142eSMartin Matuska static void
7686c95142eSMartin Matuska append_id_w(wchar_t **wp, int id)
7696c95142eSMartin Matuska {
7706c95142eSMartin Matuska 	if (id < 0)
7716c95142eSMartin Matuska 		id = 0;
7726c95142eSMartin Matuska 	if (id > 9)
7736c95142eSMartin Matuska 		append_id_w(wp, id / 10);
7746c95142eSMartin Matuska 	*(*wp)++ = L"0123456789"[id % 10];
7756c95142eSMartin Matuska }
7766c95142eSMartin Matuska 
7776c95142eSMartin Matuska static void
778e9ed7ea4SMartin Matuska append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
779e9ed7ea4SMartin Matuska     int tag, int flags, const wchar_t *wname, int perm, int id)
7806c95142eSMartin Matuska {
7815d6770bdSMartin Matuska 	int i;
7825d6770bdSMartin Matuska 
7836c95142eSMartin Matuska 	if (prefix != NULL) {
7846c95142eSMartin Matuska 		wcscpy(*wp, prefix);
7856c95142eSMartin Matuska 		*wp += wcslen(*wp);
7866c95142eSMartin Matuska 	}
7876c95142eSMartin Matuska 	switch (tag) {
7886c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
7896c95142eSMartin Matuska 		wname = NULL;
7906c95142eSMartin Matuska 		id = -1;
791e9ed7ea4SMartin Matuska 		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
792e9ed7ea4SMartin Matuska 			wcscpy(*wp, L"owner@");
793e9ed7ea4SMartin Matuska 			break;
794e9ed7ea4SMartin Matuska 		}
7956c95142eSMartin Matuska 		/* FALLTHROUGH */
7966c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_USER:
7976c95142eSMartin Matuska 		wcscpy(*wp, L"user");
7986c95142eSMartin Matuska 		break;
7996c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
8006c95142eSMartin Matuska 		wname = NULL;
8016c95142eSMartin Matuska 		id = -1;
802e9ed7ea4SMartin Matuska 		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
803e9ed7ea4SMartin Matuska 			wcscpy(*wp, L"group@");
804e9ed7ea4SMartin Matuska 			break;
805e9ed7ea4SMartin Matuska 		}
8066c95142eSMartin Matuska 		/* FALLTHROUGH */
8076c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_GROUP:
8086c95142eSMartin Matuska 		wcscpy(*wp, L"group");
8096c95142eSMartin Matuska 		break;
8106c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_MASK:
8116c95142eSMartin Matuska 		wcscpy(*wp, L"mask");
8126c95142eSMartin Matuska 		wname = NULL;
8136c95142eSMartin Matuska 		id = -1;
8146c95142eSMartin Matuska 		break;
8156c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_OTHER:
8166c95142eSMartin Matuska 		wcscpy(*wp, L"other");
8176c95142eSMartin Matuska 		wname = NULL;
8186c95142eSMartin Matuska 		id = -1;
8196c95142eSMartin Matuska 		break;
820e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_EVERYONE:
821e9ed7ea4SMartin Matuska 		wcscpy(*wp, L"everyone@");
822e9ed7ea4SMartin Matuska 		wname = NULL;
823e9ed7ea4SMartin Matuska 		id = -1;
824e9ed7ea4SMartin Matuska 		break;
8256c95142eSMartin Matuska 	}
8266c95142eSMartin Matuska 	*wp += wcslen(*wp);
8276c95142eSMartin Matuska 	*(*wp)++ = L':';
828e9ed7ea4SMartin Matuska 	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
829e9ed7ea4SMartin Matuska 	    tag == ARCHIVE_ENTRY_ACL_USER ||
830e9ed7ea4SMartin Matuska 	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
8316c95142eSMartin Matuska 		if (wname != NULL) {
8326c95142eSMartin Matuska 			wcscpy(*wp, wname);
8336c95142eSMartin Matuska 			*wp += wcslen(*wp);
8346c95142eSMartin Matuska 		} else if (tag == ARCHIVE_ENTRY_ACL_USER
8356c95142eSMartin Matuska 		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
8366c95142eSMartin Matuska 			append_id_w(wp, id);
8372dbf8c4aSMartin Matuska 			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
8386c95142eSMartin Matuska 				id = -1;
8396c95142eSMartin Matuska 		}
840e9ed7ea4SMartin Matuska 		/* Solaris style has no second colon after other and mask */
841e9ed7ea4SMartin Matuska 		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
842e9ed7ea4SMartin Matuska 		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
843e9ed7ea4SMartin Matuska 		    && tag != ARCHIVE_ENTRY_ACL_MASK))
8446c95142eSMartin Matuska 			*(*wp)++ = L':';
845e9ed7ea4SMartin Matuska 	}
846e9ed7ea4SMartin Matuska 	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
847e9ed7ea4SMartin Matuska 		/* POSIX.1e ACL perms */
848c438d384SMartin Matuska 		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
849c438d384SMartin Matuska 		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
850c438d384SMartin Matuska 		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
851e9ed7ea4SMartin Matuska 	} else {
8525d6770bdSMartin Matuska 		/* NFSv4 ACL perms */
8535d6770bdSMartin Matuska 		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
8545d6770bdSMartin Matuska 			if (perm & nfsv4_acl_perm_map[i].perm)
8555d6770bdSMartin Matuska 				*(*wp)++ = nfsv4_acl_perm_map[i].wc;
8565d6770bdSMartin Matuska 			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
8575d6770bdSMartin Matuska 				*(*wp)++ = L'-';
8585d6770bdSMartin Matuska 		}
859e9ed7ea4SMartin Matuska 		*(*wp)++ = L':';
8605d6770bdSMartin Matuska 		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
8615d6770bdSMartin Matuska 			if (perm & nfsv4_acl_flag_map[i].perm)
8625d6770bdSMartin Matuska 				*(*wp)++ = nfsv4_acl_flag_map[i].wc;
8635d6770bdSMartin Matuska 			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
8645d6770bdSMartin Matuska 				*(*wp)++ = L'-';
8655d6770bdSMartin Matuska 		}
866e9ed7ea4SMartin Matuska 		*(*wp)++ = L':';
867e9ed7ea4SMartin Matuska 		switch (type) {
868e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
869e9ed7ea4SMartin Matuska 			wcscpy(*wp, L"allow");
870e9ed7ea4SMartin Matuska 			break;
871e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
872e9ed7ea4SMartin Matuska 			wcscpy(*wp, L"deny");
873e9ed7ea4SMartin Matuska 			break;
874e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
875e9ed7ea4SMartin Matuska 			wcscpy(*wp, L"audit");
876e9ed7ea4SMartin Matuska 			break;
877e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
878e9ed7ea4SMartin Matuska 			wcscpy(*wp, L"alarm");
879e9ed7ea4SMartin Matuska 			break;
880e9ed7ea4SMartin Matuska 		default:
881e9ed7ea4SMartin Matuska 			break;
882e9ed7ea4SMartin Matuska 		}
883e9ed7ea4SMartin Matuska 		*wp += wcslen(*wp);
884e9ed7ea4SMartin Matuska 	}
8856c95142eSMartin Matuska 	if (id != -1) {
8866c95142eSMartin Matuska 		*(*wp)++ = L':';
8876c95142eSMartin Matuska 		append_id_w(wp, id);
8886c95142eSMartin Matuska 	}
8896c95142eSMartin Matuska }
8906c95142eSMartin Matuska 
891e9ed7ea4SMartin Matuska /*
892e9ed7ea4SMartin Matuska  * Generate a text version of the ACL. The flags parameter controls
893e9ed7ea4SMartin Matuska  * the type and style of the generated ACL.
894e9ed7ea4SMartin Matuska  */
895e9ed7ea4SMartin Matuska char *
896e9ed7ea4SMartin Matuska archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
8976c95142eSMartin Matuska     struct archive_string_conv *sc)
8986c95142eSMartin Matuska {
8996c95142eSMartin Matuska 	int count;
900*bd66c1b4SMartin Matuska 	size_t length;
901e9ed7ea4SMartin Matuska 	size_t len;
9026c95142eSMartin Matuska 	const char *name;
9036c95142eSMartin Matuska 	const char *prefix;
9046c95142eSMartin Matuska 	char separator;
9056c95142eSMartin Matuska 	struct archive_acl_entry *ap;
906e9ed7ea4SMartin Matuska 	int id, r, want_type;
907e9ed7ea4SMartin Matuska 	char *p, *s;
9086c95142eSMartin Matuska 
909e9ed7ea4SMartin Matuska 	want_type = archive_acl_text_want_type(acl, flags);
9106c95142eSMartin Matuska 
911e9ed7ea4SMartin Matuska 	/* Both NFSv4 and POSIX.1 types found */
912e9ed7ea4SMartin Matuska 	if (want_type == 0)
913e9ed7ea4SMartin Matuska 		return (NULL);
914e9ed7ea4SMartin Matuska 
915e9ed7ea4SMartin Matuska 	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
916e9ed7ea4SMartin Matuska 		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
917e9ed7ea4SMartin Matuska 
918e9ed7ea4SMartin Matuska 	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
919e9ed7ea4SMartin Matuska 
920e9ed7ea4SMartin Matuska 	if (length == 0)
921e9ed7ea4SMartin Matuska 		return (NULL);
922e9ed7ea4SMartin Matuska 
923e9ed7ea4SMartin Matuska 	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
9246c95142eSMartin Matuska 		separator = ',';
9256c95142eSMartin Matuska 	else
926e9ed7ea4SMartin Matuska 		separator = '\n';
927c438d384SMartin Matuska 
9286c95142eSMartin Matuska 	/* Now, allocate the string and actually populate it. */
929*bd66c1b4SMartin Matuska 	p = s = malloc(length * sizeof(*p));
930e9ed7ea4SMartin Matuska 	if (p == NULL) {
931e9ed7ea4SMartin Matuska 		if (errno == ENOMEM)
932e9ed7ea4SMartin Matuska 			__archive_errx(1, "No memory");
933e9ed7ea4SMartin Matuska 		return (NULL);
934e9ed7ea4SMartin Matuska 	}
9356c95142eSMartin Matuska 	count = 0;
936e9ed7ea4SMartin Matuska 
937e9ed7ea4SMartin Matuska 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
938e9ed7ea4SMartin Matuska 		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
939e9ed7ea4SMartin Matuska 		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
940c438d384SMartin Matuska 		    acl->mode & 0700, -1);
941e9ed7ea4SMartin Matuska 		*p++ = separator;
942e9ed7ea4SMartin Matuska 		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
943e9ed7ea4SMartin Matuska 		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
944c438d384SMartin Matuska 		    acl->mode & 0070, -1);
945e9ed7ea4SMartin Matuska 		*p++ = separator;
946e9ed7ea4SMartin Matuska 		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
947e9ed7ea4SMartin Matuska 		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
948c438d384SMartin Matuska 		    acl->mode & 0007, -1);
9496c95142eSMartin Matuska 		count += 3;
950e9ed7ea4SMartin Matuska 	}
9516c95142eSMartin Matuska 
9526c95142eSMartin Matuska 	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
953e9ed7ea4SMartin Matuska 		if ((ap->type & want_type) == 0)
9546c95142eSMartin Matuska 			continue;
955e9ed7ea4SMartin Matuska 		/*
956e9ed7ea4SMartin Matuska 		 * Filemode-mapping ACL entries are stored exclusively in
957e9ed7ea4SMartin Matuska 		 * ap->mode so they should not be in the list
958e9ed7ea4SMartin Matuska 		 */
959e9ed7ea4SMartin Matuska 		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
960e9ed7ea4SMartin Matuska 		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
961e9ed7ea4SMartin Matuska 		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
962e9ed7ea4SMartin Matuska 		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
963e9ed7ea4SMartin Matuska 			continue;
964e9ed7ea4SMartin Matuska 		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
965e9ed7ea4SMartin Matuska 		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
966e9ed7ea4SMartin Matuska 			prefix = "default:";
967e9ed7ea4SMartin Matuska 		else
968e9ed7ea4SMartin Matuska 			prefix = NULL;
9696c95142eSMartin Matuska 		r = archive_mstring_get_mbs_l(
970c3afd20fSMartin Matuska 		    NULL, &ap->name, &name, &len, sc);
971276f481dSMartin Matuska 		if (r != 0) {
972276f481dSMartin Matuska 			free(s);
973e9ed7ea4SMartin Matuska 			return (NULL);
974276f481dSMartin Matuska 		}
975e9ed7ea4SMartin Matuska 		if (count > 0)
9766c95142eSMartin Matuska 			*p++ = separator;
977e9ed7ea4SMartin Matuska 		if (name == NULL ||
978e9ed7ea4SMartin Matuska 		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
9796c95142eSMartin Matuska 			id = ap->id;
98047af42f8SMartin Matuska 		} else {
9816c95142eSMartin Matuska 			id = -1;
98247af42f8SMartin Matuska 		}
983e9ed7ea4SMartin Matuska 		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
9846c95142eSMartin Matuska 		    ap->permset, id);
9856c95142eSMartin Matuska 		count++;
9866c95142eSMartin Matuska 	}
9876c95142eSMartin Matuska 
988e9ed7ea4SMartin Matuska 	/* Add terminating character */
989e9ed7ea4SMartin Matuska 	*p++ = '\0';
9906c95142eSMartin Matuska 
991e9ed7ea4SMartin Matuska 	len = strlen(s);
9926c95142eSMartin Matuska 
993*bd66c1b4SMartin Matuska 	if (len > length - 1)
994e9ed7ea4SMartin Matuska 		__archive_errx(1, "Buffer overrun");
995e9ed7ea4SMartin Matuska 
996e9ed7ea4SMartin Matuska 	if (text_len != NULL)
997e9ed7ea4SMartin Matuska 		*text_len = len;
998e9ed7ea4SMartin Matuska 
999e9ed7ea4SMartin Matuska 	return (s);
10006c95142eSMartin Matuska }
10016c95142eSMartin Matuska 
10026c95142eSMartin Matuska static void
10036c95142eSMartin Matuska append_id(char **p, int id)
10046c95142eSMartin Matuska {
10056c95142eSMartin Matuska 	if (id < 0)
10066c95142eSMartin Matuska 		id = 0;
10076c95142eSMartin Matuska 	if (id > 9)
10086c95142eSMartin Matuska 		append_id(p, id / 10);
10096c95142eSMartin Matuska 	*(*p)++ = "0123456789"[id % 10];
10106c95142eSMartin Matuska }
10116c95142eSMartin Matuska 
10126c95142eSMartin Matuska static void
1013e9ed7ea4SMartin Matuska append_entry(char **p, const char *prefix, int type,
1014e9ed7ea4SMartin Matuska     int tag, int flags, const char *name, int perm, int id)
10156c95142eSMartin Matuska {
10165d6770bdSMartin Matuska 	int i;
10175d6770bdSMartin Matuska 
10186c95142eSMartin Matuska 	if (prefix != NULL) {
10196c95142eSMartin Matuska 		strcpy(*p, prefix);
10206c95142eSMartin Matuska 		*p += strlen(*p);
10216c95142eSMartin Matuska 	}
10226c95142eSMartin Matuska 	switch (tag) {
10236c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
10246c95142eSMartin Matuska 		name = NULL;
10256c95142eSMartin Matuska 		id = -1;
1026e9ed7ea4SMartin Matuska 		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1027e9ed7ea4SMartin Matuska 			strcpy(*p, "owner@");
1028e9ed7ea4SMartin Matuska 			break;
1029e9ed7ea4SMartin Matuska 		}
10306c95142eSMartin Matuska 		/* FALLTHROUGH */
10316c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_USER:
10326c95142eSMartin Matuska 		strcpy(*p, "user");
10336c95142eSMartin Matuska 		break;
10346c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
10356c95142eSMartin Matuska 		name = NULL;
10366c95142eSMartin Matuska 		id = -1;
1037e9ed7ea4SMartin Matuska 		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1038e9ed7ea4SMartin Matuska 			strcpy(*p, "group@");
1039e9ed7ea4SMartin Matuska 			break;
1040e9ed7ea4SMartin Matuska 		}
10416c95142eSMartin Matuska 		/* FALLTHROUGH */
10426c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_GROUP:
10436c95142eSMartin Matuska 		strcpy(*p, "group");
10446c95142eSMartin Matuska 		break;
10456c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_MASK:
10466c95142eSMartin Matuska 		strcpy(*p, "mask");
10476c95142eSMartin Matuska 		name = NULL;
10486c95142eSMartin Matuska 		id = -1;
10496c95142eSMartin Matuska 		break;
10506c95142eSMartin Matuska 	case ARCHIVE_ENTRY_ACL_OTHER:
10516c95142eSMartin Matuska 		strcpy(*p, "other");
10526c95142eSMartin Matuska 		name = NULL;
10536c95142eSMartin Matuska 		id = -1;
10546c95142eSMartin Matuska 		break;
1055e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_EVERYONE:
1056e9ed7ea4SMartin Matuska 		strcpy(*p, "everyone@");
1057e9ed7ea4SMartin Matuska 		name = NULL;
1058e9ed7ea4SMartin Matuska 		id = -1;
1059e9ed7ea4SMartin Matuska 		break;
10606c95142eSMartin Matuska 	}
10616c95142eSMartin Matuska 	*p += strlen(*p);
10626c95142eSMartin Matuska 	*(*p)++ = ':';
1063e9ed7ea4SMartin Matuska 	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1064e9ed7ea4SMartin Matuska 	    tag == ARCHIVE_ENTRY_ACL_USER ||
1065e9ed7ea4SMartin Matuska 	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
10666c95142eSMartin Matuska 		if (name != NULL) {
10676c95142eSMartin Matuska 			strcpy(*p, name);
10686c95142eSMartin Matuska 			*p += strlen(*p);
10696c95142eSMartin Matuska 		} else if (tag == ARCHIVE_ENTRY_ACL_USER
10706c95142eSMartin Matuska 		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
10716c95142eSMartin Matuska 			append_id(p, id);
10722dbf8c4aSMartin Matuska 			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
10736c95142eSMartin Matuska 				id = -1;
10746c95142eSMartin Matuska 		}
1075e9ed7ea4SMartin Matuska 		/* Solaris style has no second colon after other and mask */
1076e9ed7ea4SMartin Matuska 		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1077e9ed7ea4SMartin Matuska 		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
1078e9ed7ea4SMartin Matuska 		    && tag != ARCHIVE_ENTRY_ACL_MASK))
10796c95142eSMartin Matuska 			*(*p)++ = ':';
1080e9ed7ea4SMartin Matuska 	}
1081e9ed7ea4SMartin Matuska 	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1082e9ed7ea4SMartin Matuska 		/* POSIX.1e ACL perms */
1083c438d384SMartin Matuska 		*(*p)++ = (perm & 0444) ? 'r' : '-';
1084c438d384SMartin Matuska 		*(*p)++ = (perm & 0222) ? 'w' : '-';
1085c438d384SMartin Matuska 		*(*p)++ = (perm & 0111) ? 'x' : '-';
1086e9ed7ea4SMartin Matuska 	} else {
10875d6770bdSMartin Matuska 		/* NFSv4 ACL perms */
10885d6770bdSMartin Matuska 		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
10895d6770bdSMartin Matuska 			if (perm & nfsv4_acl_perm_map[i].perm)
10905d6770bdSMartin Matuska 				*(*p)++ = nfsv4_acl_perm_map[i].c;
10915d6770bdSMartin Matuska 			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
10925d6770bdSMartin Matuska 				*(*p)++ = '-';
10935d6770bdSMartin Matuska 		}
1094e9ed7ea4SMartin Matuska 		*(*p)++ = ':';
10955d6770bdSMartin Matuska 		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
10965d6770bdSMartin Matuska 			if (perm & nfsv4_acl_flag_map[i].perm)
10975d6770bdSMartin Matuska 				*(*p)++ = nfsv4_acl_flag_map[i].c;
10985d6770bdSMartin Matuska 			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
10995d6770bdSMartin Matuska 				*(*p)++ = '-';
11005d6770bdSMartin Matuska 		}
1101e9ed7ea4SMartin Matuska 		*(*p)++ = ':';
1102e9ed7ea4SMartin Matuska 		switch (type) {
1103e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1104e9ed7ea4SMartin Matuska 			strcpy(*p, "allow");
1105e9ed7ea4SMartin Matuska 			break;
1106e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1107e9ed7ea4SMartin Matuska 			strcpy(*p, "deny");
1108e9ed7ea4SMartin Matuska 			break;
1109e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1110e9ed7ea4SMartin Matuska 			strcpy(*p, "audit");
1111e9ed7ea4SMartin Matuska 			break;
1112e9ed7ea4SMartin Matuska 		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1113e9ed7ea4SMartin Matuska 			strcpy(*p, "alarm");
1114e9ed7ea4SMartin Matuska 			break;
1115e9ed7ea4SMartin Matuska 		}
1116e9ed7ea4SMartin Matuska 		*p += strlen(*p);
1117e9ed7ea4SMartin Matuska 	}
11186c95142eSMartin Matuska 	if (id != -1) {
11196c95142eSMartin Matuska 		*(*p)++ = ':';
11206c95142eSMartin Matuska 		append_id(p, id);
11216c95142eSMartin Matuska 	}
11226c95142eSMartin Matuska }
11236c95142eSMartin Matuska 
11246c95142eSMartin Matuska /*
1125e9ed7ea4SMartin Matuska  * Parse a wide ACL text string.
1126e9ed7ea4SMartin Matuska  *
1127e9ed7ea4SMartin Matuska  * The want_type argument may be one of the following:
1128e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1129e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1130e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1131e9ed7ea4SMartin Matuska  *
1132e9ed7ea4SMartin Matuska  * POSIX.1e ACL entries prefixed with "default:" are treated as
1133e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
11346c95142eSMartin Matuska  */
11356c95142eSMartin Matuska int
1136e9ed7ea4SMartin Matuska archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1137e9ed7ea4SMartin Matuska     int want_type)
11386c95142eSMartin Matuska {
11396c95142eSMartin Matuska 	struct {
11406c95142eSMartin Matuska 		const wchar_t *start;
11416c95142eSMartin Matuska 		const wchar_t *end;
1142e9ed7ea4SMartin Matuska 	} field[6], name;
11436c95142eSMartin Matuska 
1144e9ed7ea4SMartin Matuska 	const wchar_t *s, *st;
1145e9ed7ea4SMartin Matuska 
11469f3de9e2SMartin Matuska 	int numfields, fields, n, r, sol, ret;
1147e9ed7ea4SMartin Matuska 	int type, types, tag, permset, id;
1148e9ed7ea4SMartin Matuska 	size_t len;
11496c95142eSMartin Matuska 	wchar_t sep;
11506c95142eSMartin Matuska 
1151e9ed7ea4SMartin Matuska 	ret = ARCHIVE_OK;
1152e9ed7ea4SMartin Matuska 	types = 0;
1153e9ed7ea4SMartin Matuska 
1154e9ed7ea4SMartin Matuska 	switch (want_type) {
1155e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1156e9ed7ea4SMartin Matuska 		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1157a2a3407cSMartin Matuska 		__LA_FALLTHROUGH;
1158e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1159e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1160e9ed7ea4SMartin Matuska 		numfields = 5;
1161e9ed7ea4SMartin Matuska 		break;
1162e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1163e9ed7ea4SMartin Matuska 		numfields = 6;
1164e9ed7ea4SMartin Matuska 		break;
1165e9ed7ea4SMartin Matuska 	default:
1166e9ed7ea4SMartin Matuska 		return (ARCHIVE_FATAL);
1167e9ed7ea4SMartin Matuska 	}
1168e9ed7ea4SMartin Matuska 
11696c95142eSMartin Matuska 	while (text != NULL && *text != L'\0') {
11706c95142eSMartin Matuska 		/*
11716c95142eSMartin Matuska 		 * Parse the fields out of the next entry,
11726c95142eSMartin Matuska 		 * advance 'text' to start of next entry.
11736c95142eSMartin Matuska 		 */
11746c95142eSMartin Matuska 		fields = 0;
11756c95142eSMartin Matuska 		do {
11766c95142eSMartin Matuska 			const wchar_t *start, *end;
11776c95142eSMartin Matuska 			next_field_w(&text, &start, &end, &sep);
1178e9ed7ea4SMartin Matuska 			if (fields < numfields) {
11796c95142eSMartin Matuska 				field[fields].start = start;
11806c95142eSMartin Matuska 				field[fields].end = end;
11816c95142eSMartin Matuska 			}
11826c95142eSMartin Matuska 			++fields;
11836c95142eSMartin Matuska 		} while (sep == L':');
11846c95142eSMartin Matuska 
11856c95142eSMartin Matuska 		/* Set remaining fields to blank. */
1186e9ed7ea4SMartin Matuska 		for (n = fields; n < numfields; ++n)
11876c95142eSMartin Matuska 			field[n].start = field[n].end = NULL;
11886c95142eSMartin Matuska 
1189e9ed7ea4SMartin Matuska 		if (field[0].start != NULL && *(field[0].start) == L'#') {
1190e9ed7ea4SMartin Matuska 			/* Comment, skip entry */
1191e9ed7ea4SMartin Matuska 			continue;
1192e9ed7ea4SMartin Matuska 		}
1193e9ed7ea4SMartin Matuska 
1194e9ed7ea4SMartin Matuska 		n = 0;
11959f3de9e2SMartin Matuska 		sol = 0;
11966c95142eSMartin Matuska 		id = -1;
1197e9ed7ea4SMartin Matuska 		permset = 0;
11986c95142eSMartin Matuska 		name.start = name.end = NULL;
1199e9ed7ea4SMartin Matuska 
1200e9ed7ea4SMartin Matuska 		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1201e9ed7ea4SMartin Matuska 			/* POSIX.1e ACLs */
1202e9ed7ea4SMartin Matuska 			/*
1203e9ed7ea4SMartin Matuska 			 * Default keyword "default:user::rwx"
1204e9ed7ea4SMartin Matuska 			 * if found, we have one more field
1205e9ed7ea4SMartin Matuska 			 *
1206e9ed7ea4SMartin Matuska 			 * We also support old Solaris extension:
1207e9ed7ea4SMartin Matuska 			 * "defaultuser::rwx" is the default ACL corresponding
1208e9ed7ea4SMartin Matuska 			 * to "user::rwx", etc. valid only for first field
1209e9ed7ea4SMartin Matuska 			 */
1210e9ed7ea4SMartin Matuska 			s = field[0].start;
1211e9ed7ea4SMartin Matuska 			len = field[0].end - field[0].start;
1212e9ed7ea4SMartin Matuska 			if (*s == L'd' && (len == 1 || (len >= 7
1213e9ed7ea4SMartin Matuska 			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
1214e9ed7ea4SMartin Matuska 				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1215e9ed7ea4SMartin Matuska 				if (len > 7)
1216e9ed7ea4SMartin Matuska 					field[0].start += 7;
1217e9ed7ea4SMartin Matuska 				else
1218e9ed7ea4SMartin Matuska 					n = 1;
12196c95142eSMartin Matuska 			} else
1220e9ed7ea4SMartin Matuska 				type = want_type;
1221e9ed7ea4SMartin Matuska 
1222e9ed7ea4SMartin Matuska 			/* Check for a numeric ID in field n+1 or n+3. */
1223e9ed7ea4SMartin Matuska 			isint_w(field[n + 1].start, field[n + 1].end, &id);
1224e9ed7ea4SMartin Matuska 			/* Field n+3 is optional. */
1225e9ed7ea4SMartin Matuska 			if (id == -1 && fields > n+3)
1226e9ed7ea4SMartin Matuska 				isint_w(field[n + 3].start, field[n + 3].end,
1227e9ed7ea4SMartin Matuska 				    &id);
1228e9ed7ea4SMartin Matuska 
1229e9ed7ea4SMartin Matuska 			tag = 0;
1230e9ed7ea4SMartin Matuska 			s = field[n].start;
1231e9ed7ea4SMartin Matuska 			st = field[n].start + 1;
1232e9ed7ea4SMartin Matuska 			len = field[n].end - field[n].start;
1233e9ed7ea4SMartin Matuska 
1234e9ed7ea4SMartin Matuska 			switch (*s) {
1235e9ed7ea4SMartin Matuska 			case L'u':
1236e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 4
1237e9ed7ea4SMartin Matuska 				    && wmemcmp(st, L"ser", 3) == 0))
12386c95142eSMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1239e9ed7ea4SMartin Matuska 				break;
1240e9ed7ea4SMartin Matuska 			case L'g':
1241e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 5
1242e9ed7ea4SMartin Matuska 				    && wmemcmp(st, L"roup", 4) == 0))
1243e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1244e9ed7ea4SMartin Matuska 				break;
1245e9ed7ea4SMartin Matuska 			case L'o':
1246e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 5
1247e9ed7ea4SMartin Matuska 				    && wmemcmp(st, L"ther", 4) == 0))
1248e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_OTHER;
1249e9ed7ea4SMartin Matuska 				break;
1250e9ed7ea4SMartin Matuska 			case L'm':
1251e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 4
1252e9ed7ea4SMartin Matuska 				    && wmemcmp(st, L"ask", 3) == 0))
1253e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_MASK;
1254e9ed7ea4SMartin Matuska 				break;
1255e9ed7ea4SMartin Matuska 			default:
1256e9ed7ea4SMartin Matuska 					break;
1257e9ed7ea4SMartin Matuska 			}
1258e9ed7ea4SMartin Matuska 
1259e9ed7ea4SMartin Matuska 			switch (tag) {
1260e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_OTHER:
1261e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_MASK:
1262e9ed7ea4SMartin Matuska 				if (fields == (n + 2)
1263e9ed7ea4SMartin Matuska 				    && field[n + 1].start < field[n + 1].end
1264e9ed7ea4SMartin Matuska 				    && ismode_w(field[n + 1].start,
1265e9ed7ea4SMartin Matuska 				    field[n + 1].end, &permset)) {
1266e9ed7ea4SMartin Matuska 					/* This is Solaris-style "other:rwx" */
12679f3de9e2SMartin Matuska 					sol = 1;
1268e9ed7ea4SMartin Matuska 				} else if (fields == (n + 3) &&
1269e9ed7ea4SMartin Matuska 				    field[n + 1].start < field[n + 1].end) {
1270e9ed7ea4SMartin Matuska 					/* Invalid mask or other field */
1271e9ed7ea4SMartin Matuska 					ret = ARCHIVE_WARN;
1272e9ed7ea4SMartin Matuska 					continue;
1273e9ed7ea4SMartin Matuska 				}
1274e9ed7ea4SMartin Matuska 				break;
1275e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1276e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1277e9ed7ea4SMartin Matuska 				if (id != -1 ||
1278e9ed7ea4SMartin Matuska 				    field[n + 1].start < field[n + 1].end) {
1279e9ed7ea4SMartin Matuska 					name = field[n + 1];
1280e9ed7ea4SMartin Matuska 					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1281e9ed7ea4SMartin Matuska 						tag = ARCHIVE_ENTRY_ACL_USER;
1282e9ed7ea4SMartin Matuska 					else
12836c95142eSMartin Matuska 						tag = ARCHIVE_ENTRY_ACL_GROUP;
1284e9ed7ea4SMartin Matuska 				}
1285e9ed7ea4SMartin Matuska 				break;
1286e9ed7ea4SMartin Matuska 			default:
1287e9ed7ea4SMartin Matuska 				/* Invalid tag, skip entry */
1288e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1289e9ed7ea4SMartin Matuska 				continue;
1290e9ed7ea4SMartin Matuska 			}
1291e9ed7ea4SMartin Matuska 
12929f3de9e2SMartin Matuska 			/*
12939f3de9e2SMartin Matuska 			 * Without "default:" we expect mode in field 2
12949f3de9e2SMartin Matuska 			 * Exception: Solaris other and mask fields
12959f3de9e2SMartin Matuska 			 */
12969f3de9e2SMartin Matuska 			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
12979f3de9e2SMartin Matuska 			    field[n + 2 - sol].end, &permset)) {
1298e9ed7ea4SMartin Matuska 				/* Invalid mode, skip entry */
1299e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1300e9ed7ea4SMartin Matuska 				continue;
1301e9ed7ea4SMartin Matuska 			}
1302e9ed7ea4SMartin Matuska 		} else {
1303e9ed7ea4SMartin Matuska 			/* NFS4 ACLs */
1304e9ed7ea4SMartin Matuska 			s = field[0].start;
1305e9ed7ea4SMartin Matuska 			len = field[0].end - field[0].start;
1306e9ed7ea4SMartin Matuska 			tag = 0;
1307e9ed7ea4SMartin Matuska 
1308e9ed7ea4SMartin Matuska 			switch (len) {
1309e9ed7ea4SMartin Matuska 			case 4:
1310e9ed7ea4SMartin Matuska 				if (wmemcmp(s, L"user", 4) == 0)
1311e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_USER;
1312e9ed7ea4SMartin Matuska 				break;
1313e9ed7ea4SMartin Matuska 			case 5:
1314e9ed7ea4SMartin Matuska 				if (wmemcmp(s, L"group", 5) == 0)
1315e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_GROUP;
1316e9ed7ea4SMartin Matuska 				break;
1317e9ed7ea4SMartin Matuska 			case 6:
1318e9ed7ea4SMartin Matuska 				if (wmemcmp(s, L"owner@", 6) == 0)
1319e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1320e9ed7ea4SMartin Matuska 				else if (wmemcmp(s, L"group@", len) == 0)
1321e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1322e9ed7ea4SMartin Matuska 				break;
1323e9ed7ea4SMartin Matuska 			case 9:
1324e9ed7ea4SMartin Matuska 				if (wmemcmp(s, L"everyone@", 9) == 0)
1325e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1326e9ed7ea4SMartin Matuska 			default:
1327e9ed7ea4SMartin Matuska 				break;
1328e9ed7ea4SMartin Matuska 			}
1329e9ed7ea4SMartin Matuska 
1330e9ed7ea4SMartin Matuska 			if (tag == 0) {
1331e9ed7ea4SMartin Matuska 				/* Invalid tag, skip entry */
1332e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1333e9ed7ea4SMartin Matuska 				continue;
1334e9ed7ea4SMartin Matuska 			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1335e9ed7ea4SMartin Matuska 			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1336e9ed7ea4SMartin Matuska 				n = 1;
13376c95142eSMartin Matuska 				name = field[1];
13382dbf8c4aSMartin Matuska 				isint_w(name.start, name.end, &id);
13396c95142eSMartin Matuska 			} else
1340e9ed7ea4SMartin Matuska 				n = 0;
1341e9ed7ea4SMartin Matuska 
1342e9ed7ea4SMartin Matuska 			if (!is_nfs4_perms_w(field[1 + n].start,
1343e9ed7ea4SMartin Matuska 			    field[1 + n].end, &permset)) {
1344e9ed7ea4SMartin Matuska 				/* Invalid NFSv4 perms, skip entry */
1345e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1346e9ed7ea4SMartin Matuska 				continue;
1347e9ed7ea4SMartin Matuska 			}
1348e9ed7ea4SMartin Matuska 			if (!is_nfs4_flags_w(field[2 + n].start,
1349e9ed7ea4SMartin Matuska 			    field[2 + n].end, &permset)) {
1350e9ed7ea4SMartin Matuska 				/* Invalid NFSv4 flags, skip entry */
1351e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1352e9ed7ea4SMartin Matuska 				continue;
1353e9ed7ea4SMartin Matuska 			}
1354e9ed7ea4SMartin Matuska 			s = field[3 + n].start;
1355e9ed7ea4SMartin Matuska 			len = field[3 + n].end - field[3 + n].start;
1356e9ed7ea4SMartin Matuska 			type = 0;
1357e9ed7ea4SMartin Matuska 			if (len == 4) {
1358e9ed7ea4SMartin Matuska 				if (wmemcmp(s, L"deny", 4) == 0)
1359e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1360e9ed7ea4SMartin Matuska 			} else if (len == 5) {
1361e9ed7ea4SMartin Matuska 				if (wmemcmp(s, L"allow", 5) == 0)
1362e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1363e9ed7ea4SMartin Matuska 				else if (wmemcmp(s, L"audit", 5) == 0)
1364e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1365e9ed7ea4SMartin Matuska 				else if (wmemcmp(s, L"alarm", 5) == 0)
1366e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1367e9ed7ea4SMartin Matuska 			}
1368e9ed7ea4SMartin Matuska 			if (type == 0) {
1369e9ed7ea4SMartin Matuska 				/* Invalid entry type, skip entry */
1370e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1371e9ed7ea4SMartin Matuska 				continue;
1372e9ed7ea4SMartin Matuska 			}
1373e9ed7ea4SMartin Matuska 			isint_w(field[4 + n].start, field[4 + n].end, &id);
1374e9ed7ea4SMartin Matuska 		}
13756c95142eSMartin Matuska 
13766c95142eSMartin Matuska 		/* Add entry to the internal list. */
1377e9ed7ea4SMartin Matuska 		r = archive_acl_add_entry_w_len(acl, type, permset,
13786c95142eSMartin Matuska 		    tag, id, name.start, name.end - name.start);
1379e9ed7ea4SMartin Matuska 		if (r < ARCHIVE_WARN)
1380e9ed7ea4SMartin Matuska 			return (r);
1381e9ed7ea4SMartin Matuska 		if (r != ARCHIVE_OK)
1382e9ed7ea4SMartin Matuska 			ret = ARCHIVE_WARN;
1383e9ed7ea4SMartin Matuska 		types |= type;
13846c95142eSMartin Matuska 	}
1385e9ed7ea4SMartin Matuska 
1386e9ed7ea4SMartin Matuska 	/* Reset ACL */
1387e9ed7ea4SMartin Matuska 	archive_acl_reset(acl, types);
1388e9ed7ea4SMartin Matuska 
1389e9ed7ea4SMartin Matuska 	return (ret);
13906c95142eSMartin Matuska }
13916c95142eSMartin Matuska 
13926c95142eSMartin Matuska /*
13936c95142eSMartin Matuska  * Parse a string to a positive decimal integer.  Returns true if
13946c95142eSMartin Matuska  * the string is non-empty and consists only of decimal digits,
13956c95142eSMartin Matuska  * false otherwise.
13966c95142eSMartin Matuska  */
13976c95142eSMartin Matuska static int
13986c95142eSMartin Matuska isint_w(const wchar_t *start, const wchar_t *end, int *result)
13996c95142eSMartin Matuska {
14006c95142eSMartin Matuska 	int n = 0;
14016c95142eSMartin Matuska 	if (start >= end)
14026c95142eSMartin Matuska 		return (0);
14036c95142eSMartin Matuska 	while (start < end) {
1404c3afd20fSMartin Matuska 		if (*start < L'0' || *start > L'9')
14056c95142eSMartin Matuska 			return (0);
14066c95142eSMartin Matuska 		if (n > (INT_MAX / 10) ||
1407c3afd20fSMartin Matuska 		    (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
14086c95142eSMartin Matuska 			n = INT_MAX;
14096c95142eSMartin Matuska 		} else {
14106c95142eSMartin Matuska 			n *= 10;
1411c3afd20fSMartin Matuska 			n += *start - L'0';
14126c95142eSMartin Matuska 		}
14136c95142eSMartin Matuska 		start++;
14146c95142eSMartin Matuska 	}
14156c95142eSMartin Matuska 	*result = n;
14166c95142eSMartin Matuska 	return (1);
14176c95142eSMartin Matuska }
14186c95142eSMartin Matuska 
14196c95142eSMartin Matuska /*
14206c95142eSMartin Matuska  * Parse a string as a mode field.  Returns true if
14216c95142eSMartin Matuska  * the string is non-empty and consists only of mode characters,
14226c95142eSMartin Matuska  * false otherwise.
14236c95142eSMartin Matuska  */
14246c95142eSMartin Matuska static int
14256c95142eSMartin Matuska ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
14266c95142eSMartin Matuska {
14276c95142eSMartin Matuska 	const wchar_t *p;
14286c95142eSMartin Matuska 
14296c95142eSMartin Matuska 	if (start >= end)
14306c95142eSMartin Matuska 		return (0);
14316c95142eSMartin Matuska 	p = start;
14326c95142eSMartin Matuska 	*permset = 0;
14336c95142eSMartin Matuska 	while (p < end) {
14346c95142eSMartin Matuska 		switch (*p++) {
1435e9ed7ea4SMartin Matuska 		case L'r': case L'R':
14366c95142eSMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ;
14376c95142eSMartin Matuska 			break;
1438e9ed7ea4SMartin Matuska 		case L'w': case L'W':
14396c95142eSMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
14406c95142eSMartin Matuska 			break;
1441e9ed7ea4SMartin Matuska 		case L'x': case L'X':
14426c95142eSMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
14436c95142eSMartin Matuska 			break;
1444e9ed7ea4SMartin Matuska 		case L'-':
1445e9ed7ea4SMartin Matuska 			break;
1446e9ed7ea4SMartin Matuska 		default:
1447e9ed7ea4SMartin Matuska 			return (0);
1448e9ed7ea4SMartin Matuska 		}
1449e9ed7ea4SMartin Matuska 	}
1450e9ed7ea4SMartin Matuska 	return (1);
1451e9ed7ea4SMartin Matuska }
1452e9ed7ea4SMartin Matuska 
1453e9ed7ea4SMartin Matuska /*
1454e9ed7ea4SMartin Matuska  * Parse a string as a NFS4 ACL permission field.
1455e9ed7ea4SMartin Matuska  * Returns true if the string is non-empty and consists only of NFS4 ACL
1456e9ed7ea4SMartin Matuska  * permission characters, false otherwise
1457e9ed7ea4SMartin Matuska  */
1458e9ed7ea4SMartin Matuska static int
1459e9ed7ea4SMartin Matuska is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1460e9ed7ea4SMartin Matuska {
14615d6770bdSMartin Matuska 	const wchar_t *p = start;
1462e9ed7ea4SMartin Matuska 
1463e9ed7ea4SMartin Matuska 	while (p < end) {
1464e9ed7ea4SMartin Matuska 		switch (*p++) {
1465e9ed7ea4SMartin Matuska 		case L'r':
1466e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1467e9ed7ea4SMartin Matuska 			break;
1468e9ed7ea4SMartin Matuska 		case L'w':
1469e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1470e9ed7ea4SMartin Matuska 			break;
1471e9ed7ea4SMartin Matuska 		case L'x':
1472e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1473e9ed7ea4SMartin Matuska 			break;
1474e9ed7ea4SMartin Matuska 		case L'p':
1475e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1476e9ed7ea4SMartin Matuska 			break;
1477e9ed7ea4SMartin Matuska 		case L'D':
1478e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1479e9ed7ea4SMartin Matuska 			break;
1480e9ed7ea4SMartin Matuska 		case L'd':
1481e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1482e9ed7ea4SMartin Matuska 			break;
1483e9ed7ea4SMartin Matuska 		case L'a':
1484e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1485e9ed7ea4SMartin Matuska 			break;
1486e9ed7ea4SMartin Matuska 		case L'A':
1487e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1488e9ed7ea4SMartin Matuska 			break;
1489e9ed7ea4SMartin Matuska 		case L'R':
1490e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1491e9ed7ea4SMartin Matuska 			break;
1492e9ed7ea4SMartin Matuska 		case L'W':
1493e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1494e9ed7ea4SMartin Matuska 			break;
1495e9ed7ea4SMartin Matuska 		case L'c':
1496e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1497e9ed7ea4SMartin Matuska 			break;
1498e9ed7ea4SMartin Matuska 		case L'C':
1499e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1500e9ed7ea4SMartin Matuska 			break;
1501e9ed7ea4SMartin Matuska 		case L'o':
1502e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1503e9ed7ea4SMartin Matuska 			break;
1504e9ed7ea4SMartin Matuska 		case L's':
1505e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1506e9ed7ea4SMartin Matuska 			break;
1507e9ed7ea4SMartin Matuska 		case L'-':
1508e9ed7ea4SMartin Matuska 			break;
1509e9ed7ea4SMartin Matuska 		default:
1510e9ed7ea4SMartin Matuska 			return(0);
1511e9ed7ea4SMartin Matuska 		}
1512e9ed7ea4SMartin Matuska 	}
1513e9ed7ea4SMartin Matuska 	return (1);
1514e9ed7ea4SMartin Matuska }
1515e9ed7ea4SMartin Matuska 
1516e9ed7ea4SMartin Matuska /*
1517e9ed7ea4SMartin Matuska  * Parse a string as a NFS4 ACL flags field.
1518e9ed7ea4SMartin Matuska  * Returns true if the string is non-empty and consists only of NFS4 ACL
1519e9ed7ea4SMartin Matuska  * flag characters, false otherwise
1520e9ed7ea4SMartin Matuska  */
1521e9ed7ea4SMartin Matuska static int
1522e9ed7ea4SMartin Matuska is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1523e9ed7ea4SMartin Matuska {
15245d6770bdSMartin Matuska 	const wchar_t *p = start;
1525e9ed7ea4SMartin Matuska 
1526e9ed7ea4SMartin Matuska 	while (p < end) {
1527e9ed7ea4SMartin Matuska 		switch(*p++) {
1528e9ed7ea4SMartin Matuska 		case L'f':
1529e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1530e9ed7ea4SMartin Matuska 			break;
1531e9ed7ea4SMartin Matuska 		case L'd':
1532e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1533e9ed7ea4SMartin Matuska 			break;
1534e9ed7ea4SMartin Matuska 		case L'i':
1535e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1536e9ed7ea4SMartin Matuska 			break;
1537e9ed7ea4SMartin Matuska 		case L'n':
1538e9ed7ea4SMartin Matuska 			*permset |=
1539e9ed7ea4SMartin Matuska 			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1540e9ed7ea4SMartin Matuska 			break;
1541e9ed7ea4SMartin Matuska 		case L'S':
1542e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1543e9ed7ea4SMartin Matuska 			break;
1544e9ed7ea4SMartin Matuska 		case L'F':
1545e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1546e9ed7ea4SMartin Matuska 			break;
1547e9ed7ea4SMartin Matuska 		case L'I':
1548e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1549e9ed7ea4SMartin Matuska 			break;
1550e9ed7ea4SMartin Matuska 		case L'-':
15516c95142eSMartin Matuska 			break;
15526c95142eSMartin Matuska 		default:
15536c95142eSMartin Matuska 			return (0);
15546c95142eSMartin Matuska 		}
15556c95142eSMartin Matuska 	}
15566c95142eSMartin Matuska 	return (1);
15576c95142eSMartin Matuska }
15586c95142eSMartin Matuska 
15596c95142eSMartin Matuska /*
15606c95142eSMartin Matuska  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
15616c95142eSMartin Matuska  * to point to just after the separator.  *start points to the first
15626c95142eSMartin Matuska  * character of the matched text and *end just after the last
15636c95142eSMartin Matuska  * character of the matched identifier.  In particular *end - *start
15646c95142eSMartin Matuska  * is the length of the field body, not including leading or trailing
15656c95142eSMartin Matuska  * whitespace.
15666c95142eSMartin Matuska  */
15676c95142eSMartin Matuska static void
15686c95142eSMartin Matuska next_field_w(const wchar_t **wp, const wchar_t **start,
15696c95142eSMartin Matuska     const wchar_t **end, wchar_t *sep)
15706c95142eSMartin Matuska {
15716c95142eSMartin Matuska 	/* Skip leading whitespace to find start of field. */
15726c95142eSMartin Matuska 	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
15736c95142eSMartin Matuska 		(*wp)++;
15746c95142eSMartin Matuska 	}
15756c95142eSMartin Matuska 	*start = *wp;
15766c95142eSMartin Matuska 
15776c95142eSMartin Matuska 	/* Scan for the separator. */
15786c95142eSMartin Matuska 	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
15797d69e4cdSMartin Matuska 	    **wp != L'\n' && **wp != L'#') {
15806c95142eSMartin Matuska 		(*wp)++;
15816c95142eSMartin Matuska 	}
15826c95142eSMartin Matuska 	*sep = **wp;
15836c95142eSMartin Matuska 
15847d69e4cdSMartin Matuska 	/* Locate end of field, trim trailing whitespace if necessary */
15857d69e4cdSMartin Matuska 	if (*wp == *start) {
15867d69e4cdSMartin Matuska 		*end = *wp;
15877d69e4cdSMartin Matuska 	} else {
15886c95142eSMartin Matuska 		*end = *wp - 1;
15896c95142eSMartin Matuska 		while (**end == L' ' || **end == L'\t' || **end == L'\n') {
15906c95142eSMartin Matuska 			(*end)--;
15916c95142eSMartin Matuska 		}
15926c95142eSMartin Matuska 		(*end)++;
15937d69e4cdSMartin Matuska 	}
15947d69e4cdSMartin Matuska 
15957d69e4cdSMartin Matuska 	/* Handle in-field comments */
15967d69e4cdSMartin Matuska 	if (*sep == L'#') {
15977d69e4cdSMartin Matuska 		while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
15987d69e4cdSMartin Matuska 			(*wp)++;
15997d69e4cdSMartin Matuska 		}
16007d69e4cdSMartin Matuska 		*sep = **wp;
16017d69e4cdSMartin Matuska 	}
16026c95142eSMartin Matuska 
16036c95142eSMartin Matuska 	/* Adjust scanner location. */
16046c95142eSMartin Matuska 	if (**wp != L'\0')
16056c95142eSMartin Matuska 		(*wp)++;
16066c95142eSMartin Matuska }
16076c95142eSMartin Matuska 
16086c95142eSMartin Matuska /*
1609e9ed7ea4SMartin Matuska  * Parse an ACL text string.
1610e9ed7ea4SMartin Matuska  *
1611e9ed7ea4SMartin Matuska  * The want_type argument may be one of the following:
1612e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1613e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1614e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1615e9ed7ea4SMartin Matuska  *
1616e9ed7ea4SMartin Matuska  * POSIX.1e ACL entries prefixed with "default:" are treated as
1617e9ed7ea4SMartin Matuska  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
16186c95142eSMartin Matuska  */
16196c95142eSMartin Matuska int
1620e9ed7ea4SMartin Matuska archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1621e9ed7ea4SMartin Matuska     int want_type, struct archive_string_conv *sc)
16226c95142eSMartin Matuska {
1623*bd66c1b4SMartin Matuska 	return archive_acl_from_text_nl(acl, text, strlen(text), want_type, sc);
1624*bd66c1b4SMartin Matuska }
1625*bd66c1b4SMartin Matuska 
1626*bd66c1b4SMartin Matuska int
1627*bd66c1b4SMartin Matuska archive_acl_from_text_nl(struct archive_acl *acl, const char *text,
1628*bd66c1b4SMartin Matuska     size_t length, int want_type, struct archive_string_conv *sc)
1629*bd66c1b4SMartin Matuska {
16306c95142eSMartin Matuska 	struct {
16316c95142eSMartin Matuska 		const char *start;
16326c95142eSMartin Matuska 		const char *end;
1633e9ed7ea4SMartin Matuska 	} field[6], name;
16346c95142eSMartin Matuska 
1635e9ed7ea4SMartin Matuska 	const char *s, *st;
16369f3de9e2SMartin Matuska 	int numfields, fields, n, r, sol, ret;
1637e9ed7ea4SMartin Matuska 	int type, types, tag, permset, id;
1638e9ed7ea4SMartin Matuska 	size_t len;
16396c95142eSMartin Matuska 	char sep;
16406c95142eSMartin Matuska 
1641e9ed7ea4SMartin Matuska 	switch (want_type) {
1642e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1643e9ed7ea4SMartin Matuska 		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1644a2a3407cSMartin Matuska 		__LA_FALLTHROUGH;
1645e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1646e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1647e9ed7ea4SMartin Matuska 		numfields = 5;
1648e9ed7ea4SMartin Matuska 		break;
1649e9ed7ea4SMartin Matuska 	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1650e9ed7ea4SMartin Matuska 		numfields = 6;
1651e9ed7ea4SMartin Matuska 		break;
1652e9ed7ea4SMartin Matuska 	default:
1653e9ed7ea4SMartin Matuska 		return (ARCHIVE_FATAL);
1654e9ed7ea4SMartin Matuska 	}
1655e9ed7ea4SMartin Matuska 
1656e9ed7ea4SMartin Matuska 	ret = ARCHIVE_OK;
1657e9ed7ea4SMartin Matuska 	types = 0;
1658e9ed7ea4SMartin Matuska 
1659*bd66c1b4SMartin Matuska 	while (text != NULL && length > 0 && *text != '\0') {
16606c95142eSMartin Matuska 		/*
16616c95142eSMartin Matuska 		 * Parse the fields out of the next entry,
16626c95142eSMartin Matuska 		 * advance 'text' to start of next entry.
16636c95142eSMartin Matuska 		 */
16646c95142eSMartin Matuska 		fields = 0;
16656c95142eSMartin Matuska 		do {
16666c95142eSMartin Matuska 			const char *start, *end;
1667*bd66c1b4SMartin Matuska 			next_field(&text, &length, &start, &end, &sep);
1668e9ed7ea4SMartin Matuska 			if (fields < numfields) {
16696c95142eSMartin Matuska 				field[fields].start = start;
16706c95142eSMartin Matuska 				field[fields].end = end;
16716c95142eSMartin Matuska 			}
16726c95142eSMartin Matuska 			++fields;
16736c95142eSMartin Matuska 		} while (sep == ':');
16746c95142eSMartin Matuska 
16756c95142eSMartin Matuska 		/* Set remaining fields to blank. */
1676e9ed7ea4SMartin Matuska 		for (n = fields; n < numfields; ++n)
16776c95142eSMartin Matuska 			field[n].start = field[n].end = NULL;
16786c95142eSMartin Matuska 
1679e9ed7ea4SMartin Matuska 		if (field[0].start != NULL && *(field[0].start) == '#') {
1680e9ed7ea4SMartin Matuska 			/* Comment, skip entry */
1681e9ed7ea4SMartin Matuska 			continue;
1682e9ed7ea4SMartin Matuska 		}
1683e9ed7ea4SMartin Matuska 
1684e9ed7ea4SMartin Matuska 		n = 0;
16859f3de9e2SMartin Matuska 		sol = 0;
16866c95142eSMartin Matuska 		id = -1;
1687e9ed7ea4SMartin Matuska 		permset = 0;
16886c95142eSMartin Matuska 		name.start = name.end = NULL;
1689e9ed7ea4SMartin Matuska 
1690e9ed7ea4SMartin Matuska 		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1691e9ed7ea4SMartin Matuska 			/* POSIX.1e ACLs */
1692e9ed7ea4SMartin Matuska 			/*
1693e9ed7ea4SMartin Matuska 			 * Default keyword "default:user::rwx"
1694e9ed7ea4SMartin Matuska 			 * if found, we have one more field
1695e9ed7ea4SMartin Matuska 			 *
1696e9ed7ea4SMartin Matuska 			 * We also support old Solaris extension:
1697e9ed7ea4SMartin Matuska 			 * "defaultuser::rwx" is the default ACL corresponding
1698e9ed7ea4SMartin Matuska 			 * to "user::rwx", etc. valid only for first field
1699e9ed7ea4SMartin Matuska 			 */
1700e9ed7ea4SMartin Matuska 			s = field[0].start;
1701e9ed7ea4SMartin Matuska 			len = field[0].end - field[0].start;
1702e9ed7ea4SMartin Matuska 			if (*s == 'd' && (len == 1 || (len >= 7
1703e9ed7ea4SMartin Matuska 			    && memcmp((s + 1), "efault", 6) == 0))) {
1704e9ed7ea4SMartin Matuska 				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1705e9ed7ea4SMartin Matuska 				if (len > 7)
1706e9ed7ea4SMartin Matuska 					field[0].start += 7;
1707e9ed7ea4SMartin Matuska 				else
1708e9ed7ea4SMartin Matuska 					n = 1;
17096c95142eSMartin Matuska 			} else
1710e9ed7ea4SMartin Matuska 				type = want_type;
1711e9ed7ea4SMartin Matuska 
1712e9ed7ea4SMartin Matuska 			/* Check for a numeric ID in field n+1 or n+3. */
1713e9ed7ea4SMartin Matuska 			isint(field[n + 1].start, field[n + 1].end, &id);
1714e9ed7ea4SMartin Matuska 			/* Field n+3 is optional. */
1715e9ed7ea4SMartin Matuska 			if (id == -1 && fields > (n + 3))
1716e9ed7ea4SMartin Matuska 				isint(field[n + 3].start, field[n + 3].end,
1717e9ed7ea4SMartin Matuska 				    &id);
1718e9ed7ea4SMartin Matuska 
1719e9ed7ea4SMartin Matuska 			tag = 0;
1720e9ed7ea4SMartin Matuska 			s = field[n].start;
1721e9ed7ea4SMartin Matuska 			st = field[n].start + 1;
1722e9ed7ea4SMartin Matuska 			len = field[n].end - field[n].start;
1723e9ed7ea4SMartin Matuska 
172498bf66e6SMartin Matuska 			if (len == 0) {
172598bf66e6SMartin Matuska 				ret = ARCHIVE_WARN;
172698bf66e6SMartin Matuska 				continue;
172798bf66e6SMartin Matuska 			}
172898bf66e6SMartin Matuska 
1729e9ed7ea4SMartin Matuska 			switch (*s) {
1730e9ed7ea4SMartin Matuska 			case 'u':
1731e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 4
1732e9ed7ea4SMartin Matuska 				    && memcmp(st, "ser", 3) == 0))
17336c95142eSMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1734e9ed7ea4SMartin Matuska 				break;
1735e9ed7ea4SMartin Matuska 			case 'g':
1736e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 5
1737e9ed7ea4SMartin Matuska 				    && memcmp(st, "roup", 4) == 0))
1738e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1739e9ed7ea4SMartin Matuska 				break;
1740e9ed7ea4SMartin Matuska 			case 'o':
1741e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 5
1742e9ed7ea4SMartin Matuska 				    && memcmp(st, "ther", 4) == 0))
1743e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_OTHER;
1744e9ed7ea4SMartin Matuska 				break;
1745e9ed7ea4SMartin Matuska 			case 'm':
1746e9ed7ea4SMartin Matuska 				if (len == 1 || (len == 4
1747e9ed7ea4SMartin Matuska 				    && memcmp(st, "ask", 3) == 0))
1748e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_MASK;
1749e9ed7ea4SMartin Matuska 				break;
1750e9ed7ea4SMartin Matuska 			default:
1751e9ed7ea4SMartin Matuska 					break;
1752e9ed7ea4SMartin Matuska 			}
1753e9ed7ea4SMartin Matuska 
1754e9ed7ea4SMartin Matuska 			switch (tag) {
1755e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_OTHER:
1756e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_MASK:
1757e9ed7ea4SMartin Matuska 				if (fields == (n + 2)
1758e9ed7ea4SMartin Matuska 				    && field[n + 1].start < field[n + 1].end
1759e9ed7ea4SMartin Matuska 				    && ismode(field[n + 1].start,
1760e9ed7ea4SMartin Matuska 				    field[n + 1].end, &permset)) {
1761e9ed7ea4SMartin Matuska 					/* This is Solaris-style "other:rwx" */
17629f3de9e2SMartin Matuska 					sol = 1;
1763e9ed7ea4SMartin Matuska 				} else if (fields == (n + 3) &&
1764e9ed7ea4SMartin Matuska 				    field[n + 1].start < field[n + 1].end) {
1765e9ed7ea4SMartin Matuska 					/* Invalid mask or other field */
1766e9ed7ea4SMartin Matuska 					ret = ARCHIVE_WARN;
1767e9ed7ea4SMartin Matuska 					continue;
1768e9ed7ea4SMartin Matuska 				}
1769e9ed7ea4SMartin Matuska 				break;
1770e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1771e9ed7ea4SMartin Matuska 			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1772e9ed7ea4SMartin Matuska 				if (id != -1 ||
1773e9ed7ea4SMartin Matuska 				    field[n + 1].start < field[n + 1].end) {
1774e9ed7ea4SMartin Matuska 					name = field[n + 1];
1775e9ed7ea4SMartin Matuska 					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1776e9ed7ea4SMartin Matuska 						tag = ARCHIVE_ENTRY_ACL_USER;
1777e9ed7ea4SMartin Matuska 					else
17786c95142eSMartin Matuska 						tag = ARCHIVE_ENTRY_ACL_GROUP;
1779e9ed7ea4SMartin Matuska 				}
1780e9ed7ea4SMartin Matuska 				break;
1781e9ed7ea4SMartin Matuska 			default:
1782e9ed7ea4SMartin Matuska 				/* Invalid tag, skip entry */
1783e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1784e9ed7ea4SMartin Matuska 				continue;
1785e9ed7ea4SMartin Matuska 			}
1786e9ed7ea4SMartin Matuska 
17879f3de9e2SMartin Matuska 			/*
17889f3de9e2SMartin Matuska 			 * Without "default:" we expect mode in field 3
17899f3de9e2SMartin Matuska 			 * Exception: Solaris other and mask fields
17909f3de9e2SMartin Matuska 			 */
17919f3de9e2SMartin Matuska 			if (permset == 0 && !ismode(field[n + 2 - sol].start,
17929f3de9e2SMartin Matuska 			    field[n + 2 - sol].end, &permset)) {
1793e9ed7ea4SMartin Matuska 				/* Invalid mode, skip entry */
1794e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1795e9ed7ea4SMartin Matuska 				continue;
1796e9ed7ea4SMartin Matuska 			}
1797e9ed7ea4SMartin Matuska 		} else {
1798e9ed7ea4SMartin Matuska 			/* NFS4 ACLs */
1799e9ed7ea4SMartin Matuska 			s = field[0].start;
1800e9ed7ea4SMartin Matuska 			len = field[0].end - field[0].start;
1801e9ed7ea4SMartin Matuska 			tag = 0;
1802e9ed7ea4SMartin Matuska 
1803e9ed7ea4SMartin Matuska 			switch (len) {
1804e9ed7ea4SMartin Matuska 			case 4:
1805e9ed7ea4SMartin Matuska 				if (memcmp(s, "user", 4) == 0)
1806e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_USER;
1807e9ed7ea4SMartin Matuska 				break;
1808e9ed7ea4SMartin Matuska 			case 5:
1809e9ed7ea4SMartin Matuska 				if (memcmp(s, "group", 5) == 0)
1810e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_GROUP;
1811e9ed7ea4SMartin Matuska 				break;
1812e9ed7ea4SMartin Matuska 			case 6:
1813e9ed7ea4SMartin Matuska 				if (memcmp(s, "owner@", 6) == 0)
1814e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1815e9ed7ea4SMartin Matuska 				else if (memcmp(s, "group@", 6) == 0)
1816e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1817e9ed7ea4SMartin Matuska 				break;
1818e9ed7ea4SMartin Matuska 			case 9:
1819e9ed7ea4SMartin Matuska 				if (memcmp(s, "everyone@", 9) == 0)
1820e9ed7ea4SMartin Matuska 					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1821e9ed7ea4SMartin Matuska 				break;
1822e9ed7ea4SMartin Matuska 			default:
1823e9ed7ea4SMartin Matuska 				break;
1824e9ed7ea4SMartin Matuska 			}
1825e9ed7ea4SMartin Matuska 
1826e9ed7ea4SMartin Matuska 			if (tag == 0) {
1827e9ed7ea4SMartin Matuska 				/* Invalid tag, skip entry */
1828e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1829e9ed7ea4SMartin Matuska 				continue;
1830e9ed7ea4SMartin Matuska 			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1831e9ed7ea4SMartin Matuska 			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1832e9ed7ea4SMartin Matuska 				n = 1;
18336c95142eSMartin Matuska 				name = field[1];
18342dbf8c4aSMartin Matuska 				isint(name.start, name.end, &id);
18356c95142eSMartin Matuska 			} else
1836e9ed7ea4SMartin Matuska 				n = 0;
1837e9ed7ea4SMartin Matuska 
1838e9ed7ea4SMartin Matuska 			if (!is_nfs4_perms(field[1 + n].start,
1839e9ed7ea4SMartin Matuska 			    field[1 + n].end, &permset)) {
1840e9ed7ea4SMartin Matuska 				/* Invalid NFSv4 perms, skip entry */
1841e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1842e9ed7ea4SMartin Matuska 				continue;
1843e9ed7ea4SMartin Matuska 			}
1844e9ed7ea4SMartin Matuska 			if (!is_nfs4_flags(field[2 + n].start,
1845e9ed7ea4SMartin Matuska 			    field[2 + n].end, &permset)) {
1846e9ed7ea4SMartin Matuska 				/* Invalid NFSv4 flags, skip entry */
1847e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1848e9ed7ea4SMartin Matuska 				continue;
1849e9ed7ea4SMartin Matuska 			}
1850e9ed7ea4SMartin Matuska 			s = field[3 + n].start;
1851e9ed7ea4SMartin Matuska 			len = field[3 + n].end - field[3 + n].start;
1852e9ed7ea4SMartin Matuska 			type = 0;
1853e9ed7ea4SMartin Matuska 			if (len == 4) {
1854e9ed7ea4SMartin Matuska 				if (memcmp(s, "deny", 4) == 0)
1855e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1856e9ed7ea4SMartin Matuska 			} else if (len == 5) {
1857e9ed7ea4SMartin Matuska 				if (memcmp(s, "allow", 5) == 0)
1858e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1859e9ed7ea4SMartin Matuska 				else if (memcmp(s, "audit", 5) == 0)
1860e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1861e9ed7ea4SMartin Matuska 				else if (memcmp(s, "alarm", 5) == 0)
1862e9ed7ea4SMartin Matuska 					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1863e9ed7ea4SMartin Matuska 			}
1864e9ed7ea4SMartin Matuska 			if (type == 0) {
1865e9ed7ea4SMartin Matuska 				/* Invalid entry type, skip entry */
1866e9ed7ea4SMartin Matuska 				ret = ARCHIVE_WARN;
1867e9ed7ea4SMartin Matuska 				continue;
1868e9ed7ea4SMartin Matuska 			}
1869e9ed7ea4SMartin Matuska 			isint(field[4 + n].start, field[4 + n].end,
1870e9ed7ea4SMartin Matuska 			    &id);
1871e9ed7ea4SMartin Matuska 		}
18726c95142eSMartin Matuska 
18736c95142eSMartin Matuska 		/* Add entry to the internal list. */
18746c95142eSMartin Matuska 		r = archive_acl_add_entry_len_l(acl, type, permset,
18756c95142eSMartin Matuska 		    tag, id, name.start, name.end - name.start, sc);
18766c95142eSMartin Matuska 		if (r < ARCHIVE_WARN)
18776c95142eSMartin Matuska 			return (r);
18786c95142eSMartin Matuska 		if (r != ARCHIVE_OK)
18796c95142eSMartin Matuska 			ret = ARCHIVE_WARN;
1880e9ed7ea4SMartin Matuska 		types |= type;
18816c95142eSMartin Matuska 	}
1882e9ed7ea4SMartin Matuska 
1883e9ed7ea4SMartin Matuska 	/* Reset ACL */
1884e9ed7ea4SMartin Matuska 	archive_acl_reset(acl, types);
1885e9ed7ea4SMartin Matuska 
18866c95142eSMartin Matuska 	return (ret);
18876c95142eSMartin Matuska }
18886c95142eSMartin Matuska 
18896c95142eSMartin Matuska /*
18906c95142eSMartin Matuska  * Parse a string to a positive decimal integer.  Returns true if
18916c95142eSMartin Matuska  * the string is non-empty and consists only of decimal digits,
18926c95142eSMartin Matuska  * false otherwise.
18936c95142eSMartin Matuska  */
18946c95142eSMartin Matuska static int
18956c95142eSMartin Matuska isint(const char *start, const char *end, int *result)
18966c95142eSMartin Matuska {
18976c95142eSMartin Matuska 	int n = 0;
18986c95142eSMartin Matuska 	if (start >= end)
18996c95142eSMartin Matuska 		return (0);
19006c95142eSMartin Matuska 	while (start < end) {
19016c95142eSMartin Matuska 		if (*start < '0' || *start > '9')
19026c95142eSMartin Matuska 			return (0);
19036c95142eSMartin Matuska 		if (n > (INT_MAX / 10) ||
19046c95142eSMartin Matuska 		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
19056c95142eSMartin Matuska 			n = INT_MAX;
19066c95142eSMartin Matuska 		} else {
19076c95142eSMartin Matuska 			n *= 10;
19086c95142eSMartin Matuska 			n += *start - '0';
19096c95142eSMartin Matuska 		}
19106c95142eSMartin Matuska 		start++;
19116c95142eSMartin Matuska 	}
19126c95142eSMartin Matuska 	*result = n;
19136c95142eSMartin Matuska 	return (1);
19146c95142eSMartin Matuska }
19156c95142eSMartin Matuska 
19166c95142eSMartin Matuska /*
19176c95142eSMartin Matuska  * Parse a string as a mode field.  Returns true if
19186c95142eSMartin Matuska  * the string is non-empty and consists only of mode characters,
19196c95142eSMartin Matuska  * false otherwise.
19206c95142eSMartin Matuska  */
19216c95142eSMartin Matuska static int
19226c95142eSMartin Matuska ismode(const char *start, const char *end, int *permset)
19236c95142eSMartin Matuska {
19246c95142eSMartin Matuska 	const char *p;
19256c95142eSMartin Matuska 
19266c95142eSMartin Matuska 	if (start >= end)
19276c95142eSMartin Matuska 		return (0);
19286c95142eSMartin Matuska 	p = start;
19296c95142eSMartin Matuska 	*permset = 0;
19306c95142eSMartin Matuska 	while (p < end) {
19316c95142eSMartin Matuska 		switch (*p++) {
19326c95142eSMartin Matuska 		case 'r': case 'R':
19336c95142eSMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ;
19346c95142eSMartin Matuska 			break;
19356c95142eSMartin Matuska 		case 'w': case 'W':
19366c95142eSMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
19376c95142eSMartin Matuska 			break;
19386c95142eSMartin Matuska 		case 'x': case 'X':
19396c95142eSMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
19406c95142eSMartin Matuska 			break;
19416c95142eSMartin Matuska 		case '-':
19426c95142eSMartin Matuska 			break;
19436c95142eSMartin Matuska 		default:
19446c95142eSMartin Matuska 			return (0);
19456c95142eSMartin Matuska 		}
19466c95142eSMartin Matuska 	}
19476c95142eSMartin Matuska 	return (1);
19486c95142eSMartin Matuska }
19496c95142eSMartin Matuska 
19506c95142eSMartin Matuska /*
1951e9ed7ea4SMartin Matuska  * Parse a string as a NFS4 ACL permission field.
1952e9ed7ea4SMartin Matuska  * Returns true if the string is non-empty and consists only of NFS4 ACL
1953e9ed7ea4SMartin Matuska  * permission characters, false otherwise
1954e9ed7ea4SMartin Matuska  */
1955e9ed7ea4SMartin Matuska static int
1956e9ed7ea4SMartin Matuska is_nfs4_perms(const char *start, const char *end, int *permset)
1957e9ed7ea4SMartin Matuska {
19585d6770bdSMartin Matuska 	const char *p = start;
1959e9ed7ea4SMartin Matuska 
1960e9ed7ea4SMartin Matuska 	while (p < end) {
1961e9ed7ea4SMartin Matuska 		switch (*p++) {
1962e9ed7ea4SMartin Matuska 		case 'r':
1963e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1964e9ed7ea4SMartin Matuska 			break;
1965e9ed7ea4SMartin Matuska 		case 'w':
1966e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1967e9ed7ea4SMartin Matuska 			break;
1968e9ed7ea4SMartin Matuska 		case 'x':
1969e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1970e9ed7ea4SMartin Matuska 			break;
1971e9ed7ea4SMartin Matuska 		case 'p':
1972e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1973e9ed7ea4SMartin Matuska 			break;
1974e9ed7ea4SMartin Matuska 		case 'D':
1975e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1976e9ed7ea4SMartin Matuska 			break;
1977e9ed7ea4SMartin Matuska 		case 'd':
1978e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1979e9ed7ea4SMartin Matuska 			break;
1980e9ed7ea4SMartin Matuska 		case 'a':
1981e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1982e9ed7ea4SMartin Matuska 			break;
1983e9ed7ea4SMartin Matuska 		case 'A':
1984e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1985e9ed7ea4SMartin Matuska 			break;
1986e9ed7ea4SMartin Matuska 		case 'R':
1987e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1988e9ed7ea4SMartin Matuska 			break;
1989e9ed7ea4SMartin Matuska 		case 'W':
1990e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1991e9ed7ea4SMartin Matuska 			break;
1992e9ed7ea4SMartin Matuska 		case 'c':
1993e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1994e9ed7ea4SMartin Matuska 			break;
1995e9ed7ea4SMartin Matuska 		case 'C':
1996e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1997e9ed7ea4SMartin Matuska 			break;
1998e9ed7ea4SMartin Matuska 		case 'o':
1999e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
2000e9ed7ea4SMartin Matuska 			break;
2001e9ed7ea4SMartin Matuska 		case 's':
2002e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
2003e9ed7ea4SMartin Matuska 			break;
2004e9ed7ea4SMartin Matuska 		case '-':
2005e9ed7ea4SMartin Matuska 			break;
2006e9ed7ea4SMartin Matuska 		default:
2007e9ed7ea4SMartin Matuska 			return(0);
2008e9ed7ea4SMartin Matuska 		}
2009e9ed7ea4SMartin Matuska 	}
2010e9ed7ea4SMartin Matuska 	return (1);
2011e9ed7ea4SMartin Matuska }
2012e9ed7ea4SMartin Matuska 
2013e9ed7ea4SMartin Matuska /*
2014e9ed7ea4SMartin Matuska  * Parse a string as a NFS4 ACL flags field.
2015e9ed7ea4SMartin Matuska  * Returns true if the string is non-empty and consists only of NFS4 ACL
2016e9ed7ea4SMartin Matuska  * flag characters, false otherwise
2017e9ed7ea4SMartin Matuska  */
2018e9ed7ea4SMartin Matuska static int
2019e9ed7ea4SMartin Matuska is_nfs4_flags(const char *start, const char *end, int *permset)
2020e9ed7ea4SMartin Matuska {
20215d6770bdSMartin Matuska 	const char *p = start;
2022e9ed7ea4SMartin Matuska 
2023e9ed7ea4SMartin Matuska 	while (p < end) {
2024e9ed7ea4SMartin Matuska 		switch(*p++) {
2025e9ed7ea4SMartin Matuska 		case 'f':
2026e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2027e9ed7ea4SMartin Matuska 			break;
2028e9ed7ea4SMartin Matuska 		case 'd':
2029e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2030e9ed7ea4SMartin Matuska 			break;
2031e9ed7ea4SMartin Matuska 		case 'i':
2032e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2033e9ed7ea4SMartin Matuska 			break;
2034e9ed7ea4SMartin Matuska 		case 'n':
2035e9ed7ea4SMartin Matuska 			*permset |=
2036e9ed7ea4SMartin Matuska 			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2037e9ed7ea4SMartin Matuska 			break;
2038e9ed7ea4SMartin Matuska 		case 'S':
2039e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2040e9ed7ea4SMartin Matuska 			break;
2041e9ed7ea4SMartin Matuska 		case 'F':
2042e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2043e9ed7ea4SMartin Matuska 			break;
2044e9ed7ea4SMartin Matuska 		case 'I':
2045e9ed7ea4SMartin Matuska 			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2046e9ed7ea4SMartin Matuska 			break;
2047e9ed7ea4SMartin Matuska 		case '-':
2048e9ed7ea4SMartin Matuska 			break;
2049e9ed7ea4SMartin Matuska 		default:
2050e9ed7ea4SMartin Matuska 			return (0);
2051e9ed7ea4SMartin Matuska 		}
2052e9ed7ea4SMartin Matuska 	}
2053e9ed7ea4SMartin Matuska 	return (1);
2054e9ed7ea4SMartin Matuska }
2055e9ed7ea4SMartin Matuska 
2056e9ed7ea4SMartin Matuska /*
2057*bd66c1b4SMartin Matuska  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *p is updated
20586c95142eSMartin Matuska  * to point to just after the separator.  *start points to the first
20596c95142eSMartin Matuska  * character of the matched text and *end just after the last
20606c95142eSMartin Matuska  * character of the matched identifier.  In particular *end - *start
20616c95142eSMartin Matuska  * is the length of the field body, not including leading or trailing
20626c95142eSMartin Matuska  * whitespace.
20636c95142eSMartin Matuska  */
20646c95142eSMartin Matuska static void
2065*bd66c1b4SMartin Matuska next_field(const char **p, size_t *l, const char **start,
20666c95142eSMartin Matuska     const char **end, char *sep)
20676c95142eSMartin Matuska {
20686c95142eSMartin Matuska 	/* Skip leading whitespace to find start of field. */
2069*bd66c1b4SMartin Matuska 	while (*l > 0 && (**p == ' ' || **p == '\t' || **p == '\n')) {
20706c95142eSMartin Matuska 		(*p)++;
2071*bd66c1b4SMartin Matuska 		(*l)--;
20726c95142eSMartin Matuska 	}
20736c95142eSMartin Matuska 	*start = *p;
20746c95142eSMartin Matuska 
2075*bd66c1b4SMartin Matuska 	/* Locate end of field, trim trailing whitespace if necessary */
2076*bd66c1b4SMartin Matuska 	while (*l > 0 && **p != ' ' && **p != '\t' && **p != '\n' && **p != ',' && **p != ':' && **p != '#') {
20776c95142eSMartin Matuska 		(*p)++;
2078*bd66c1b4SMartin Matuska 		(*l)--;
2079*bd66c1b4SMartin Matuska 	}
2080*bd66c1b4SMartin Matuska 	*end = *p;
2081*bd66c1b4SMartin Matuska 
2082*bd66c1b4SMartin Matuska 	/* Scan for the separator. */
2083*bd66c1b4SMartin Matuska 	while (*l > 0 && **p != ',' && **p != ':' && **p != '\n' && **p != '#') {
2084*bd66c1b4SMartin Matuska 		(*p)++;
2085*bd66c1b4SMartin Matuska 		(*l)--;
20866c95142eSMartin Matuska 	}
20876c95142eSMartin Matuska 	*sep = **p;
20886c95142eSMartin Matuska 
20897d69e4cdSMartin Matuska 	/* Handle in-field comments */
20907d69e4cdSMartin Matuska 	if (*sep == '#') {
2091*bd66c1b4SMartin Matuska 		while (*l > 0 && **p != ',' && **p != '\n') {
20927d69e4cdSMartin Matuska 			(*p)++;
2093*bd66c1b4SMartin Matuska 			(*l)--;
20947d69e4cdSMartin Matuska 		}
20957d69e4cdSMartin Matuska 		*sep = **p;
20967d69e4cdSMartin Matuska 	}
20976c95142eSMartin Matuska 
2098*bd66c1b4SMartin Matuska 	/* Skip separator. */
2099*bd66c1b4SMartin Matuska 	if (*l > 0) {
21006c95142eSMartin Matuska 		(*p)++;
2101*bd66c1b4SMartin Matuska 		(*l)--;
2102*bd66c1b4SMartin Matuska 	}
21036c95142eSMartin Matuska }
2104