archive_acl.c revision 339005
1231200Smm/*-
2231200Smm * Copyright (c) 2003-2010 Tim Kientzle
3313570Smm * Copyright (c) 2016 Martin Matuska
4231200Smm * All rights reserved.
5231200Smm *
6231200Smm * Redistribution and use in source and binary forms, with or without
7231200Smm * modification, are permitted provided that the following conditions
8231200Smm * are met:
9231200Smm * 1. Redistributions of source code must retain the above copyright
10231200Smm *    notice, this list of conditions and the following disclaimer.
11231200Smm * 2. Redistributions in binary form must reproduce the above copyright
12231200Smm *    notice, this list of conditions and the following disclaimer in the
13231200Smm *    documentation and/or other materials provided with the distribution.
14231200Smm *
15231200Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16231200Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17231200Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18231200Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19231200Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20231200Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21231200Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22231200Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23231200Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24231200Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25231200Smm */
26231200Smm
27231200Smm#include "archive_platform.h"
28231200Smm__FBSDID("$FreeBSD$");
29231200Smm
30231200Smm#ifdef HAVE_ERRNO_H
31231200Smm#include <errno.h>
32231200Smm#endif
33231200Smm#ifdef HAVE_LIMITS_H
34231200Smm#include <limits.h>
35231200Smm#endif
36231200Smm#ifdef HAVE_WCHAR_H
37231200Smm#include <wchar.h>
38231200Smm#endif
39231200Smm
40231200Smm#include "archive_acl_private.h"
41231200Smm#include "archive_entry.h"
42231200Smm#include "archive_private.h"
43231200Smm
44231200Smm#undef max
45231200Smm#define	max(a, b)	((a)>(b)?(a):(b))
46231200Smm
47231200Smm#ifndef HAVE_WMEMCMP
48231200Smm/* Good enough for simple equality testing, but not for sorting. */
49231200Smm#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
50231200Smm#endif
51231200Smm
52231200Smmstatic int	acl_special(struct archive_acl *acl,
53231200Smm		    int type, int permset, int tag);
54231200Smmstatic struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
55231200Smm		    int type, int permset, int tag, int id);
56232153Smmstatic int	archive_acl_add_entry_len_l(struct archive_acl *acl,
57232153Smm		    int type, int permset, int tag, int id, const char *name,
58232153Smm		    size_t len, struct archive_string_conv *sc);
59313570Smmstatic int	archive_acl_text_want_type(struct archive_acl *acl, int flags);
60313570Smmstatic ssize_t	archive_acl_text_len(struct archive_acl *acl, int want_type,
61313570Smm		    int flags, int wide, struct archive *a,
62313570Smm		    struct archive_string_conv *sc);
63231200Smmstatic int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
64231200Smmstatic int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
65313570Smmstatic int	is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
66313570Smm		    int *result);
67313570Smmstatic int	is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
68313570Smm		    int *result);
69231200Smmstatic void	next_field_w(const wchar_t **wp, const wchar_t **start,
70231200Smm		    const wchar_t **end, wchar_t *sep);
71313570Smmstatic void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
72313570Smm		    int tag, int flags, const wchar_t *wname, int perm, int id);
73231200Smmstatic void	append_id_w(wchar_t **wp, int id);
74231200Smmstatic int	isint(const char *start, const char *end, int *result);
75231200Smmstatic int	ismode(const char *start, const char *end, int *result);
76313570Smmstatic int	is_nfs4_flags(const char *start, const char *end,
77313570Smm		    int *result);
78313570Smmstatic int	is_nfs4_perms(const char *start, const char *end,
79313570Smm		    int *result);
80231200Smmstatic void	next_field(const char **p, const char **start,
81231200Smm		    const char **end, char *sep);
82313570Smmstatic void	append_entry(char **p, const char *prefix, int type,
83313570Smm		    int tag, int flags, const char *name, int perm, int id);
84231200Smmstatic void	append_id(char **p, int id);
85231200Smm
86313926Smmstatic const struct {
87313926Smm	const int perm;
88313926Smm	const char c;
89313926Smm	const wchar_t wc;
90313926Smm} nfsv4_acl_perm_map[] = {
91313926Smm	{ ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
92313926Smm	    L'r' },
93313926Smm	{ ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
94313926Smm	    L'w' },
95313926Smm	{ ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
96313926Smm	{ ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
97313926Smm	    'p', L'p' },
98313926Smm	{ ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
99313926Smm	{ ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
100313926Smm	{ ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
101313926Smm	{ ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
102313926Smm	{ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
103313926Smm	{ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
104313926Smm	{ ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
105313926Smm	{ ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
106313926Smm	{ ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
107313926Smm	{ ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
108313926Smm};
109313926Smm
110313926Smmstatic const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
111313926Smm    sizeof(nfsv4_acl_perm_map[0]));
112313926Smm
113313926Smmstatic const struct {
114313926Smm	const int perm;
115313926Smm	const char c;
116313926Smm	const wchar_t wc;
117313926Smm} nfsv4_acl_flag_map[] = {
118313926Smm	{ ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
119313926Smm	{ ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
120313926Smm	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
121313926Smm	{ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
122313926Smm	{ ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
123313926Smm	{ ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
124313926Smm	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
125313926Smm};
126313926Smm
127313926Smmstatic const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
128313926Smm    sizeof(nfsv4_acl_flag_map[0]));
129313926Smm
130231200Smmvoid
131231200Smmarchive_acl_clear(struct archive_acl *acl)
132231200Smm{
133231200Smm	struct archive_acl_entry *ap;
134231200Smm
135231200Smm	while (acl->acl_head != NULL) {
136231200Smm		ap = acl->acl_head->next;
137231200Smm		archive_mstring_clean(&acl->acl_head->name);
138231200Smm		free(acl->acl_head);
139231200Smm		acl->acl_head = ap;
140231200Smm	}
141231200Smm	if (acl->acl_text_w != NULL) {
142231200Smm		free(acl->acl_text_w);
143231200Smm		acl->acl_text_w = NULL;
144231200Smm	}
145231200Smm	if (acl->acl_text != NULL) {
146231200Smm		free(acl->acl_text);
147231200Smm		acl->acl_text = NULL;
148231200Smm	}
149231200Smm	acl->acl_p = NULL;
150311041Smm	acl->acl_types = 0;
151231200Smm	acl->acl_state = 0; /* Not counting. */
152231200Smm}
153231200Smm
154231200Smmvoid
155231200Smmarchive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
156231200Smm{
157231200Smm	struct archive_acl_entry *ap, *ap2;
158231200Smm
159231200Smm	archive_acl_clear(dest);
160231200Smm
161231200Smm	dest->mode = src->mode;
162231200Smm	ap = src->acl_head;
163231200Smm	while (ap != NULL) {
164231200Smm		ap2 = acl_new_entry(dest,
165231200Smm		    ap->type, ap->permset, ap->tag, ap->id);
166231200Smm		if (ap2 != NULL)
167231200Smm			archive_mstring_copy(&ap2->name, &ap->name);
168231200Smm		ap = ap->next;
169231200Smm	}
170231200Smm}
171231200Smm
172231200Smmint
173231200Smmarchive_acl_add_entry(struct archive_acl *acl,
174231200Smm    int type, int permset, int tag, int id, const char *name)
175231200Smm{
176231200Smm	struct archive_acl_entry *ap;
177231200Smm
178231200Smm	if (acl_special(acl, type, permset, tag) == 0)
179231200Smm		return ARCHIVE_OK;
180231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
181231200Smm	if (ap == NULL) {
182231200Smm		/* XXX Error XXX */
183231200Smm		return ARCHIVE_FAILED;
184231200Smm	}
185231200Smm	if (name != NULL  &&  *name != '\0')
186231200Smm		archive_mstring_copy_mbs(&ap->name, name);
187231200Smm	else
188231200Smm		archive_mstring_clean(&ap->name);
189231200Smm	return ARCHIVE_OK;
190231200Smm}
191231200Smm
192231200Smmint
193231200Smmarchive_acl_add_entry_w_len(struct archive_acl *acl,
194231200Smm    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
195231200Smm{
196231200Smm	struct archive_acl_entry *ap;
197231200Smm
198231200Smm	if (acl_special(acl, type, permset, tag) == 0)
199231200Smm		return ARCHIVE_OK;
200231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
201231200Smm	if (ap == NULL) {
202231200Smm		/* XXX Error XXX */
203231200Smm		return ARCHIVE_FAILED;
204231200Smm	}
205231200Smm	if (name != NULL  &&  *name != L'\0' && len > 0)
206231200Smm		archive_mstring_copy_wcs_len(&ap->name, name, len);
207231200Smm	else
208231200Smm		archive_mstring_clean(&ap->name);
209231200Smm	return ARCHIVE_OK;
210231200Smm}
211231200Smm
212232153Smmstatic int
213231200Smmarchive_acl_add_entry_len_l(struct archive_acl *acl,
214231200Smm    int type, int permset, int tag, int id, const char *name, size_t len,
215231200Smm    struct archive_string_conv *sc)
216231200Smm{
217231200Smm	struct archive_acl_entry *ap;
218231200Smm	int r;
219231200Smm
220231200Smm	if (acl_special(acl, type, permset, tag) == 0)
221231200Smm		return ARCHIVE_OK;
222231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
223231200Smm	if (ap == NULL) {
224231200Smm		/* XXX Error XXX */
225231200Smm		return ARCHIVE_FAILED;
226231200Smm	}
227231200Smm	if (name != NULL  &&  *name != '\0' && len > 0) {
228231200Smm		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
229231200Smm	} else {
230231200Smm		r = 0;
231231200Smm		archive_mstring_clean(&ap->name);
232231200Smm	}
233231200Smm	if (r == 0)
234231200Smm		return (ARCHIVE_OK);
235231200Smm	else if (errno == ENOMEM)
236231200Smm		return (ARCHIVE_FATAL);
237231200Smm	else
238231200Smm		return (ARCHIVE_WARN);
239231200Smm}
240231200Smm
241231200Smm/*
242231200Smm * If this ACL entry is part of the standard POSIX permissions set,
243231200Smm * store the permissions in the stat structure and return zero.
244231200Smm */
245231200Smmstatic int
246231200Smmacl_special(struct archive_acl *acl, int type, int permset, int tag)
247231200Smm{
248231200Smm	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
249231200Smm	    && ((permset & ~007) == 0)) {
250231200Smm		switch (tag) {
251231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
252231200Smm			acl->mode &= ~0700;
253231200Smm			acl->mode |= (permset & 7) << 6;
254231200Smm			return (0);
255231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
256231200Smm			acl->mode &= ~0070;
257231200Smm			acl->mode |= (permset & 7) << 3;
258231200Smm			return (0);
259231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
260231200Smm			acl->mode &= ~0007;
261231200Smm			acl->mode |= permset & 7;
262231200Smm			return (0);
263231200Smm		}
264231200Smm	}
265231200Smm	return (1);
266231200Smm}
267231200Smm
268231200Smm/*
269231200Smm * Allocate and populate a new ACL entry with everything but the
270231200Smm * name.
271231200Smm */
272231200Smmstatic struct archive_acl_entry *
273231200Smmacl_new_entry(struct archive_acl *acl,
274231200Smm    int type, int permset, int tag, int id)
275231200Smm{
276231200Smm	struct archive_acl_entry *ap, *aq;
277231200Smm
278231200Smm	/* Type argument must be a valid NFS4 or POSIX.1e type.
279231200Smm	 * The type must agree with anything already set and
280231200Smm	 * the permset must be compatible. */
281231200Smm	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
282231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
283231200Smm			return (NULL);
284231200Smm		}
285231200Smm		if (permset &
286231200Smm		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
287231200Smm			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
288231200Smm			return (NULL);
289231200Smm		}
290231200Smm	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
291231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
292231200Smm			return (NULL);
293231200Smm		}
294231200Smm		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
295231200Smm			return (NULL);
296231200Smm		}
297231200Smm	} else {
298231200Smm		return (NULL);
299231200Smm	}
300231200Smm
301231200Smm	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
302231200Smm	switch (tag) {
303231200Smm	case ARCHIVE_ENTRY_ACL_USER:
304231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
305231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
306231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
307231200Smm		/* Tags valid in both NFS4 and POSIX.1e */
308231200Smm		break;
309231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
310231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
311231200Smm		/* Tags valid only in POSIX.1e. */
312231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
313231200Smm			return (NULL);
314231200Smm		}
315231200Smm		break;
316231200Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
317231200Smm		/* Tags valid only in NFS4. */
318231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
319231200Smm			return (NULL);
320231200Smm		}
321231200Smm		break;
322231200Smm	default:
323231200Smm		/* No other values are valid. */
324231200Smm		return (NULL);
325231200Smm	}
326231200Smm
327231200Smm	if (acl->acl_text_w != NULL) {
328231200Smm		free(acl->acl_text_w);
329231200Smm		acl->acl_text_w = NULL;
330231200Smm	}
331231200Smm	if (acl->acl_text != NULL) {
332231200Smm		free(acl->acl_text);
333231200Smm		acl->acl_text = NULL;
334231200Smm	}
335231200Smm
336311041Smm	/*
337311041Smm	 * If there's a matching entry already in the list, overwrite it.
338311041Smm	 * NFSv4 entries may be repeated and are not overwritten.
339311041Smm	 *
340311041Smm	 * TODO: compare names of no id is provided (needs more rework)
341311041Smm	 */
342231200Smm	ap = acl->acl_head;
343231200Smm	aq = NULL;
344231200Smm	while (ap != NULL) {
345311041Smm		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
346311041Smm		    ap->type == type && ap->tag == tag && ap->id == id) {
347311041Smm			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
348311041Smm			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {
349311041Smm				ap->permset = permset;
350311041Smm				return (ap);
351311041Smm			}
352231200Smm		}
353231200Smm		aq = ap;
354231200Smm		ap = ap->next;
355231200Smm	}
356231200Smm
357231200Smm	/* Add a new entry to the end of the list. */
358311041Smm	ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
359231200Smm	if (ap == NULL)
360231200Smm		return (NULL);
361231200Smm	if (aq == NULL)
362231200Smm		acl->acl_head = ap;
363231200Smm	else
364231200Smm		aq->next = ap;
365231200Smm	ap->type = type;
366231200Smm	ap->tag = tag;
367231200Smm	ap->id = id;
368231200Smm	ap->permset = permset;
369231200Smm	acl->acl_types |= type;
370231200Smm	return (ap);
371231200Smm}
372231200Smm
373231200Smm/*
374231200Smm * Return a count of entries matching "want_type".
375231200Smm */
376231200Smmint
377231200Smmarchive_acl_count(struct archive_acl *acl, int want_type)
378231200Smm{
379231200Smm	int count;
380231200Smm	struct archive_acl_entry *ap;
381231200Smm
382231200Smm	count = 0;
383231200Smm	ap = acl->acl_head;
384231200Smm	while (ap != NULL) {
385231200Smm		if ((ap->type & want_type) != 0)
386231200Smm			count++;
387231200Smm		ap = ap->next;
388231200Smm	}
389231200Smm
390231200Smm	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
391231200Smm		count += 3;
392231200Smm	return (count);
393231200Smm}
394231200Smm
395231200Smm/*
396313570Smm * Return a bitmask of stored ACL types in an ACL list
397313570Smm */
398313570Smmint
399313570Smmarchive_acl_types(struct archive_acl *acl)
400313570Smm{
401313570Smm	return (acl->acl_types);
402313570Smm}
403313570Smm
404313570Smm/*
405231200Smm * Prepare for reading entries from the ACL data.  Returns a count
406231200Smm * of entries matching "want_type", or zero if there are no
407231200Smm * non-extended ACL entries of that type.
408231200Smm */
409231200Smmint
410231200Smmarchive_acl_reset(struct archive_acl *acl, int want_type)
411231200Smm{
412231200Smm	int count, cutoff;
413231200Smm
414231200Smm	count = archive_acl_count(acl, want_type);
415231200Smm
416231200Smm	/*
417231200Smm	 * If the only entries are the three standard ones,
418231200Smm	 * then don't return any ACL data.  (In this case,
419231200Smm	 * client can just use chmod(2) to set permissions.)
420231200Smm	 */
421231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
422231200Smm		cutoff = 3;
423231200Smm	else
424231200Smm		cutoff = 0;
425231200Smm
426231200Smm	if (count > cutoff)
427231200Smm		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
428231200Smm	else
429231200Smm		acl->acl_state = 0;
430231200Smm	acl->acl_p = acl->acl_head;
431231200Smm	return (count);
432231200Smm}
433231200Smm
434231200Smm
435231200Smm/*
436231200Smm * Return the next ACL entry in the list.  Fake entries for the
437231200Smm * standard permissions and include them in the returned list.
438231200Smm */
439231200Smmint
440313570Smmarchive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
441313570Smm    int *type, int *permset, int *tag, int *id, const char **name)
442231200Smm{
443231200Smm	*name = NULL;
444231200Smm	*id = -1;
445231200Smm
446231200Smm	/*
447231200Smm	 * The acl_state is either zero (no entries available), -1
448231200Smm	 * (reading from list), or an entry type (retrieve that type
449231200Smm	 * from ae_stat.aest_mode).
450231200Smm	 */
451231200Smm	if (acl->acl_state == 0)
452231200Smm		return (ARCHIVE_WARN);
453231200Smm
454231200Smm	/* The first three access entries are special. */
455231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
456231200Smm		switch (acl->acl_state) {
457231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
458231200Smm			*permset = (acl->mode >> 6) & 7;
459231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
460231200Smm			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
461231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
462231200Smm			return (ARCHIVE_OK);
463231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
464231200Smm			*permset = (acl->mode >> 3) & 7;
465231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
466231200Smm			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
467231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
468231200Smm			return (ARCHIVE_OK);
469231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
470231200Smm			*permset = acl->mode & 7;
471231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
472231200Smm			*tag = ARCHIVE_ENTRY_ACL_OTHER;
473231200Smm			acl->acl_state = -1;
474231200Smm			acl->acl_p = acl->acl_head;
475231200Smm			return (ARCHIVE_OK);
476231200Smm		default:
477231200Smm			break;
478231200Smm		}
479231200Smm	}
480231200Smm
481231200Smm	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
482231200Smm		acl->acl_p = acl->acl_p->next;
483231200Smm	if (acl->acl_p == NULL) {
484231200Smm		acl->acl_state = 0;
485231200Smm		*type = 0;
486231200Smm		*permset = 0;
487231200Smm		*tag = 0;
488231200Smm		*id = -1;
489231200Smm		*name = NULL;
490231200Smm		return (ARCHIVE_EOF); /* End of ACL entries. */
491231200Smm	}
492231200Smm	*type = acl->acl_p->type;
493231200Smm	*permset = acl->acl_p->permset;
494231200Smm	*tag = acl->acl_p->tag;
495231200Smm	*id = acl->acl_p->id;
496238856Smm	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
497238856Smm		if (errno == ENOMEM)
498238856Smm			return (ARCHIVE_FATAL);
499231200Smm		*name = NULL;
500238856Smm	}
501231200Smm	acl->acl_p = acl->acl_p->next;
502231200Smm	return (ARCHIVE_OK);
503231200Smm}
504231200Smm
505231200Smm/*
506313570Smm * Determine what type of ACL do we want
507231200Smm */
508313570Smmstatic int
509313570Smmarchive_acl_text_want_type(struct archive_acl *acl, int flags)
510231200Smm{
511313570Smm	int want_type;
512231200Smm
513313570Smm	/* Check if ACL is NFSv4 */
514313570Smm	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
515313570Smm		/* NFSv4 should never mix with POSIX.1e */
516313570Smm		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
517313570Smm			return (0);
518313570Smm		else
519313570Smm			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
520231200Smm	}
521231200Smm
522313570Smm	/* Now deal with POSIX.1e ACLs */
523313570Smm
524313570Smm	want_type = 0;
525313570Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
526313570Smm		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
527313570Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
528313570Smm		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
529313570Smm
530313570Smm	/* By default we want both access and default ACLs */
531313570Smm	if (want_type == 0)
532313570Smm		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
533313570Smm
534313570Smm	return (want_type);
535313570Smm}
536313570Smm
537313570Smm/*
538313570Smm * Calculate ACL text string length
539313570Smm */
540313570Smmstatic ssize_t
541313570Smmarchive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
542313570Smm    int wide, struct archive *a, struct archive_string_conv *sc) {
543313570Smm	struct archive_acl_entry *ap;
544313570Smm	const char *name;
545313570Smm	const wchar_t *wname;
546313570Smm	int count, idlen, tmp, r;
547313570Smm	ssize_t length;
548313570Smm	size_t len;
549313570Smm
550231200Smm	count = 0;
551231200Smm	length = 0;
552313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
553313570Smm		if ((ap->type & want_type) == 0)
554313570Smm			continue;
555313570Smm		/*
556313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
557313570Smm		 * ap->mode so they should not be in the list
558313570Smm		 */
559313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
560313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
561313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
562313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
563313570Smm			continue;
564313570Smm		count++;
565313570Smm		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
566313570Smm		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
567313570Smm			length += 8; /* "default:" */
568313570Smm		switch (ap->tag) {
569313570Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
570313570Smm			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
571313570Smm				length += 6; /* "owner@" */
572313570Smm				break;
573313570Smm			}
574313570Smm			/* FALLTHROUGH */
575313570Smm		case ARCHIVE_ENTRY_ACL_USER:
576313570Smm		case ARCHIVE_ENTRY_ACL_MASK:
577313570Smm			length += 4; /* "user", "mask" */
578313570Smm			break;
579313570Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
580313570Smm			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
581313570Smm				length += 6; /* "group@" */
582313570Smm				break;
583313570Smm			}
584313570Smm			/* FALLTHROUGH */
585313570Smm		case ARCHIVE_ENTRY_ACL_GROUP:
586313570Smm		case ARCHIVE_ENTRY_ACL_OTHER:
587313570Smm			length += 5; /* "group", "other" */
588313570Smm			break;
589313570Smm		case ARCHIVE_ENTRY_ACL_EVERYONE:
590313570Smm			length += 9; /* "everyone@" */
591313570Smm			break;
592313570Smm		}
593313570Smm		length += 1; /* colon after tag */
594313570Smm		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
595313570Smm		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
596313570Smm			if (wide) {
597313570Smm				r = archive_mstring_get_wcs(a, &ap->name,
598313570Smm				    &wname);
599313570Smm				if (r == 0 && wname != NULL)
600313570Smm					length += wcslen(wname);
601313570Smm				else if (r < 0 && errno == ENOMEM)
602313570Smm					return (0);
603313570Smm				else
604313570Smm					length += sizeof(uid_t) * 3 + 1;
605313570Smm			} else {
606313570Smm				r = archive_mstring_get_mbs_l(&ap->name, &name,
607313570Smm				    &len, sc);
608313570Smm				if (r != 0)
609313570Smm					return (0);
610313570Smm				if (len > 0 && name != NULL)
611313570Smm					length += len;
612313570Smm				else
613313570Smm					length += sizeof(uid_t) * 3 + 1;
614313570Smm			}
615313570Smm			length += 1; /* colon after user or group name */
616313570Smm		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
617313570Smm			length += 1; /* 2nd colon empty user,group or other */
618313570Smm
619313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
620313570Smm		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
621313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
622313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
623313570Smm			/* Solaris has no colon after other: and mask: */
624313570Smm			length = length - 1;
625313570Smm		}
626313570Smm
627313570Smm		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
628313570Smm			/* rwxpdDaARWcCos:fdinSFI:deny */
629313570Smm			length += 27;
630313570Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
631313570Smm				length += 1; /* allow, alarm, audit */
632313570Smm		} else
633231200Smm			length += 3; /* rwx */
634313570Smm
635313570Smm		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
636313570Smm		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
637313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
638231200Smm			length += 1; /* colon */
639313570Smm			/* ID digit count */
640313570Smm			idlen = 1;
641313570Smm			tmp = ap->id;
642313570Smm			while (tmp > 9) {
643313570Smm				tmp = tmp / 10;
644313570Smm				idlen++;
645313570Smm			}
646313570Smm			length += idlen;
647231200Smm		}
648313570Smm		length ++; /* entry separator */
649231200Smm	}
650231200Smm
651313570Smm	/* Add filemode-mapping access entries to the length */
652313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
653313570Smm		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
654313570Smm			/* "user::rwx\ngroup::rwx\nother:rwx\n" */
655313570Smm			length += 31;
656313570Smm		} else {
657313570Smm			/* "user::rwx\ngroup::rwx\nother::rwx\n" */
658313570Smm			length += 32;
659313570Smm		}
660313570Smm	} else if (count == 0)
661313570Smm		return (0);
662231200Smm
663313570Smm	/* The terminating character is included in count */
664313570Smm	return (length);
665313570Smm}
666313570Smm
667313570Smm/*
668313570Smm * Generate a wide text version of the ACL. The flags parameter controls
669313570Smm * the type and style of the generated ACL.
670313570Smm */
671313570Smmwchar_t *
672313570Smmarchive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
673313570Smm    struct archive *a)
674313570Smm{
675313570Smm	int count;
676313570Smm	ssize_t length;
677313570Smm	size_t len;
678313570Smm	const wchar_t *wname;
679313570Smm	const wchar_t *prefix;
680313570Smm	wchar_t separator;
681313570Smm	struct archive_acl_entry *ap;
682313570Smm	int id, r, want_type;
683313570Smm	wchar_t *wp, *ws;
684313570Smm
685313570Smm	want_type = archive_acl_text_want_type(acl, flags);
686313570Smm
687313570Smm	/* Both NFSv4 and POSIX.1 types found */
688313570Smm	if (want_type == 0)
689231200Smm		return (NULL);
690231200Smm
691313570Smm	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
692313570Smm		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
693313570Smm
694313570Smm	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
695313570Smm
696313570Smm	if (length == 0)
697313570Smm		return (NULL);
698313570Smm
699313570Smm	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
700313570Smm		separator = L',';
701313570Smm	else
702313570Smm		separator = L'\n';
703313570Smm
704231200Smm	/* Now, allocate the string and actually populate it. */
705313570Smm	wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
706313570Smm	if (wp == NULL) {
707313570Smm		if (errno == ENOMEM)
708313570Smm			__archive_errx(1, "No memory");
709238856Smm		return (NULL);
710313570Smm	}
711231200Smm	count = 0;
712313570Smm
713313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
714313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
715313570Smm		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
716231200Smm		    acl->mode & 0700, -1);
717313570Smm		*wp++ = separator;
718313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
719313570Smm		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
720231200Smm		    acl->mode & 0070, -1);
721313570Smm		*wp++ = separator;
722313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
723313570Smm		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
724231200Smm		    acl->mode & 0007, -1);
725231200Smm		count += 3;
726231200Smm	}
727231200Smm
728313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
729313570Smm		if ((ap->type & want_type) == 0)
730313570Smm			continue;
731313570Smm		/*
732313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
733313570Smm		 * ap->mode so they should not be in the list
734313570Smm		 */
735313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
736313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
737313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
738313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
739313570Smm			continue;
740313570Smm		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
741313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
742231200Smm			prefix = L"default:";
743231200Smm		else
744231200Smm			prefix = NULL;
745313570Smm		r = archive_mstring_get_wcs(a, &ap->name, &wname);
746313570Smm		if (r == 0) {
747313570Smm			if (count > 0)
748313570Smm				*wp++ = separator;
749313570Smm			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
750313570Smm				id = ap->id;
751313570Smm			else
752313570Smm				id = -1;
753313570Smm			append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
754313570Smm			    wname, ap->permset, id);
755313570Smm			count++;
756313570Smm		} else if (r < 0 && errno == ENOMEM)
757313570Smm			return (NULL);
758231200Smm	}
759231200Smm
760313570Smm	/* Add terminating character */
761313570Smm	*wp++ = L'\0';
762313570Smm
763313570Smm	len = wcslen(ws);
764313570Smm
765313570Smm	if ((ssize_t)len > (length - 1))
766313570Smm		__archive_errx(1, "Buffer overrun");
767313570Smm
768313570Smm	if (text_len != NULL)
769313570Smm		*text_len = len;
770313570Smm
771313570Smm	return (ws);
772231200Smm}
773231200Smm
774231200Smmstatic void
775231200Smmappend_id_w(wchar_t **wp, int id)
776231200Smm{
777231200Smm	if (id < 0)
778231200Smm		id = 0;
779231200Smm	if (id > 9)
780231200Smm		append_id_w(wp, id / 10);
781231200Smm	*(*wp)++ = L"0123456789"[id % 10];
782231200Smm}
783231200Smm
784231200Smmstatic void
785313570Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
786313570Smm    int tag, int flags, const wchar_t *wname, int perm, int id)
787231200Smm{
788313926Smm	int i;
789313926Smm
790231200Smm	if (prefix != NULL) {
791231200Smm		wcscpy(*wp, prefix);
792231200Smm		*wp += wcslen(*wp);
793231200Smm	}
794231200Smm	switch (tag) {
795231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
796231200Smm		wname = NULL;
797231200Smm		id = -1;
798313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
799313570Smm			wcscpy(*wp, L"owner@");
800313570Smm			break;
801313570Smm		}
802231200Smm		/* FALLTHROUGH */
803231200Smm	case ARCHIVE_ENTRY_ACL_USER:
804231200Smm		wcscpy(*wp, L"user");
805231200Smm		break;
806231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
807231200Smm		wname = NULL;
808231200Smm		id = -1;
809313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
810313570Smm			wcscpy(*wp, L"group@");
811313570Smm			break;
812313570Smm		}
813231200Smm		/* FALLTHROUGH */
814231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
815231200Smm		wcscpy(*wp, L"group");
816231200Smm		break;
817231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
818231200Smm		wcscpy(*wp, L"mask");
819231200Smm		wname = NULL;
820231200Smm		id = -1;
821231200Smm		break;
822231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
823231200Smm		wcscpy(*wp, L"other");
824231200Smm		wname = NULL;
825231200Smm		id = -1;
826231200Smm		break;
827313570Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
828313570Smm		wcscpy(*wp, L"everyone@");
829313570Smm		wname = NULL;
830313570Smm		id = -1;
831313570Smm		break;
832231200Smm	}
833231200Smm	*wp += wcslen(*wp);
834231200Smm	*(*wp)++ = L':';
835313570Smm	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
836313570Smm	    tag == ARCHIVE_ENTRY_ACL_USER ||
837313570Smm	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
838313570Smm		if (wname != NULL) {
839313570Smm			wcscpy(*wp, wname);
840313570Smm			*wp += wcslen(*wp);
841313570Smm		} else if (tag == ARCHIVE_ENTRY_ACL_USER
842313570Smm		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
843313570Smm			append_id_w(wp, id);
844313570Smm			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
845313570Smm				id = -1;
846313570Smm		}
847313570Smm		/* Solaris style has no second colon after other and mask */
848313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
849313570Smm		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
850313570Smm		    && tag != ARCHIVE_ENTRY_ACL_MASK))
851313570Smm			*(*wp)++ = L':';
852313570Smm	}
853313570Smm	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
854313570Smm		/* POSIX.1e ACL perms */
855313570Smm		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
856313570Smm		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
857313570Smm		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
858313570Smm	} else {
859313926Smm		/* NFSv4 ACL perms */
860313926Smm		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
861313926Smm			if (perm & nfsv4_acl_perm_map[i].perm)
862313926Smm				*(*wp)++ = nfsv4_acl_perm_map[i].wc;
863313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
864313926Smm				*(*wp)++ = L'-';
865313926Smm		}
866313570Smm		*(*wp)++ = L':';
867313926Smm		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
868313926Smm			if (perm & nfsv4_acl_flag_map[i].perm)
869313926Smm				*(*wp)++ = nfsv4_acl_flag_map[i].wc;
870313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
871313926Smm				*(*wp)++ = L'-';
872313926Smm		}
873313570Smm		*(*wp)++ = L':';
874313570Smm		switch (type) {
875313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
876313570Smm			wcscpy(*wp, L"allow");
877313570Smm			break;
878313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
879313570Smm			wcscpy(*wp, L"deny");
880313570Smm			break;
881313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
882313570Smm			wcscpy(*wp, L"audit");
883313570Smm			break;
884313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
885313570Smm			wcscpy(*wp, L"alarm");
886313570Smm			break;
887313570Smm		default:
888313570Smm			break;
889313570Smm		}
890231200Smm		*wp += wcslen(*wp);
891231200Smm	}
892231200Smm	if (id != -1) {
893231200Smm		*(*wp)++ = L':';
894231200Smm		append_id_w(wp, id);
895231200Smm	}
896231200Smm}
897231200Smm
898313570Smm/*
899313570Smm * Generate a text version of the ACL. The flags parameter controls
900313570Smm * the type and style of the generated ACL.
901313570Smm */
902313570Smmchar *
903313570Smmarchive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
904231200Smm    struct archive_string_conv *sc)
905231200Smm{
906231200Smm	int count;
907313570Smm	ssize_t length;
908313570Smm	size_t len;
909231200Smm	const char *name;
910231200Smm	const char *prefix;
911231200Smm	char separator;
912231200Smm	struct archive_acl_entry *ap;
913313570Smm	int id, r, want_type;
914313570Smm	char *p, *s;
915231200Smm
916313570Smm	want_type = archive_acl_text_want_type(acl, flags);
917231200Smm
918313570Smm	/* Both NFSv4 and POSIX.1 types found */
919313570Smm	if (want_type == 0)
920313570Smm		return (NULL);
921231200Smm
922313570Smm	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
923313570Smm		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
924231200Smm
925313570Smm	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
926231200Smm
927313570Smm	if (length == 0)
928313570Smm		return (NULL);
929313570Smm
930313570Smm	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
931313570Smm		separator = ',';
932313570Smm	else
933313570Smm		separator = '\n';
934313570Smm
935231200Smm	/* Now, allocate the string and actually populate it. */
936313570Smm	p = s = (char *)malloc(length * sizeof(char));
937313570Smm	if (p == NULL) {
938313570Smm		if (errno == ENOMEM)
939313570Smm			__archive_errx(1, "No memory");
940313570Smm		return (NULL);
941313570Smm	}
942231200Smm	count = 0;
943313570Smm
944313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
945313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
946313570Smm		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
947231200Smm		    acl->mode & 0700, -1);
948313570Smm		*p++ = separator;
949313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
950313570Smm		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
951231200Smm		    acl->mode & 0070, -1);
952313570Smm		*p++ = separator;
953313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
954313570Smm		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
955231200Smm		    acl->mode & 0007, -1);
956231200Smm		count += 3;
957231200Smm	}
958231200Smm
959313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
960313570Smm		if ((ap->type & want_type) == 0)
961313570Smm			continue;
962313570Smm		/*
963313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
964313570Smm		 * ap->mode so they should not be in the list
965313570Smm		 */
966313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
967313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
968313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
969313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
970313570Smm			continue;
971313570Smm		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
972313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
973231200Smm			prefix = "default:";
974231200Smm		else
975231200Smm			prefix = NULL;
976313570Smm		r = archive_mstring_get_mbs_l(
977313570Smm		    &ap->name, &name, &len, sc);
978313570Smm		if (r != 0)
979313570Smm			return (NULL);
980313570Smm		if (count > 0)
981313570Smm			*p++ = separator;
982313570Smm		if (name == NULL ||
983313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
984313570Smm			id = ap->id;
985313570Smm		} else {
986313570Smm			id = -1;
987231200Smm		}
988313570Smm		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
989313570Smm		    ap->permset, id);
990313570Smm		count++;
991231200Smm	}
992231200Smm
993313570Smm	/* Add terminating character */
994313570Smm	*p++ = '\0';
995313570Smm
996313570Smm	len = strlen(s);
997313570Smm
998313570Smm	if ((ssize_t)len > (length - 1))
999313570Smm		__archive_errx(1, "Buffer overrun");
1000313570Smm
1001313570Smm	if (text_len != NULL)
1002313570Smm		*text_len = len;
1003313570Smm
1004313570Smm	return (s);
1005231200Smm}
1006231200Smm
1007231200Smmstatic void
1008231200Smmappend_id(char **p, int id)
1009231200Smm{
1010231200Smm	if (id < 0)
1011231200Smm		id = 0;
1012231200Smm	if (id > 9)
1013231200Smm		append_id(p, id / 10);
1014231200Smm	*(*p)++ = "0123456789"[id % 10];
1015231200Smm}
1016231200Smm
1017231200Smmstatic void
1018313570Smmappend_entry(char **p, const char *prefix, int type,
1019313570Smm    int tag, int flags, const char *name, int perm, int id)
1020231200Smm{
1021313926Smm	int i;
1022313926Smm
1023231200Smm	if (prefix != NULL) {
1024231200Smm		strcpy(*p, prefix);
1025231200Smm		*p += strlen(*p);
1026231200Smm	}
1027231200Smm	switch (tag) {
1028231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
1029231200Smm		name = NULL;
1030231200Smm		id = -1;
1031313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1032313570Smm			strcpy(*p, "owner@");
1033313570Smm			break;
1034313570Smm		}
1035231200Smm		/* FALLTHROUGH */
1036231200Smm	case ARCHIVE_ENTRY_ACL_USER:
1037231200Smm		strcpy(*p, "user");
1038231200Smm		break;
1039231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1040231200Smm		name = NULL;
1041231200Smm		id = -1;
1042313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1043313570Smm			strcpy(*p, "group@");
1044313570Smm			break;
1045313570Smm		}
1046231200Smm		/* FALLTHROUGH */
1047231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
1048231200Smm		strcpy(*p, "group");
1049231200Smm		break;
1050231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
1051231200Smm		strcpy(*p, "mask");
1052231200Smm		name = NULL;
1053231200Smm		id = -1;
1054231200Smm		break;
1055231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
1056231200Smm		strcpy(*p, "other");
1057231200Smm		name = NULL;
1058231200Smm		id = -1;
1059231200Smm		break;
1060313570Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
1061313570Smm		strcpy(*p, "everyone@");
1062313570Smm		name = NULL;
1063313570Smm		id = -1;
1064313570Smm		break;
1065231200Smm	}
1066231200Smm	*p += strlen(*p);
1067231200Smm	*(*p)++ = ':';
1068313570Smm	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1069313570Smm	    tag == ARCHIVE_ENTRY_ACL_USER ||
1070313570Smm	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1071313570Smm		if (name != NULL) {
1072313570Smm			strcpy(*p, name);
1073313570Smm			*p += strlen(*p);
1074313570Smm		} else if (tag == ARCHIVE_ENTRY_ACL_USER
1075313570Smm		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1076313570Smm			append_id(p, id);
1077313570Smm			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1078313570Smm				id = -1;
1079313570Smm		}
1080313570Smm		/* Solaris style has no second colon after other and mask */
1081313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1082313570Smm		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
1083313570Smm		    && tag != ARCHIVE_ENTRY_ACL_MASK))
1084313570Smm			*(*p)++ = ':';
1085313570Smm	}
1086313570Smm	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1087313570Smm		/* POSIX.1e ACL perms */
1088313570Smm		*(*p)++ = (perm & 0444) ? 'r' : '-';
1089313570Smm		*(*p)++ = (perm & 0222) ? 'w' : '-';
1090313570Smm		*(*p)++ = (perm & 0111) ? 'x' : '-';
1091313570Smm	} else {
1092313926Smm		/* NFSv4 ACL perms */
1093313926Smm		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1094313926Smm			if (perm & nfsv4_acl_perm_map[i].perm)
1095313926Smm				*(*p)++ = nfsv4_acl_perm_map[i].c;
1096313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1097313926Smm				*(*p)++ = '-';
1098313926Smm		}
1099313570Smm		*(*p)++ = ':';
1100313926Smm		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1101313926Smm			if (perm & nfsv4_acl_flag_map[i].perm)
1102313926Smm				*(*p)++ = nfsv4_acl_flag_map[i].c;
1103313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1104313926Smm				*(*p)++ = '-';
1105313926Smm		}
1106313570Smm		*(*p)++ = ':';
1107313570Smm		switch (type) {
1108313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1109313570Smm			strcpy(*p, "allow");
1110313570Smm			break;
1111313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1112313570Smm			strcpy(*p, "deny");
1113313570Smm			break;
1114313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1115313570Smm			strcpy(*p, "audit");
1116313570Smm			break;
1117313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1118313570Smm			strcpy(*p, "alarm");
1119313570Smm			break;
1120313570Smm		}
1121231200Smm		*p += strlen(*p);
1122231200Smm	}
1123231200Smm	if (id != -1) {
1124231200Smm		*(*p)++ = ':';
1125231200Smm		append_id(p, id);
1126231200Smm	}
1127231200Smm}
1128231200Smm
1129231200Smm/*
1130313570Smm * Parse a wide ACL text string.
1131313570Smm *
1132313570Smm * The want_type argument may be one of the following:
1133313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1134313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1135313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1136313570Smm *
1137313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as
1138313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1139231200Smm */
1140231200Smmint
1141313570Smmarchive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1142313570Smm    int want_type)
1143231200Smm{
1144231200Smm	struct {
1145231200Smm		const wchar_t *start;
1146231200Smm		const wchar_t *end;
1147313570Smm	} field[6], name;
1148231200Smm
1149313570Smm	const wchar_t *s, *st;
1150313570Smm
1151313570Smm	int numfields, fields, n, r, sol, ret;
1152313570Smm	int type, types, tag, permset, id;
1153313570Smm	size_t len;
1154231200Smm	wchar_t sep;
1155231200Smm
1156313570Smm	ret = ARCHIVE_OK;
1157313570Smm	types = 0;
1158313570Smm
1159313570Smm	switch (want_type) {
1160313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1161313570Smm		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1162328827Smm		__LA_FALLTHROUGH;
1163313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1164313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1165313570Smm		numfields = 5;
1166313570Smm		break;
1167313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1168313570Smm		numfields = 6;
1169313570Smm		break;
1170313570Smm	default:
1171313570Smm		return (ARCHIVE_FATAL);
1172313570Smm	}
1173313570Smm
1174313570Smm	while (text != NULL && *text != L'\0') {
1175231200Smm		/*
1176231200Smm		 * Parse the fields out of the next entry,
1177231200Smm		 * advance 'text' to start of next entry.
1178231200Smm		 */
1179231200Smm		fields = 0;
1180231200Smm		do {
1181231200Smm			const wchar_t *start, *end;
1182231200Smm			next_field_w(&text, &start, &end, &sep);
1183313570Smm			if (fields < numfields) {
1184231200Smm				field[fields].start = start;
1185231200Smm				field[fields].end = end;
1186231200Smm			}
1187231200Smm			++fields;
1188231200Smm		} while (sep == L':');
1189231200Smm
1190231200Smm		/* Set remaining fields to blank. */
1191313570Smm		for (n = fields; n < numfields; ++n)
1192231200Smm			field[n].start = field[n].end = NULL;
1193231200Smm
1194313570Smm		if (field[0].start != NULL && *(field[0].start) == L'#') {
1195313570Smm			/* Comment, skip entry */
1196313570Smm			continue;
1197313570Smm		}
1198313570Smm
1199313570Smm		n = 0;
1200313570Smm		sol = 0;
1201231200Smm		id = -1;
1202313570Smm		permset = 0;
1203313570Smm		name.start = name.end = NULL;
1204231200Smm
1205313570Smm		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1206313570Smm			/* POSIX.1e ACLs */
1207313570Smm			/*
1208313570Smm			 * Default keyword "default:user::rwx"
1209313570Smm			 * if found, we have one more field
1210313570Smm			 *
1211313570Smm			 * We also support old Solaris extension:
1212313570Smm			 * "defaultuser::rwx" is the default ACL corresponding
1213313570Smm			 * to "user::rwx", etc. valid only for first field
1214313570Smm			 */
1215313570Smm			s = field[0].start;
1216313570Smm			len = field[0].end - field[0].start;
1217313570Smm			if (*s == L'd' && (len == 1 || (len >= 7
1218313570Smm			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
1219313570Smm				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1220313570Smm				if (len > 7)
1221313570Smm					field[0].start += 7;
1222313570Smm				else
1223313570Smm					n = 1;
1224313570Smm			} else
1225313570Smm				type = want_type;
1226231200Smm
1227313570Smm			/* Check for a numeric ID in field n+1 or n+3. */
1228313570Smm			isint_w(field[n + 1].start, field[n + 1].end, &id);
1229313570Smm			/* Field n+3 is optional. */
1230313570Smm			if (id == -1 && fields > n+3)
1231313570Smm				isint_w(field[n + 3].start, field[n + 3].end,
1232313570Smm				    &id);
1233313570Smm
1234313570Smm			tag = 0;
1235313570Smm			s = field[n].start;
1236313570Smm			st = field[n].start + 1;
1237313570Smm			len = field[n].end - field[n].start;
1238313570Smm
1239313570Smm			switch (*s) {
1240313570Smm			case L'u':
1241313570Smm				if (len == 1 || (len == 4
1242313570Smm				    && wmemcmp(st, L"ser", 3) == 0))
1243313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1244313570Smm				break;
1245313570Smm			case L'g':
1246313570Smm				if (len == 1 || (len == 5
1247313570Smm				    && wmemcmp(st, L"roup", 4) == 0))
1248313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1249313570Smm				break;
1250313570Smm			case L'o':
1251313570Smm				if (len == 1 || (len == 5
1252313570Smm				    && wmemcmp(st, L"ther", 4) == 0))
1253313570Smm					tag = ARCHIVE_ENTRY_ACL_OTHER;
1254313570Smm				break;
1255313570Smm			case L'm':
1256313570Smm				if (len == 1 || (len == 4
1257313570Smm				    && wmemcmp(st, L"ask", 3) == 0))
1258313570Smm					tag = ARCHIVE_ENTRY_ACL_MASK;
1259313570Smm				break;
1260313570Smm			default:
1261313570Smm					break;
1262313570Smm			}
1263313570Smm
1264313570Smm			switch (tag) {
1265313570Smm			case ARCHIVE_ENTRY_ACL_OTHER:
1266313570Smm			case ARCHIVE_ENTRY_ACL_MASK:
1267313570Smm				if (fields == (n + 2)
1268313570Smm				    && field[n + 1].start < field[n + 1].end
1269313570Smm				    && ismode_w(field[n + 1].start,
1270313570Smm				    field[n + 1].end, &permset)) {
1271313570Smm					/* This is Solaris-style "other:rwx" */
1272313570Smm					sol = 1;
1273313570Smm				} else if (fields == (n + 3) &&
1274313570Smm				    field[n + 1].start < field[n + 1].end) {
1275313570Smm					/* Invalid mask or other field */
1276313570Smm					ret = ARCHIVE_WARN;
1277313570Smm					continue;
1278313570Smm				}
1279313570Smm				break;
1280313570Smm			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1281313570Smm			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1282313570Smm				if (id != -1 ||
1283313570Smm				    field[n + 1].start < field[n + 1].end) {
1284313570Smm					name = field[n + 1];
1285313570Smm					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1286313570Smm						tag = ARCHIVE_ENTRY_ACL_USER;
1287313570Smm					else
1288313570Smm						tag = ARCHIVE_ENTRY_ACL_GROUP;
1289313570Smm				}
1290313570Smm				break;
1291313570Smm			default:
1292313570Smm				/* Invalid tag, skip entry */
1293313570Smm				ret = ARCHIVE_WARN;
1294313570Smm				continue;
1295313570Smm			}
1296313570Smm
1297313570Smm			/*
1298313570Smm			 * Without "default:" we expect mode in field 2
1299313570Smm			 * Exception: Solaris other and mask fields
1300313570Smm			 */
1301313570Smm			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1302313570Smm			    field[n + 2 - sol].end, &permset)) {
1303313570Smm				/* Invalid mode, skip entry */
1304313570Smm				ret = ARCHIVE_WARN;
1305313570Smm				continue;
1306313570Smm			}
1307313570Smm		} else {
1308313570Smm			/* NFS4 ACLs */
1309313570Smm			s = field[0].start;
1310313570Smm			len = field[0].end - field[0].start;
1311313570Smm			tag = 0;
1312313570Smm
1313313570Smm			switch (len) {
1314313570Smm			case 4:
1315313570Smm				if (wmemcmp(s, L"user", 4) == 0)
1316313570Smm					tag = ARCHIVE_ENTRY_ACL_USER;
1317313570Smm				break;
1318313570Smm			case 5:
1319313570Smm				if (wmemcmp(s, L"group", 5) == 0)
1320313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP;
1321313570Smm				break;
1322313570Smm			case 6:
1323313570Smm				if (wmemcmp(s, L"owner@", 6) == 0)
1324313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1325313570Smm				else if (wmemcmp(s, L"group@", len) == 0)
1326313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1327313570Smm				break;
1328313570Smm			case 9:
1329313570Smm				if (wmemcmp(s, L"everyone@", 9) == 0)
1330313570Smm					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1331313570Smm			default:
1332313570Smm				break;
1333313570Smm			}
1334313570Smm
1335313570Smm			if (tag == 0) {
1336313570Smm				/* Invalid tag, skip entry */
1337313570Smm				ret = ARCHIVE_WARN;
1338313570Smm				continue;
1339313570Smm			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1340313570Smm			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1341313570Smm				n = 1;
1342231200Smm				name = field[1];
1343313570Smm				isint_w(name.start, name.end, &id);
1344231200Smm			} else
1345313570Smm				n = 0;
1346231200Smm
1347313570Smm			if (!is_nfs4_perms_w(field[1 + n].start,
1348313570Smm			    field[1 + n].end, &permset)) {
1349313570Smm				/* Invalid NFSv4 perms, skip entry */
1350313570Smm				ret = ARCHIVE_WARN;
1351313570Smm				continue;
1352313570Smm			}
1353313570Smm			if (!is_nfs4_flags_w(field[2 + n].start,
1354313570Smm			    field[2 + n].end, &permset)) {
1355313570Smm				/* Invalid NFSv4 flags, skip entry */
1356313570Smm				ret = ARCHIVE_WARN;
1357313570Smm				continue;
1358313570Smm			}
1359313570Smm			s = field[3 + n].start;
1360313570Smm			len = field[3 + n].end - field[3 + n].start;
1361313570Smm			type = 0;
1362313570Smm			if (len == 4) {
1363313570Smm				if (wmemcmp(s, L"deny", 4) == 0)
1364313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1365313570Smm			} else if (len == 5) {
1366313570Smm				if (wmemcmp(s, L"allow", 5) == 0)
1367313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1368313570Smm				else if (wmemcmp(s, L"audit", 5) == 0)
1369313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1370313570Smm				else if (wmemcmp(s, L"alarm", 5) == 0)
1371313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1372313570Smm			}
1373313570Smm			if (type == 0) {
1374313570Smm				/* Invalid entry type, skip entry */
1375313570Smm				ret = ARCHIVE_WARN;
1376313570Smm				continue;
1377313570Smm			}
1378313570Smm			isint_w(field[4 + n].start, field[4 + n].end, &id);
1379313570Smm		}
1380313570Smm
1381231200Smm		/* Add entry to the internal list. */
1382313570Smm		r = archive_acl_add_entry_w_len(acl, type, permset,
1383231200Smm		    tag, id, name.start, name.end - name.start);
1384313570Smm		if (r < ARCHIVE_WARN)
1385313570Smm			return (r);
1386313570Smm		if (r != ARCHIVE_OK)
1387313570Smm			ret = ARCHIVE_WARN;
1388313570Smm		types |= type;
1389231200Smm	}
1390313570Smm
1391313570Smm	/* Reset ACL */
1392313570Smm	archive_acl_reset(acl, types);
1393313570Smm
1394313570Smm	return (ret);
1395231200Smm}
1396231200Smm
1397231200Smm/*
1398231200Smm * Parse a string to a positive decimal integer.  Returns true if
1399231200Smm * the string is non-empty and consists only of decimal digits,
1400231200Smm * false otherwise.
1401231200Smm */
1402231200Smmstatic int
1403231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result)
1404231200Smm{
1405231200Smm	int n = 0;
1406231200Smm	if (start >= end)
1407231200Smm		return (0);
1408231200Smm	while (start < end) {
1409231200Smm		if (*start < '0' || *start > '9')
1410231200Smm			return (0);
1411231200Smm		if (n > (INT_MAX / 10) ||
1412231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1413231200Smm			n = INT_MAX;
1414231200Smm		} else {
1415231200Smm			n *= 10;
1416231200Smm			n += *start - '0';
1417231200Smm		}
1418231200Smm		start++;
1419231200Smm	}
1420231200Smm	*result = n;
1421231200Smm	return (1);
1422231200Smm}
1423231200Smm
1424231200Smm/*
1425231200Smm * Parse a string as a mode field.  Returns true if
1426231200Smm * the string is non-empty and consists only of mode characters,
1427231200Smm * false otherwise.
1428231200Smm */
1429231200Smmstatic int
1430231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1431231200Smm{
1432231200Smm	const wchar_t *p;
1433231200Smm
1434231200Smm	if (start >= end)
1435231200Smm		return (0);
1436231200Smm	p = start;
1437231200Smm	*permset = 0;
1438231200Smm	while (p < end) {
1439231200Smm		switch (*p++) {
1440313570Smm		case L'r': case L'R':
1441231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1442231200Smm			break;
1443313570Smm		case L'w': case L'W':
1444231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1445231200Smm			break;
1446313570Smm		case L'x': case L'X':
1447231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1448231200Smm			break;
1449313570Smm		case L'-':
1450231200Smm			break;
1451231200Smm		default:
1452231200Smm			return (0);
1453231200Smm		}
1454231200Smm	}
1455231200Smm	return (1);
1456231200Smm}
1457231200Smm
1458231200Smm/*
1459313570Smm * Parse a string as a NFS4 ACL permission field.
1460313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1461313570Smm * permission characters, false otherwise
1462313570Smm */
1463313570Smmstatic int
1464313570Smmis_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1465313570Smm{
1466313926Smm	const wchar_t *p = start;
1467313570Smm
1468313570Smm	while (p < end) {
1469313570Smm		switch (*p++) {
1470313570Smm		case L'r':
1471313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1472313570Smm			break;
1473313570Smm		case L'w':
1474313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1475313570Smm			break;
1476313570Smm		case L'x':
1477313570Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1478313570Smm			break;
1479313570Smm		case L'p':
1480313570Smm			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1481313570Smm			break;
1482313570Smm		case L'D':
1483313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1484313570Smm			break;
1485313570Smm		case L'd':
1486313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1487313570Smm			break;
1488313570Smm		case L'a':
1489313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1490313570Smm			break;
1491313570Smm		case L'A':
1492313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1493313570Smm			break;
1494313570Smm		case L'R':
1495313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1496313570Smm			break;
1497313570Smm		case L'W':
1498313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1499313570Smm			break;
1500313570Smm		case L'c':
1501313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1502313570Smm			break;
1503313570Smm		case L'C':
1504313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1505313570Smm			break;
1506313570Smm		case L'o':
1507313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1508313570Smm			break;
1509313570Smm		case L's':
1510313570Smm			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1511313570Smm			break;
1512313570Smm		case L'-':
1513313570Smm			break;
1514313570Smm		default:
1515313570Smm			return(0);
1516313570Smm		}
1517313570Smm	}
1518313570Smm	return (1);
1519313570Smm}
1520313570Smm
1521313570Smm/*
1522313570Smm * Parse a string as a NFS4 ACL flags field.
1523313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1524313570Smm * flag characters, false otherwise
1525313570Smm */
1526313570Smmstatic int
1527313570Smmis_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1528313570Smm{
1529313926Smm	const wchar_t *p = start;
1530313570Smm
1531313570Smm	while (p < end) {
1532313570Smm		switch(*p++) {
1533313570Smm		case L'f':
1534313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1535313570Smm			break;
1536313570Smm		case L'd':
1537313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1538313570Smm			break;
1539313570Smm		case L'i':
1540313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1541313570Smm			break;
1542313570Smm		case L'n':
1543313570Smm			*permset |=
1544313570Smm			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1545313570Smm			break;
1546313570Smm		case L'S':
1547313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1548313570Smm			break;
1549313570Smm		case L'F':
1550313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1551313570Smm			break;
1552313570Smm		case L'I':
1553313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1554313570Smm			break;
1555313570Smm		case L'-':
1556313570Smm			break;
1557313570Smm		default:
1558313570Smm			return (0);
1559313570Smm		}
1560313570Smm	}
1561313570Smm	return (1);
1562313570Smm}
1563313570Smm
1564313570Smm/*
1565231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1566231200Smm * to point to just after the separator.  *start points to the first
1567231200Smm * character of the matched text and *end just after the last
1568231200Smm * character of the matched identifier.  In particular *end - *start
1569231200Smm * is the length of the field body, not including leading or trailing
1570231200Smm * whitespace.
1571231200Smm */
1572231200Smmstatic void
1573231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start,
1574231200Smm    const wchar_t **end, wchar_t *sep)
1575231200Smm{
1576231200Smm	/* Skip leading whitespace to find start of field. */
1577231200Smm	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1578231200Smm		(*wp)++;
1579231200Smm	}
1580231200Smm	*start = *wp;
1581231200Smm
1582231200Smm	/* Scan for the separator. */
1583231200Smm	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1584231200Smm	    **wp != L'\n') {
1585231200Smm		(*wp)++;
1586231200Smm	}
1587231200Smm	*sep = **wp;
1588231200Smm
1589231200Smm	/* Trim trailing whitespace to locate end of field. */
1590231200Smm	*end = *wp - 1;
1591231200Smm	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1592231200Smm		(*end)--;
1593231200Smm	}
1594231200Smm	(*end)++;
1595231200Smm
1596231200Smm	/* Adjust scanner location. */
1597231200Smm	if (**wp != L'\0')
1598231200Smm		(*wp)++;
1599231200Smm}
1600231200Smm
1601231200Smm/*
1602313570Smm * Parse an ACL text string.
1603313570Smm *
1604313570Smm * The want_type argument may be one of the following:
1605313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1606313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1607313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1608313570Smm *
1609313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as
1610313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1611231200Smm */
1612231200Smmint
1613313570Smmarchive_acl_from_text_l(struct archive_acl *acl, const char *text,
1614313570Smm    int want_type, struct archive_string_conv *sc)
1615231200Smm{
1616231200Smm	struct {
1617231200Smm		const char *start;
1618231200Smm		const char *end;
1619313570Smm	} field[6], name;
1620231200Smm
1621313570Smm	const char *s, *st;
1622313570Smm	int numfields, fields, n, r, sol, ret;
1623313570Smm	int type, types, tag, permset, id;
1624313570Smm	size_t len;
1625231200Smm	char sep;
1626231200Smm
1627313570Smm	switch (want_type) {
1628313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1629313570Smm		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1630328827Smm		__LA_FALLTHROUGH;
1631313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1632313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1633313570Smm		numfields = 5;
1634313570Smm		break;
1635313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1636313570Smm		numfields = 6;
1637313570Smm		break;
1638313570Smm	default:
1639313570Smm		return (ARCHIVE_FATAL);
1640313570Smm	}
1641313570Smm
1642313570Smm	ret = ARCHIVE_OK;
1643313570Smm	types = 0;
1644313570Smm
1645231200Smm	while (text != NULL  &&  *text != '\0') {
1646231200Smm		/*
1647231200Smm		 * Parse the fields out of the next entry,
1648231200Smm		 * advance 'text' to start of next entry.
1649231200Smm		 */
1650231200Smm		fields = 0;
1651231200Smm		do {
1652231200Smm			const char *start, *end;
1653231200Smm			next_field(&text, &start, &end, &sep);
1654313570Smm			if (fields < numfields) {
1655231200Smm				field[fields].start = start;
1656231200Smm				field[fields].end = end;
1657231200Smm			}
1658231200Smm			++fields;
1659231200Smm		} while (sep == ':');
1660231200Smm
1661231200Smm		/* Set remaining fields to blank. */
1662313570Smm		for (n = fields; n < numfields; ++n)
1663231200Smm			field[n].start = field[n].end = NULL;
1664231200Smm
1665313570Smm		if (field[0].start != NULL && *(field[0].start) == '#') {
1666313570Smm			/* Comment, skip entry */
1667313570Smm			continue;
1668313570Smm		}
1669313570Smm
1670313570Smm		n = 0;
1671313570Smm		sol = 0;
1672231200Smm		id = -1;
1673313570Smm		permset = 0;
1674313570Smm		name.start = name.end = NULL;
1675231200Smm
1676313570Smm		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1677313570Smm			/* POSIX.1e ACLs */
1678313570Smm			/*
1679313570Smm			 * Default keyword "default:user::rwx"
1680313570Smm			 * if found, we have one more field
1681313570Smm			 *
1682313570Smm			 * We also support old Solaris extension:
1683313570Smm			 * "defaultuser::rwx" is the default ACL corresponding
1684313570Smm			 * to "user::rwx", etc. valid only for first field
1685313570Smm			 */
1686313570Smm			s = field[0].start;
1687313570Smm			len = field[0].end - field[0].start;
1688313570Smm			if (*s == 'd' && (len == 1 || (len >= 7
1689313570Smm			    && memcmp((s + 1), "efault", 6) == 0))) {
1690313570Smm				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1691313570Smm				if (len > 7)
1692313570Smm					field[0].start += 7;
1693313570Smm				else
1694313570Smm					n = 1;
1695313570Smm			} else
1696313570Smm				type = want_type;
1697231200Smm
1698313570Smm			/* Check for a numeric ID in field n+1 or n+3. */
1699313570Smm			isint(field[n + 1].start, field[n + 1].end, &id);
1700313570Smm			/* Field n+3 is optional. */
1701313570Smm			if (id == -1 && fields > (n + 3))
1702313570Smm				isint(field[n + 3].start, field[n + 3].end,
1703313570Smm				    &id);
1704313570Smm
1705313570Smm			tag = 0;
1706313570Smm			s = field[n].start;
1707313570Smm			st = field[n].start + 1;
1708313570Smm			len = field[n].end - field[n].start;
1709313570Smm
1710313570Smm			switch (*s) {
1711313570Smm			case 'u':
1712313570Smm				if (len == 1 || (len == 4
1713313570Smm				    && memcmp(st, "ser", 3) == 0))
1714313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1715313570Smm				break;
1716313570Smm			case 'g':
1717313570Smm				if (len == 1 || (len == 5
1718313570Smm				    && memcmp(st, "roup", 4) == 0))
1719313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1720313570Smm				break;
1721313570Smm			case 'o':
1722313570Smm				if (len == 1 || (len == 5
1723313570Smm				    && memcmp(st, "ther", 4) == 0))
1724313570Smm					tag = ARCHIVE_ENTRY_ACL_OTHER;
1725313570Smm				break;
1726313570Smm			case 'm':
1727313570Smm				if (len == 1 || (len == 4
1728313570Smm				    && memcmp(st, "ask", 3) == 0))
1729313570Smm					tag = ARCHIVE_ENTRY_ACL_MASK;
1730313570Smm				break;
1731313570Smm			default:
1732313570Smm					break;
1733313570Smm			}
1734313570Smm
1735313570Smm			switch (tag) {
1736313570Smm			case ARCHIVE_ENTRY_ACL_OTHER:
1737313570Smm			case ARCHIVE_ENTRY_ACL_MASK:
1738313570Smm				if (fields == (n + 2)
1739313570Smm				    && field[n + 1].start < field[n + 1].end
1740313570Smm				    && ismode(field[n + 1].start,
1741313570Smm				    field[n + 1].end, &permset)) {
1742313570Smm					/* This is Solaris-style "other:rwx" */
1743313570Smm					sol = 1;
1744313570Smm				} else if (fields == (n + 3) &&
1745313570Smm				    field[n + 1].start < field[n + 1].end) {
1746313570Smm					/* Invalid mask or other field */
1747313570Smm					ret = ARCHIVE_WARN;
1748313570Smm					continue;
1749313570Smm				}
1750313570Smm				break;
1751313570Smm			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1752313570Smm			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1753313570Smm				if (id != -1 ||
1754313570Smm				    field[n + 1].start < field[n + 1].end) {
1755313570Smm					name = field[n + 1];
1756313570Smm					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1757313570Smm						tag = ARCHIVE_ENTRY_ACL_USER;
1758313570Smm					else
1759313570Smm						tag = ARCHIVE_ENTRY_ACL_GROUP;
1760313570Smm				}
1761313570Smm				break;
1762313570Smm			default:
1763313570Smm				/* Invalid tag, skip entry */
1764313570Smm				ret = ARCHIVE_WARN;
1765313570Smm				continue;
1766313570Smm			}
1767313570Smm
1768313570Smm			/*
1769313570Smm			 * Without "default:" we expect mode in field 3
1770313570Smm			 * Exception: Solaris other and mask fields
1771313570Smm			 */
1772313570Smm			if (permset == 0 && !ismode(field[n + 2 - sol].start,
1773313570Smm			    field[n + 2 - sol].end, &permset)) {
1774313570Smm				/* Invalid mode, skip entry */
1775313570Smm				ret = ARCHIVE_WARN;
1776313570Smm				continue;
1777313570Smm			}
1778313570Smm		} else {
1779313570Smm			/* NFS4 ACLs */
1780313570Smm			s = field[0].start;
1781313570Smm			len = field[0].end - field[0].start;
1782313570Smm			tag = 0;
1783313570Smm
1784313570Smm			switch (len) {
1785313570Smm			case 4:
1786313570Smm				if (memcmp(s, "user", 4) == 0)
1787313570Smm					tag = ARCHIVE_ENTRY_ACL_USER;
1788313570Smm				break;
1789313570Smm			case 5:
1790313570Smm				if (memcmp(s, "group", 5) == 0)
1791313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP;
1792313570Smm				break;
1793313570Smm			case 6:
1794313570Smm				if (memcmp(s, "owner@", 6) == 0)
1795313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1796313570Smm				else if (memcmp(s, "group@", 6) == 0)
1797313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1798313570Smm				break;
1799313570Smm			case 9:
1800313570Smm				if (memcmp(s, "everyone@", 9) == 0)
1801313570Smm					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1802313570Smm				break;
1803313570Smm			default:
1804313570Smm				break;
1805313570Smm			}
1806313570Smm
1807313570Smm			if (tag == 0) {
1808313570Smm				/* Invalid tag, skip entry */
1809313570Smm				ret = ARCHIVE_WARN;
1810313570Smm				continue;
1811313570Smm			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1812313570Smm			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1813313570Smm				n = 1;
1814231200Smm				name = field[1];
1815313570Smm				isint(name.start, name.end, &id);
1816231200Smm			} else
1817313570Smm				n = 0;
1818231200Smm
1819313570Smm			if (!is_nfs4_perms(field[1 + n].start,
1820313570Smm			    field[1 + n].end, &permset)) {
1821313570Smm				/* Invalid NFSv4 perms, skip entry */
1822313570Smm				ret = ARCHIVE_WARN;
1823313570Smm				continue;
1824313570Smm			}
1825313570Smm			if (!is_nfs4_flags(field[2 + n].start,
1826313570Smm			    field[2 + n].end, &permset)) {
1827313570Smm				/* Invalid NFSv4 flags, skip entry */
1828313570Smm				ret = ARCHIVE_WARN;
1829313570Smm				continue;
1830313570Smm			}
1831313570Smm			s = field[3 + n].start;
1832313570Smm			len = field[3 + n].end - field[3 + n].start;
1833313570Smm			type = 0;
1834313570Smm			if (len == 4) {
1835313570Smm				if (memcmp(s, "deny", 4) == 0)
1836313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1837313570Smm			} else if (len == 5) {
1838313570Smm				if (memcmp(s, "allow", 5) == 0)
1839313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1840313570Smm				else if (memcmp(s, "audit", 5) == 0)
1841313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1842313570Smm				else if (memcmp(s, "alarm", 5) == 0)
1843313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1844313570Smm			}
1845313570Smm			if (type == 0) {
1846313570Smm				/* Invalid entry type, skip entry */
1847313570Smm				ret = ARCHIVE_WARN;
1848313570Smm				continue;
1849313570Smm			}
1850313570Smm			isint(field[4 + n].start, field[4 + n].end,
1851313570Smm			    &id);
1852313570Smm		}
1853313570Smm
1854231200Smm		/* Add entry to the internal list. */
1855231200Smm		r = archive_acl_add_entry_len_l(acl, type, permset,
1856231200Smm		    tag, id, name.start, name.end - name.start, sc);
1857231200Smm		if (r < ARCHIVE_WARN)
1858231200Smm			return (r);
1859231200Smm		if (r != ARCHIVE_OK)
1860231200Smm			ret = ARCHIVE_WARN;
1861313570Smm		types |= type;
1862231200Smm	}
1863313570Smm
1864313570Smm	/* Reset ACL */
1865313570Smm	archive_acl_reset(acl, types);
1866313570Smm
1867231200Smm	return (ret);
1868231200Smm}
1869231200Smm
1870231200Smm/*
1871231200Smm * Parse a string to a positive decimal integer.  Returns true if
1872231200Smm * the string is non-empty and consists only of decimal digits,
1873231200Smm * false otherwise.
1874231200Smm */
1875231200Smmstatic int
1876231200Smmisint(const char *start, const char *end, int *result)
1877231200Smm{
1878231200Smm	int n = 0;
1879231200Smm	if (start >= end)
1880231200Smm		return (0);
1881231200Smm	while (start < end) {
1882231200Smm		if (*start < '0' || *start > '9')
1883231200Smm			return (0);
1884231200Smm		if (n > (INT_MAX / 10) ||
1885231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1886231200Smm			n = INT_MAX;
1887231200Smm		} else {
1888231200Smm			n *= 10;
1889231200Smm			n += *start - '0';
1890231200Smm		}
1891231200Smm		start++;
1892231200Smm	}
1893231200Smm	*result = n;
1894231200Smm	return (1);
1895231200Smm}
1896231200Smm
1897231200Smm/*
1898231200Smm * Parse a string as a mode field.  Returns true if
1899231200Smm * the string is non-empty and consists only of mode characters,
1900231200Smm * false otherwise.
1901231200Smm */
1902231200Smmstatic int
1903231200Smmismode(const char *start, const char *end, int *permset)
1904231200Smm{
1905231200Smm	const char *p;
1906231200Smm
1907231200Smm	if (start >= end)
1908231200Smm		return (0);
1909231200Smm	p = start;
1910231200Smm	*permset = 0;
1911231200Smm	while (p < end) {
1912231200Smm		switch (*p++) {
1913231200Smm		case 'r': case 'R':
1914231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1915231200Smm			break;
1916231200Smm		case 'w': case 'W':
1917231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1918231200Smm			break;
1919231200Smm		case 'x': case 'X':
1920231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1921231200Smm			break;
1922231200Smm		case '-':
1923231200Smm			break;
1924231200Smm		default:
1925231200Smm			return (0);
1926231200Smm		}
1927231200Smm	}
1928231200Smm	return (1);
1929231200Smm}
1930231200Smm
1931231200Smm/*
1932313570Smm * Parse a string as a NFS4 ACL permission field.
1933313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1934313570Smm * permission characters, false otherwise
1935313570Smm */
1936313570Smmstatic int
1937313570Smmis_nfs4_perms(const char *start, const char *end, int *permset)
1938313570Smm{
1939313926Smm	const char *p = start;
1940313570Smm
1941313570Smm	while (p < end) {
1942313570Smm		switch (*p++) {
1943313570Smm		case 'r':
1944313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1945313570Smm			break;
1946313570Smm		case 'w':
1947313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1948313570Smm			break;
1949313570Smm		case 'x':
1950313570Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1951313570Smm			break;
1952313570Smm		case 'p':
1953313570Smm			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1954313570Smm			break;
1955313570Smm		case 'D':
1956313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1957313570Smm			break;
1958313570Smm		case 'd':
1959313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1960313570Smm			break;
1961313570Smm		case 'a':
1962313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1963313570Smm			break;
1964313570Smm		case 'A':
1965313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1966313570Smm			break;
1967313570Smm		case 'R':
1968313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1969313570Smm			break;
1970313570Smm		case 'W':
1971313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1972313570Smm			break;
1973313570Smm		case 'c':
1974313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1975313570Smm			break;
1976313570Smm		case 'C':
1977313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1978313570Smm			break;
1979313570Smm		case 'o':
1980313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1981313570Smm			break;
1982313570Smm		case 's':
1983313570Smm			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1984313570Smm			break;
1985313570Smm		case '-':
1986313570Smm			break;
1987313570Smm		default:
1988313570Smm			return(0);
1989313570Smm		}
1990313570Smm	}
1991313570Smm	return (1);
1992313570Smm}
1993313570Smm
1994313570Smm/*
1995313570Smm * Parse a string as a NFS4 ACL flags field.
1996313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1997313570Smm * flag characters, false otherwise
1998313570Smm */
1999313570Smmstatic int
2000313570Smmis_nfs4_flags(const char *start, const char *end, int *permset)
2001313570Smm{
2002313926Smm	const char *p = start;
2003313570Smm
2004313570Smm	while (p < end) {
2005313570Smm		switch(*p++) {
2006313570Smm		case 'f':
2007313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2008313570Smm			break;
2009313570Smm		case 'd':
2010313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2011313570Smm			break;
2012313570Smm		case 'i':
2013313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2014313570Smm			break;
2015313570Smm		case 'n':
2016313570Smm			*permset |=
2017313570Smm			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2018313570Smm			break;
2019313570Smm		case 'S':
2020313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2021313570Smm			break;
2022313570Smm		case 'F':
2023313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2024313570Smm			break;
2025313570Smm		case 'I':
2026313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2027313570Smm			break;
2028313570Smm		case '-':
2029313570Smm			break;
2030313570Smm		default:
2031313570Smm			return (0);
2032313570Smm		}
2033313570Smm	}
2034313570Smm	return (1);
2035313570Smm}
2036313570Smm
2037313570Smm/*
2038231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2039231200Smm * to point to just after the separator.  *start points to the first
2040231200Smm * character of the matched text and *end just after the last
2041231200Smm * character of the matched identifier.  In particular *end - *start
2042231200Smm * is the length of the field body, not including leading or trailing
2043231200Smm * whitespace.
2044231200Smm */
2045231200Smmstatic void
2046231200Smmnext_field(const char **p, const char **start,
2047231200Smm    const char **end, char *sep)
2048231200Smm{
2049231200Smm	/* Skip leading whitespace to find start of field. */
2050231200Smm	while (**p == ' ' || **p == '\t' || **p == '\n') {
2051231200Smm		(*p)++;
2052231200Smm	}
2053231200Smm	*start = *p;
2054231200Smm
2055231200Smm	/* Scan for the separator. */
2056231200Smm	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
2057231200Smm		(*p)++;
2058231200Smm	}
2059231200Smm	*sep = **p;
2060231200Smm
2061339005Smm	/* If the field is only whitespace, bail out now. */
2062339005Smm	if (**p == '\0') {
2063339005Smm		*end = *p;
2064339005Smm		return;
2065339005Smm	}
2066339005Smm
2067231200Smm	/* Trim trailing whitespace to locate end of field. */
2068231200Smm	*end = *p - 1;
2069231200Smm	while (**end == ' ' || **end == '\t' || **end == '\n') {
2070231200Smm		(*end)--;
2071231200Smm	}
2072231200Smm	(*end)++;
2073231200Smm
2074231200Smm	/* Adjust scanner location. */
2075231200Smm	if (**p != '\0')
2076231200Smm		(*p)++;
2077231200Smm}
2078