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	}
141344673Smm	free(acl->acl_text_w);
142344673Smm	acl->acl_text_w = NULL;
143344673Smm	free(acl->acl_text);
144344673Smm	acl->acl_text = NULL;
145231200Smm	acl->acl_p = NULL;
146311041Smm	acl->acl_types = 0;
147231200Smm	acl->acl_state = 0; /* Not counting. */
148231200Smm}
149231200Smm
150231200Smmvoid
151231200Smmarchive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
152231200Smm{
153231200Smm	struct archive_acl_entry *ap, *ap2;
154231200Smm
155231200Smm	archive_acl_clear(dest);
156231200Smm
157231200Smm	dest->mode = src->mode;
158231200Smm	ap = src->acl_head;
159231200Smm	while (ap != NULL) {
160231200Smm		ap2 = acl_new_entry(dest,
161231200Smm		    ap->type, ap->permset, ap->tag, ap->id);
162231200Smm		if (ap2 != NULL)
163231200Smm			archive_mstring_copy(&ap2->name, &ap->name);
164231200Smm		ap = ap->next;
165231200Smm	}
166231200Smm}
167231200Smm
168231200Smmint
169231200Smmarchive_acl_add_entry(struct archive_acl *acl,
170231200Smm    int type, int permset, int tag, int id, const char *name)
171231200Smm{
172231200Smm	struct archive_acl_entry *ap;
173231200Smm
174231200Smm	if (acl_special(acl, type, permset, tag) == 0)
175231200Smm		return ARCHIVE_OK;
176231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
177231200Smm	if (ap == NULL) {
178231200Smm		/* XXX Error XXX */
179231200Smm		return ARCHIVE_FAILED;
180231200Smm	}
181231200Smm	if (name != NULL  &&  *name != '\0')
182231200Smm		archive_mstring_copy_mbs(&ap->name, name);
183231200Smm	else
184231200Smm		archive_mstring_clean(&ap->name);
185231200Smm	return ARCHIVE_OK;
186231200Smm}
187231200Smm
188231200Smmint
189231200Smmarchive_acl_add_entry_w_len(struct archive_acl *acl,
190231200Smm    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
191231200Smm{
192231200Smm	struct archive_acl_entry *ap;
193231200Smm
194231200Smm	if (acl_special(acl, type, permset, tag) == 0)
195231200Smm		return ARCHIVE_OK;
196231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
197231200Smm	if (ap == NULL) {
198231200Smm		/* XXX Error XXX */
199231200Smm		return ARCHIVE_FAILED;
200231200Smm	}
201231200Smm	if (name != NULL  &&  *name != L'\0' && len > 0)
202231200Smm		archive_mstring_copy_wcs_len(&ap->name, name, len);
203231200Smm	else
204231200Smm		archive_mstring_clean(&ap->name);
205231200Smm	return ARCHIVE_OK;
206231200Smm}
207231200Smm
208232153Smmstatic int
209231200Smmarchive_acl_add_entry_len_l(struct archive_acl *acl,
210231200Smm    int type, int permset, int tag, int id, const char *name, size_t len,
211231200Smm    struct archive_string_conv *sc)
212231200Smm{
213231200Smm	struct archive_acl_entry *ap;
214231200Smm	int r;
215231200Smm
216231200Smm	if (acl_special(acl, type, permset, tag) == 0)
217231200Smm		return ARCHIVE_OK;
218231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
219231200Smm	if (ap == NULL) {
220231200Smm		/* XXX Error XXX */
221231200Smm		return ARCHIVE_FAILED;
222231200Smm	}
223231200Smm	if (name != NULL  &&  *name != '\0' && len > 0) {
224231200Smm		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
225231200Smm	} else {
226231200Smm		r = 0;
227231200Smm		archive_mstring_clean(&ap->name);
228231200Smm	}
229231200Smm	if (r == 0)
230231200Smm		return (ARCHIVE_OK);
231231200Smm	else if (errno == ENOMEM)
232231200Smm		return (ARCHIVE_FATAL);
233231200Smm	else
234231200Smm		return (ARCHIVE_WARN);
235231200Smm}
236231200Smm
237231200Smm/*
238231200Smm * If this ACL entry is part of the standard POSIX permissions set,
239231200Smm * store the permissions in the stat structure and return zero.
240231200Smm */
241231200Smmstatic int
242231200Smmacl_special(struct archive_acl *acl, int type, int permset, int tag)
243231200Smm{
244231200Smm	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
245231200Smm	    && ((permset & ~007) == 0)) {
246231200Smm		switch (tag) {
247231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
248231200Smm			acl->mode &= ~0700;
249231200Smm			acl->mode |= (permset & 7) << 6;
250231200Smm			return (0);
251231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
252231200Smm			acl->mode &= ~0070;
253231200Smm			acl->mode |= (permset & 7) << 3;
254231200Smm			return (0);
255231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
256231200Smm			acl->mode &= ~0007;
257231200Smm			acl->mode |= permset & 7;
258231200Smm			return (0);
259231200Smm		}
260231200Smm	}
261231200Smm	return (1);
262231200Smm}
263231200Smm
264231200Smm/*
265231200Smm * Allocate and populate a new ACL entry with everything but the
266231200Smm * name.
267231200Smm */
268231200Smmstatic struct archive_acl_entry *
269231200Smmacl_new_entry(struct archive_acl *acl,
270231200Smm    int type, int permset, int tag, int id)
271231200Smm{
272231200Smm	struct archive_acl_entry *ap, *aq;
273231200Smm
274231200Smm	/* Type argument must be a valid NFS4 or POSIX.1e type.
275231200Smm	 * The type must agree with anything already set and
276231200Smm	 * the permset must be compatible. */
277231200Smm	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
278231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
279231200Smm			return (NULL);
280231200Smm		}
281231200Smm		if (permset &
282231200Smm		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
283231200Smm			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
284231200Smm			return (NULL);
285231200Smm		}
286231200Smm	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
287231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
288231200Smm			return (NULL);
289231200Smm		}
290231200Smm		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
291231200Smm			return (NULL);
292231200Smm		}
293231200Smm	} else {
294231200Smm		return (NULL);
295231200Smm	}
296231200Smm
297231200Smm	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
298231200Smm	switch (tag) {
299231200Smm	case ARCHIVE_ENTRY_ACL_USER:
300231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
301231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
302231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
303231200Smm		/* Tags valid in both NFS4 and POSIX.1e */
304231200Smm		break;
305231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
306231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
307231200Smm		/* Tags valid only in POSIX.1e. */
308231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
309231200Smm			return (NULL);
310231200Smm		}
311231200Smm		break;
312231200Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
313231200Smm		/* Tags valid only in NFS4. */
314231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
315231200Smm			return (NULL);
316231200Smm		}
317231200Smm		break;
318231200Smm	default:
319231200Smm		/* No other values are valid. */
320231200Smm		return (NULL);
321231200Smm	}
322231200Smm
323344673Smm	free(acl->acl_text_w);
324344673Smm	acl->acl_text_w = NULL;
325344673Smm	free(acl->acl_text);
326344673Smm	acl->acl_text = NULL;
327231200Smm
328311041Smm	/*
329311041Smm	 * If there's a matching entry already in the list, overwrite it.
330311041Smm	 * NFSv4 entries may be repeated and are not overwritten.
331311041Smm	 *
332311041Smm	 * TODO: compare names of no id is provided (needs more rework)
333311041Smm	 */
334231200Smm	ap = acl->acl_head;
335231200Smm	aq = NULL;
336231200Smm	while (ap != NULL) {
337311041Smm		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
338311041Smm		    ap->type == type && ap->tag == tag && ap->id == id) {
339311041Smm			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
340311041Smm			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {
341311041Smm				ap->permset = permset;
342311041Smm				return (ap);
343311041Smm			}
344231200Smm		}
345231200Smm		aq = ap;
346231200Smm		ap = ap->next;
347231200Smm	}
348231200Smm
349231200Smm	/* Add a new entry to the end of the list. */
350311041Smm	ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
351231200Smm	if (ap == NULL)
352231200Smm		return (NULL);
353231200Smm	if (aq == NULL)
354231200Smm		acl->acl_head = ap;
355231200Smm	else
356231200Smm		aq->next = ap;
357231200Smm	ap->type = type;
358231200Smm	ap->tag = tag;
359231200Smm	ap->id = id;
360231200Smm	ap->permset = permset;
361231200Smm	acl->acl_types |= type;
362231200Smm	return (ap);
363231200Smm}
364231200Smm
365231200Smm/*
366231200Smm * Return a count of entries matching "want_type".
367231200Smm */
368231200Smmint
369231200Smmarchive_acl_count(struct archive_acl *acl, int want_type)
370231200Smm{
371231200Smm	int count;
372231200Smm	struct archive_acl_entry *ap;
373231200Smm
374231200Smm	count = 0;
375231200Smm	ap = acl->acl_head;
376231200Smm	while (ap != NULL) {
377231200Smm		if ((ap->type & want_type) != 0)
378231200Smm			count++;
379231200Smm		ap = ap->next;
380231200Smm	}
381231200Smm
382231200Smm	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
383231200Smm		count += 3;
384231200Smm	return (count);
385231200Smm}
386231200Smm
387231200Smm/*
388313570Smm * Return a bitmask of stored ACL types in an ACL list
389313570Smm */
390313570Smmint
391313570Smmarchive_acl_types(struct archive_acl *acl)
392313570Smm{
393313570Smm	return (acl->acl_types);
394313570Smm}
395313570Smm
396313570Smm/*
397231200Smm * Prepare for reading entries from the ACL data.  Returns a count
398231200Smm * of entries matching "want_type", or zero if there are no
399231200Smm * non-extended ACL entries of that type.
400231200Smm */
401231200Smmint
402231200Smmarchive_acl_reset(struct archive_acl *acl, int want_type)
403231200Smm{
404231200Smm	int count, cutoff;
405231200Smm
406231200Smm	count = archive_acl_count(acl, want_type);
407231200Smm
408231200Smm	/*
409231200Smm	 * If the only entries are the three standard ones,
410231200Smm	 * then don't return any ACL data.  (In this case,
411231200Smm	 * client can just use chmod(2) to set permissions.)
412231200Smm	 */
413231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
414231200Smm		cutoff = 3;
415231200Smm	else
416231200Smm		cutoff = 0;
417231200Smm
418231200Smm	if (count > cutoff)
419231200Smm		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
420231200Smm	else
421231200Smm		acl->acl_state = 0;
422231200Smm	acl->acl_p = acl->acl_head;
423231200Smm	return (count);
424231200Smm}
425231200Smm
426231200Smm
427231200Smm/*
428231200Smm * Return the next ACL entry in the list.  Fake entries for the
429231200Smm * standard permissions and include them in the returned list.
430231200Smm */
431231200Smmint
432313570Smmarchive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
433313570Smm    int *type, int *permset, int *tag, int *id, const char **name)
434231200Smm{
435231200Smm	*name = NULL;
436231200Smm	*id = -1;
437231200Smm
438231200Smm	/*
439231200Smm	 * The acl_state is either zero (no entries available), -1
440231200Smm	 * (reading from list), or an entry type (retrieve that type
441231200Smm	 * from ae_stat.aest_mode).
442231200Smm	 */
443231200Smm	if (acl->acl_state == 0)
444231200Smm		return (ARCHIVE_WARN);
445231200Smm
446231200Smm	/* The first three access entries are special. */
447231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
448231200Smm		switch (acl->acl_state) {
449231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
450231200Smm			*permset = (acl->mode >> 6) & 7;
451231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
452231200Smm			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
453231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
454231200Smm			return (ARCHIVE_OK);
455231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
456231200Smm			*permset = (acl->mode >> 3) & 7;
457231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
458231200Smm			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
459231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
460231200Smm			return (ARCHIVE_OK);
461231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
462231200Smm			*permset = acl->mode & 7;
463231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
464231200Smm			*tag = ARCHIVE_ENTRY_ACL_OTHER;
465231200Smm			acl->acl_state = -1;
466231200Smm			acl->acl_p = acl->acl_head;
467231200Smm			return (ARCHIVE_OK);
468231200Smm		default:
469231200Smm			break;
470231200Smm		}
471231200Smm	}
472231200Smm
473231200Smm	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
474231200Smm		acl->acl_p = acl->acl_p->next;
475231200Smm	if (acl->acl_p == NULL) {
476231200Smm		acl->acl_state = 0;
477231200Smm		*type = 0;
478231200Smm		*permset = 0;
479231200Smm		*tag = 0;
480231200Smm		*id = -1;
481231200Smm		*name = NULL;
482231200Smm		return (ARCHIVE_EOF); /* End of ACL entries. */
483231200Smm	}
484231200Smm	*type = acl->acl_p->type;
485231200Smm	*permset = acl->acl_p->permset;
486231200Smm	*tag = acl->acl_p->tag;
487231200Smm	*id = acl->acl_p->id;
488238856Smm	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
489238856Smm		if (errno == ENOMEM)
490238856Smm			return (ARCHIVE_FATAL);
491231200Smm		*name = NULL;
492238856Smm	}
493231200Smm	acl->acl_p = acl->acl_p->next;
494231200Smm	return (ARCHIVE_OK);
495231200Smm}
496231200Smm
497231200Smm/*
498313570Smm * Determine what type of ACL do we want
499231200Smm */
500313570Smmstatic int
501313570Smmarchive_acl_text_want_type(struct archive_acl *acl, int flags)
502231200Smm{
503313570Smm	int want_type;
504231200Smm
505313570Smm	/* Check if ACL is NFSv4 */
506313570Smm	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
507313570Smm		/* NFSv4 should never mix with POSIX.1e */
508313570Smm		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
509313570Smm			return (0);
510313570Smm		else
511313570Smm			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
512231200Smm	}
513231200Smm
514313570Smm	/* Now deal with POSIX.1e ACLs */
515313570Smm
516313570Smm	want_type = 0;
517313570Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
518313570Smm		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
519313570Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
520313570Smm		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
521313570Smm
522313570Smm	/* By default we want both access and default ACLs */
523313570Smm	if (want_type == 0)
524313570Smm		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
525313570Smm
526313570Smm	return (want_type);
527313570Smm}
528313570Smm
529313570Smm/*
530313570Smm * Calculate ACL text string length
531313570Smm */
532313570Smmstatic ssize_t
533313570Smmarchive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
534313570Smm    int wide, struct archive *a, struct archive_string_conv *sc) {
535313570Smm	struct archive_acl_entry *ap;
536313570Smm	const char *name;
537313570Smm	const wchar_t *wname;
538313570Smm	int count, idlen, tmp, r;
539313570Smm	ssize_t length;
540313570Smm	size_t len;
541313570Smm
542231200Smm	count = 0;
543231200Smm	length = 0;
544313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
545313570Smm		if ((ap->type & want_type) == 0)
546313570Smm			continue;
547313570Smm		/*
548313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
549313570Smm		 * ap->mode so they should not be in the list
550313570Smm		 */
551313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
552313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
553313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
554313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
555313570Smm			continue;
556313570Smm		count++;
557313570Smm		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
558313570Smm		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
559313570Smm			length += 8; /* "default:" */
560313570Smm		switch (ap->tag) {
561313570Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
562313570Smm			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
563313570Smm				length += 6; /* "owner@" */
564313570Smm				break;
565313570Smm			}
566313570Smm			/* FALLTHROUGH */
567313570Smm		case ARCHIVE_ENTRY_ACL_USER:
568313570Smm		case ARCHIVE_ENTRY_ACL_MASK:
569313570Smm			length += 4; /* "user", "mask" */
570313570Smm			break;
571313570Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
572313570Smm			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
573313570Smm				length += 6; /* "group@" */
574313570Smm				break;
575313570Smm			}
576313570Smm			/* FALLTHROUGH */
577313570Smm		case ARCHIVE_ENTRY_ACL_GROUP:
578313570Smm		case ARCHIVE_ENTRY_ACL_OTHER:
579313570Smm			length += 5; /* "group", "other" */
580313570Smm			break;
581313570Smm		case ARCHIVE_ENTRY_ACL_EVERYONE:
582313570Smm			length += 9; /* "everyone@" */
583313570Smm			break;
584313570Smm		}
585313570Smm		length += 1; /* colon after tag */
586313570Smm		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
587313570Smm		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
588313570Smm			if (wide) {
589313570Smm				r = archive_mstring_get_wcs(a, &ap->name,
590313570Smm				    &wname);
591313570Smm				if (r == 0 && wname != NULL)
592313570Smm					length += wcslen(wname);
593313570Smm				else if (r < 0 && errno == ENOMEM)
594313570Smm					return (0);
595313570Smm				else
596313570Smm					length += sizeof(uid_t) * 3 + 1;
597313570Smm			} else {
598368707Smm				r = archive_mstring_get_mbs_l(a, &ap->name, &name,
599313570Smm				    &len, sc);
600313570Smm				if (r != 0)
601313570Smm					return (0);
602313570Smm				if (len > 0 && name != NULL)
603313570Smm					length += len;
604313570Smm				else
605313570Smm					length += sizeof(uid_t) * 3 + 1;
606313570Smm			}
607313570Smm			length += 1; /* colon after user or group name */
608313570Smm		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
609313570Smm			length += 1; /* 2nd colon empty user,group or other */
610313570Smm
611313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
612313570Smm		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
613313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
614313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
615313570Smm			/* Solaris has no colon after other: and mask: */
616313570Smm			length = length - 1;
617313570Smm		}
618313570Smm
619313570Smm		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
620313570Smm			/* rwxpdDaARWcCos:fdinSFI:deny */
621313570Smm			length += 27;
622313570Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
623313570Smm				length += 1; /* allow, alarm, audit */
624313570Smm		} else
625231200Smm			length += 3; /* rwx */
626313570Smm
627313570Smm		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
628313570Smm		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
629313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
630231200Smm			length += 1; /* colon */
631313570Smm			/* ID digit count */
632313570Smm			idlen = 1;
633313570Smm			tmp = ap->id;
634313570Smm			while (tmp > 9) {
635313570Smm				tmp = tmp / 10;
636313570Smm				idlen++;
637313570Smm			}
638313570Smm			length += idlen;
639231200Smm		}
640313570Smm		length ++; /* entry separator */
641231200Smm	}
642231200Smm
643313570Smm	/* Add filemode-mapping access entries to the length */
644313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
645313570Smm		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
646313570Smm			/* "user::rwx\ngroup::rwx\nother:rwx\n" */
647313570Smm			length += 31;
648313570Smm		} else {
649313570Smm			/* "user::rwx\ngroup::rwx\nother::rwx\n" */
650313570Smm			length += 32;
651313570Smm		}
652313570Smm	} else if (count == 0)
653313570Smm		return (0);
654231200Smm
655313570Smm	/* The terminating character is included in count */
656313570Smm	return (length);
657313570Smm}
658313570Smm
659313570Smm/*
660313570Smm * Generate a wide text version of the ACL. The flags parameter controls
661313570Smm * the type and style of the generated ACL.
662313570Smm */
663313570Smmwchar_t *
664313570Smmarchive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
665313570Smm    struct archive *a)
666313570Smm{
667313570Smm	int count;
668313570Smm	ssize_t length;
669313570Smm	size_t len;
670313570Smm	const wchar_t *wname;
671313570Smm	const wchar_t *prefix;
672313570Smm	wchar_t separator;
673313570Smm	struct archive_acl_entry *ap;
674313570Smm	int id, r, want_type;
675313570Smm	wchar_t *wp, *ws;
676313570Smm
677313570Smm	want_type = archive_acl_text_want_type(acl, flags);
678313570Smm
679313570Smm	/* Both NFSv4 and POSIX.1 types found */
680313570Smm	if (want_type == 0)
681231200Smm		return (NULL);
682231200Smm
683313570Smm	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
684313570Smm		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
685313570Smm
686313570Smm	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
687313570Smm
688313570Smm	if (length == 0)
689313570Smm		return (NULL);
690313570Smm
691313570Smm	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
692313570Smm		separator = L',';
693313570Smm	else
694313570Smm		separator = L'\n';
695313570Smm
696231200Smm	/* Now, allocate the string and actually populate it. */
697313570Smm	wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
698313570Smm	if (wp == NULL) {
699313570Smm		if (errno == ENOMEM)
700313570Smm			__archive_errx(1, "No memory");
701238856Smm		return (NULL);
702313570Smm	}
703231200Smm	count = 0;
704313570Smm
705313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
706313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
707313570Smm		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
708231200Smm		    acl->mode & 0700, -1);
709313570Smm		*wp++ = separator;
710313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
711313570Smm		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
712231200Smm		    acl->mode & 0070, -1);
713313570Smm		*wp++ = separator;
714313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
715313570Smm		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
716231200Smm		    acl->mode & 0007, -1);
717231200Smm		count += 3;
718231200Smm	}
719231200Smm
720313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
721313570Smm		if ((ap->type & want_type) == 0)
722313570Smm			continue;
723313570Smm		/*
724313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
725313570Smm		 * ap->mode so they should not be in the list
726313570Smm		 */
727313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
728313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
729313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
730313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
731313570Smm			continue;
732313570Smm		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
733313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
734231200Smm			prefix = L"default:";
735231200Smm		else
736231200Smm			prefix = NULL;
737313570Smm		r = archive_mstring_get_wcs(a, &ap->name, &wname);
738313570Smm		if (r == 0) {
739313570Smm			if (count > 0)
740313570Smm				*wp++ = separator;
741313570Smm			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
742313570Smm				id = ap->id;
743313570Smm			else
744313570Smm				id = -1;
745313570Smm			append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
746313570Smm			    wname, ap->permset, id);
747313570Smm			count++;
748342360Smm		} else if (r < 0 && errno == ENOMEM) {
749342360Smm			free(ws);
750313570Smm			return (NULL);
751342360Smm		}
752231200Smm	}
753231200Smm
754313570Smm	/* Add terminating character */
755313570Smm	*wp++ = L'\0';
756313570Smm
757313570Smm	len = wcslen(ws);
758313570Smm
759313570Smm	if ((ssize_t)len > (length - 1))
760313570Smm		__archive_errx(1, "Buffer overrun");
761313570Smm
762313570Smm	if (text_len != NULL)
763313570Smm		*text_len = len;
764313570Smm
765313570Smm	return (ws);
766231200Smm}
767231200Smm
768231200Smmstatic void
769231200Smmappend_id_w(wchar_t **wp, int id)
770231200Smm{
771231200Smm	if (id < 0)
772231200Smm		id = 0;
773231200Smm	if (id > 9)
774231200Smm		append_id_w(wp, id / 10);
775231200Smm	*(*wp)++ = L"0123456789"[id % 10];
776231200Smm}
777231200Smm
778231200Smmstatic void
779313570Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
780313570Smm    int tag, int flags, const wchar_t *wname, int perm, int id)
781231200Smm{
782313926Smm	int i;
783313926Smm
784231200Smm	if (prefix != NULL) {
785231200Smm		wcscpy(*wp, prefix);
786231200Smm		*wp += wcslen(*wp);
787231200Smm	}
788231200Smm	switch (tag) {
789231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
790231200Smm		wname = NULL;
791231200Smm		id = -1;
792313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
793313570Smm			wcscpy(*wp, L"owner@");
794313570Smm			break;
795313570Smm		}
796231200Smm		/* FALLTHROUGH */
797231200Smm	case ARCHIVE_ENTRY_ACL_USER:
798231200Smm		wcscpy(*wp, L"user");
799231200Smm		break;
800231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
801231200Smm		wname = NULL;
802231200Smm		id = -1;
803313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
804313570Smm			wcscpy(*wp, L"group@");
805313570Smm			break;
806313570Smm		}
807231200Smm		/* FALLTHROUGH */
808231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
809231200Smm		wcscpy(*wp, L"group");
810231200Smm		break;
811231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
812231200Smm		wcscpy(*wp, L"mask");
813231200Smm		wname = NULL;
814231200Smm		id = -1;
815231200Smm		break;
816231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
817231200Smm		wcscpy(*wp, L"other");
818231200Smm		wname = NULL;
819231200Smm		id = -1;
820231200Smm		break;
821313570Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
822313570Smm		wcscpy(*wp, L"everyone@");
823313570Smm		wname = NULL;
824313570Smm		id = -1;
825313570Smm		break;
826231200Smm	}
827231200Smm	*wp += wcslen(*wp);
828231200Smm	*(*wp)++ = L':';
829313570Smm	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
830313570Smm	    tag == ARCHIVE_ENTRY_ACL_USER ||
831313570Smm	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
832313570Smm		if (wname != NULL) {
833313570Smm			wcscpy(*wp, wname);
834313570Smm			*wp += wcslen(*wp);
835313570Smm		} else if (tag == ARCHIVE_ENTRY_ACL_USER
836313570Smm		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
837313570Smm			append_id_w(wp, id);
838313570Smm			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
839313570Smm				id = -1;
840313570Smm		}
841313570Smm		/* Solaris style has no second colon after other and mask */
842313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
843313570Smm		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
844313570Smm		    && tag != ARCHIVE_ENTRY_ACL_MASK))
845313570Smm			*(*wp)++ = L':';
846313570Smm	}
847313570Smm	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
848313570Smm		/* POSIX.1e ACL perms */
849313570Smm		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
850313570Smm		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
851313570Smm		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
852313570Smm	} else {
853313926Smm		/* NFSv4 ACL perms */
854313926Smm		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
855313926Smm			if (perm & nfsv4_acl_perm_map[i].perm)
856313926Smm				*(*wp)++ = nfsv4_acl_perm_map[i].wc;
857313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
858313926Smm				*(*wp)++ = L'-';
859313926Smm		}
860313570Smm		*(*wp)++ = L':';
861313926Smm		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
862313926Smm			if (perm & nfsv4_acl_flag_map[i].perm)
863313926Smm				*(*wp)++ = nfsv4_acl_flag_map[i].wc;
864313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
865313926Smm				*(*wp)++ = L'-';
866313926Smm		}
867313570Smm		*(*wp)++ = L':';
868313570Smm		switch (type) {
869313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
870313570Smm			wcscpy(*wp, L"allow");
871313570Smm			break;
872313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
873313570Smm			wcscpy(*wp, L"deny");
874313570Smm			break;
875313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
876313570Smm			wcscpy(*wp, L"audit");
877313570Smm			break;
878313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
879313570Smm			wcscpy(*wp, L"alarm");
880313570Smm			break;
881313570Smm		default:
882313570Smm			break;
883313570Smm		}
884231200Smm		*wp += wcslen(*wp);
885231200Smm	}
886231200Smm	if (id != -1) {
887231200Smm		*(*wp)++ = L':';
888231200Smm		append_id_w(wp, id);
889231200Smm	}
890231200Smm}
891231200Smm
892313570Smm/*
893313570Smm * Generate a text version of the ACL. The flags parameter controls
894313570Smm * the type and style of the generated ACL.
895313570Smm */
896313570Smmchar *
897313570Smmarchive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
898231200Smm    struct archive_string_conv *sc)
899231200Smm{
900231200Smm	int count;
901313570Smm	ssize_t length;
902313570Smm	size_t len;
903231200Smm	const char *name;
904231200Smm	const char *prefix;
905231200Smm	char separator;
906231200Smm	struct archive_acl_entry *ap;
907313570Smm	int id, r, want_type;
908313570Smm	char *p, *s;
909231200Smm
910313570Smm	want_type = archive_acl_text_want_type(acl, flags);
911231200Smm
912313570Smm	/* Both NFSv4 and POSIX.1 types found */
913313570Smm	if (want_type == 0)
914313570Smm		return (NULL);
915231200Smm
916313570Smm	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
917313570Smm		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
918231200Smm
919313570Smm	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
920231200Smm
921313570Smm	if (length == 0)
922313570Smm		return (NULL);
923313570Smm
924313570Smm	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
925313570Smm		separator = ',';
926313570Smm	else
927313570Smm		separator = '\n';
928313570Smm
929231200Smm	/* Now, allocate the string and actually populate it. */
930313570Smm	p = s = (char *)malloc(length * sizeof(char));
931313570Smm	if (p == NULL) {
932313570Smm		if (errno == ENOMEM)
933313570Smm			__archive_errx(1, "No memory");
934313570Smm		return (NULL);
935313570Smm	}
936231200Smm	count = 0;
937313570Smm
938313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
939313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
940313570Smm		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
941231200Smm		    acl->mode & 0700, -1);
942313570Smm		*p++ = separator;
943313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
944313570Smm		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
945231200Smm		    acl->mode & 0070, -1);
946313570Smm		*p++ = separator;
947313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
948313570Smm		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
949231200Smm		    acl->mode & 0007, -1);
950231200Smm		count += 3;
951231200Smm	}
952231200Smm
953313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
954313570Smm		if ((ap->type & want_type) == 0)
955313570Smm			continue;
956313570Smm		/*
957313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
958313570Smm		 * ap->mode so they should not be in the list
959313570Smm		 */
960313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
961313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
962313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
963313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
964313570Smm			continue;
965313570Smm		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
966313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
967231200Smm			prefix = "default:";
968231200Smm		else
969231200Smm			prefix = NULL;
970313570Smm		r = archive_mstring_get_mbs_l(
971368707Smm		    NULL, &ap->name, &name, &len, sc);
972342360Smm		if (r != 0) {
973342360Smm			free(s);
974313570Smm			return (NULL);
975342360Smm		}
976313570Smm		if (count > 0)
977313570Smm			*p++ = separator;
978313570Smm		if (name == NULL ||
979313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
980313570Smm			id = ap->id;
981313570Smm		} else {
982313570Smm			id = -1;
983231200Smm		}
984313570Smm		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
985313570Smm		    ap->permset, id);
986313570Smm		count++;
987231200Smm	}
988231200Smm
989313570Smm	/* Add terminating character */
990313570Smm	*p++ = '\0';
991313570Smm
992313570Smm	len = strlen(s);
993313570Smm
994313570Smm	if ((ssize_t)len > (length - 1))
995313570Smm		__archive_errx(1, "Buffer overrun");
996313570Smm
997313570Smm	if (text_len != NULL)
998313570Smm		*text_len = len;
999313570Smm
1000313570Smm	return (s);
1001231200Smm}
1002231200Smm
1003231200Smmstatic void
1004231200Smmappend_id(char **p, int id)
1005231200Smm{
1006231200Smm	if (id < 0)
1007231200Smm		id = 0;
1008231200Smm	if (id > 9)
1009231200Smm		append_id(p, id / 10);
1010231200Smm	*(*p)++ = "0123456789"[id % 10];
1011231200Smm}
1012231200Smm
1013231200Smmstatic void
1014313570Smmappend_entry(char **p, const char *prefix, int type,
1015313570Smm    int tag, int flags, const char *name, int perm, int id)
1016231200Smm{
1017313926Smm	int i;
1018313926Smm
1019231200Smm	if (prefix != NULL) {
1020231200Smm		strcpy(*p, prefix);
1021231200Smm		*p += strlen(*p);
1022231200Smm	}
1023231200Smm	switch (tag) {
1024231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
1025231200Smm		name = NULL;
1026231200Smm		id = -1;
1027313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1028313570Smm			strcpy(*p, "owner@");
1029313570Smm			break;
1030313570Smm		}
1031231200Smm		/* FALLTHROUGH */
1032231200Smm	case ARCHIVE_ENTRY_ACL_USER:
1033231200Smm		strcpy(*p, "user");
1034231200Smm		break;
1035231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1036231200Smm		name = NULL;
1037231200Smm		id = -1;
1038313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1039313570Smm			strcpy(*p, "group@");
1040313570Smm			break;
1041313570Smm		}
1042231200Smm		/* FALLTHROUGH */
1043231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
1044231200Smm		strcpy(*p, "group");
1045231200Smm		break;
1046231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
1047231200Smm		strcpy(*p, "mask");
1048231200Smm		name = NULL;
1049231200Smm		id = -1;
1050231200Smm		break;
1051231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
1052231200Smm		strcpy(*p, "other");
1053231200Smm		name = NULL;
1054231200Smm		id = -1;
1055231200Smm		break;
1056313570Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
1057313570Smm		strcpy(*p, "everyone@");
1058313570Smm		name = NULL;
1059313570Smm		id = -1;
1060313570Smm		break;
1061231200Smm	}
1062231200Smm	*p += strlen(*p);
1063231200Smm	*(*p)++ = ':';
1064313570Smm	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1065313570Smm	    tag == ARCHIVE_ENTRY_ACL_USER ||
1066313570Smm	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1067313570Smm		if (name != NULL) {
1068313570Smm			strcpy(*p, name);
1069313570Smm			*p += strlen(*p);
1070313570Smm		} else if (tag == ARCHIVE_ENTRY_ACL_USER
1071313570Smm		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1072313570Smm			append_id(p, id);
1073313570Smm			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1074313570Smm				id = -1;
1075313570Smm		}
1076313570Smm		/* Solaris style has no second colon after other and mask */
1077313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1078313570Smm		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
1079313570Smm		    && tag != ARCHIVE_ENTRY_ACL_MASK))
1080313570Smm			*(*p)++ = ':';
1081313570Smm	}
1082313570Smm	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1083313570Smm		/* POSIX.1e ACL perms */
1084313570Smm		*(*p)++ = (perm & 0444) ? 'r' : '-';
1085313570Smm		*(*p)++ = (perm & 0222) ? 'w' : '-';
1086313570Smm		*(*p)++ = (perm & 0111) ? 'x' : '-';
1087313570Smm	} else {
1088313926Smm		/* NFSv4 ACL perms */
1089313926Smm		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1090313926Smm			if (perm & nfsv4_acl_perm_map[i].perm)
1091313926Smm				*(*p)++ = nfsv4_acl_perm_map[i].c;
1092313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1093313926Smm				*(*p)++ = '-';
1094313926Smm		}
1095313570Smm		*(*p)++ = ':';
1096313926Smm		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1097313926Smm			if (perm & nfsv4_acl_flag_map[i].perm)
1098313926Smm				*(*p)++ = nfsv4_acl_flag_map[i].c;
1099313926Smm			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1100313926Smm				*(*p)++ = '-';
1101313926Smm		}
1102313570Smm		*(*p)++ = ':';
1103313570Smm		switch (type) {
1104313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1105313570Smm			strcpy(*p, "allow");
1106313570Smm			break;
1107313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1108313570Smm			strcpy(*p, "deny");
1109313570Smm			break;
1110313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1111313570Smm			strcpy(*p, "audit");
1112313570Smm			break;
1113313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1114313570Smm			strcpy(*p, "alarm");
1115313570Smm			break;
1116313570Smm		}
1117231200Smm		*p += strlen(*p);
1118231200Smm	}
1119231200Smm	if (id != -1) {
1120231200Smm		*(*p)++ = ':';
1121231200Smm		append_id(p, id);
1122231200Smm	}
1123231200Smm}
1124231200Smm
1125231200Smm/*
1126313570Smm * Parse a wide ACL text string.
1127313570Smm *
1128313570Smm * The want_type argument may be one of the following:
1129313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1130313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1131313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1132313570Smm *
1133313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as
1134313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1135231200Smm */
1136231200Smmint
1137313570Smmarchive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1138313570Smm    int want_type)
1139231200Smm{
1140231200Smm	struct {
1141231200Smm		const wchar_t *start;
1142231200Smm		const wchar_t *end;
1143313570Smm	} field[6], name;
1144231200Smm
1145313570Smm	const wchar_t *s, *st;
1146313570Smm
1147313570Smm	int numfields, fields, n, r, sol, ret;
1148313570Smm	int type, types, tag, permset, id;
1149313570Smm	size_t len;
1150231200Smm	wchar_t sep;
1151231200Smm
1152313570Smm	ret = ARCHIVE_OK;
1153313570Smm	types = 0;
1154313570Smm
1155313570Smm	switch (want_type) {
1156313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1157313570Smm		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1158328827Smm		__LA_FALLTHROUGH;
1159313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1160313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1161313570Smm		numfields = 5;
1162313570Smm		break;
1163313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1164313570Smm		numfields = 6;
1165313570Smm		break;
1166313570Smm	default:
1167313570Smm		return (ARCHIVE_FATAL);
1168313570Smm	}
1169313570Smm
1170313570Smm	while (text != NULL && *text != L'\0') {
1171231200Smm		/*
1172231200Smm		 * Parse the fields out of the next entry,
1173231200Smm		 * advance 'text' to start of next entry.
1174231200Smm		 */
1175231200Smm		fields = 0;
1176231200Smm		do {
1177231200Smm			const wchar_t *start, *end;
1178231200Smm			next_field_w(&text, &start, &end, &sep);
1179313570Smm			if (fields < numfields) {
1180231200Smm				field[fields].start = start;
1181231200Smm				field[fields].end = end;
1182231200Smm			}
1183231200Smm			++fields;
1184231200Smm		} while (sep == L':');
1185231200Smm
1186231200Smm		/* Set remaining fields to blank. */
1187313570Smm		for (n = fields; n < numfields; ++n)
1188231200Smm			field[n].start = field[n].end = NULL;
1189231200Smm
1190313570Smm		if (field[0].start != NULL && *(field[0].start) == L'#') {
1191313570Smm			/* Comment, skip entry */
1192313570Smm			continue;
1193313570Smm		}
1194313570Smm
1195313570Smm		n = 0;
1196313570Smm		sol = 0;
1197231200Smm		id = -1;
1198313570Smm		permset = 0;
1199313570Smm		name.start = name.end = NULL;
1200231200Smm
1201313570Smm		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1202313570Smm			/* POSIX.1e ACLs */
1203313570Smm			/*
1204313570Smm			 * Default keyword "default:user::rwx"
1205313570Smm			 * if found, we have one more field
1206313570Smm			 *
1207313570Smm			 * We also support old Solaris extension:
1208313570Smm			 * "defaultuser::rwx" is the default ACL corresponding
1209313570Smm			 * to "user::rwx", etc. valid only for first field
1210313570Smm			 */
1211313570Smm			s = field[0].start;
1212313570Smm			len = field[0].end - field[0].start;
1213313570Smm			if (*s == L'd' && (len == 1 || (len >= 7
1214313570Smm			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
1215313570Smm				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1216313570Smm				if (len > 7)
1217313570Smm					field[0].start += 7;
1218313570Smm				else
1219313570Smm					n = 1;
1220313570Smm			} else
1221313570Smm				type = want_type;
1222231200Smm
1223313570Smm			/* Check for a numeric ID in field n+1 or n+3. */
1224313570Smm			isint_w(field[n + 1].start, field[n + 1].end, &id);
1225313570Smm			/* Field n+3 is optional. */
1226313570Smm			if (id == -1 && fields > n+3)
1227313570Smm				isint_w(field[n + 3].start, field[n + 3].end,
1228313570Smm				    &id);
1229313570Smm
1230313570Smm			tag = 0;
1231313570Smm			s = field[n].start;
1232313570Smm			st = field[n].start + 1;
1233313570Smm			len = field[n].end - field[n].start;
1234313570Smm
1235313570Smm			switch (*s) {
1236313570Smm			case L'u':
1237313570Smm				if (len == 1 || (len == 4
1238313570Smm				    && wmemcmp(st, L"ser", 3) == 0))
1239313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1240313570Smm				break;
1241313570Smm			case L'g':
1242313570Smm				if (len == 1 || (len == 5
1243313570Smm				    && wmemcmp(st, L"roup", 4) == 0))
1244313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1245313570Smm				break;
1246313570Smm			case L'o':
1247313570Smm				if (len == 1 || (len == 5
1248313570Smm				    && wmemcmp(st, L"ther", 4) == 0))
1249313570Smm					tag = ARCHIVE_ENTRY_ACL_OTHER;
1250313570Smm				break;
1251313570Smm			case L'm':
1252313570Smm				if (len == 1 || (len == 4
1253313570Smm				    && wmemcmp(st, L"ask", 3) == 0))
1254313570Smm					tag = ARCHIVE_ENTRY_ACL_MASK;
1255313570Smm				break;
1256313570Smm			default:
1257313570Smm					break;
1258313570Smm			}
1259313570Smm
1260313570Smm			switch (tag) {
1261313570Smm			case ARCHIVE_ENTRY_ACL_OTHER:
1262313570Smm			case ARCHIVE_ENTRY_ACL_MASK:
1263313570Smm				if (fields == (n + 2)
1264313570Smm				    && field[n + 1].start < field[n + 1].end
1265313570Smm				    && ismode_w(field[n + 1].start,
1266313570Smm				    field[n + 1].end, &permset)) {
1267313570Smm					/* This is Solaris-style "other:rwx" */
1268313570Smm					sol = 1;
1269313570Smm				} else if (fields == (n + 3) &&
1270313570Smm				    field[n + 1].start < field[n + 1].end) {
1271313570Smm					/* Invalid mask or other field */
1272313570Smm					ret = ARCHIVE_WARN;
1273313570Smm					continue;
1274313570Smm				}
1275313570Smm				break;
1276313570Smm			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1277313570Smm			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1278313570Smm				if (id != -1 ||
1279313570Smm				    field[n + 1].start < field[n + 1].end) {
1280313570Smm					name = field[n + 1];
1281313570Smm					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1282313570Smm						tag = ARCHIVE_ENTRY_ACL_USER;
1283313570Smm					else
1284313570Smm						tag = ARCHIVE_ENTRY_ACL_GROUP;
1285313570Smm				}
1286313570Smm				break;
1287313570Smm			default:
1288313570Smm				/* Invalid tag, skip entry */
1289313570Smm				ret = ARCHIVE_WARN;
1290313570Smm				continue;
1291313570Smm			}
1292313570Smm
1293313570Smm			/*
1294313570Smm			 * Without "default:" we expect mode in field 2
1295313570Smm			 * Exception: Solaris other and mask fields
1296313570Smm			 */
1297313570Smm			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1298313570Smm			    field[n + 2 - sol].end, &permset)) {
1299313570Smm				/* Invalid mode, skip entry */
1300313570Smm				ret = ARCHIVE_WARN;
1301313570Smm				continue;
1302313570Smm			}
1303313570Smm		} else {
1304313570Smm			/* NFS4 ACLs */
1305313570Smm			s = field[0].start;
1306313570Smm			len = field[0].end - field[0].start;
1307313570Smm			tag = 0;
1308313570Smm
1309313570Smm			switch (len) {
1310313570Smm			case 4:
1311313570Smm				if (wmemcmp(s, L"user", 4) == 0)
1312313570Smm					tag = ARCHIVE_ENTRY_ACL_USER;
1313313570Smm				break;
1314313570Smm			case 5:
1315313570Smm				if (wmemcmp(s, L"group", 5) == 0)
1316313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP;
1317313570Smm				break;
1318313570Smm			case 6:
1319313570Smm				if (wmemcmp(s, L"owner@", 6) == 0)
1320313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1321313570Smm				else if (wmemcmp(s, L"group@", len) == 0)
1322313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1323313570Smm				break;
1324313570Smm			case 9:
1325313570Smm				if (wmemcmp(s, L"everyone@", 9) == 0)
1326313570Smm					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1327313570Smm			default:
1328313570Smm				break;
1329313570Smm			}
1330313570Smm
1331313570Smm			if (tag == 0) {
1332313570Smm				/* Invalid tag, skip entry */
1333313570Smm				ret = ARCHIVE_WARN;
1334313570Smm				continue;
1335313570Smm			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1336313570Smm			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1337313570Smm				n = 1;
1338231200Smm				name = field[1];
1339313570Smm				isint_w(name.start, name.end, &id);
1340231200Smm			} else
1341313570Smm				n = 0;
1342231200Smm
1343313570Smm			if (!is_nfs4_perms_w(field[1 + n].start,
1344313570Smm			    field[1 + n].end, &permset)) {
1345313570Smm				/* Invalid NFSv4 perms, skip entry */
1346313570Smm				ret = ARCHIVE_WARN;
1347313570Smm				continue;
1348313570Smm			}
1349313570Smm			if (!is_nfs4_flags_w(field[2 + n].start,
1350313570Smm			    field[2 + n].end, &permset)) {
1351313570Smm				/* Invalid NFSv4 flags, skip entry */
1352313570Smm				ret = ARCHIVE_WARN;
1353313570Smm				continue;
1354313570Smm			}
1355313570Smm			s = field[3 + n].start;
1356313570Smm			len = field[3 + n].end - field[3 + n].start;
1357313570Smm			type = 0;
1358313570Smm			if (len == 4) {
1359313570Smm				if (wmemcmp(s, L"deny", 4) == 0)
1360313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1361313570Smm			} else if (len == 5) {
1362313570Smm				if (wmemcmp(s, L"allow", 5) == 0)
1363313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1364313570Smm				else if (wmemcmp(s, L"audit", 5) == 0)
1365313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1366313570Smm				else if (wmemcmp(s, L"alarm", 5) == 0)
1367313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1368313570Smm			}
1369313570Smm			if (type == 0) {
1370313570Smm				/* Invalid entry type, skip entry */
1371313570Smm				ret = ARCHIVE_WARN;
1372313570Smm				continue;
1373313570Smm			}
1374313570Smm			isint_w(field[4 + n].start, field[4 + n].end, &id);
1375313570Smm		}
1376313570Smm
1377231200Smm		/* Add entry to the internal list. */
1378313570Smm		r = archive_acl_add_entry_w_len(acl, type, permset,
1379231200Smm		    tag, id, name.start, name.end - name.start);
1380313570Smm		if (r < ARCHIVE_WARN)
1381313570Smm			return (r);
1382313570Smm		if (r != ARCHIVE_OK)
1383313570Smm			ret = ARCHIVE_WARN;
1384313570Smm		types |= type;
1385231200Smm	}
1386313570Smm
1387313570Smm	/* Reset ACL */
1388313570Smm	archive_acl_reset(acl, types);
1389313570Smm
1390313570Smm	return (ret);
1391231200Smm}
1392231200Smm
1393231200Smm/*
1394231200Smm * Parse a string to a positive decimal integer.  Returns true if
1395231200Smm * the string is non-empty and consists only of decimal digits,
1396231200Smm * false otherwise.
1397231200Smm */
1398231200Smmstatic int
1399231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result)
1400231200Smm{
1401231200Smm	int n = 0;
1402231200Smm	if (start >= end)
1403231200Smm		return (0);
1404231200Smm	while (start < end) {
1405368707Smm		if (*start < L'0' || *start > L'9')
1406231200Smm			return (0);
1407231200Smm		if (n > (INT_MAX / 10) ||
1408368707Smm		    (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
1409231200Smm			n = INT_MAX;
1410231200Smm		} else {
1411231200Smm			n *= 10;
1412368707Smm			n += *start - L'0';
1413231200Smm		}
1414231200Smm		start++;
1415231200Smm	}
1416231200Smm	*result = n;
1417231200Smm	return (1);
1418231200Smm}
1419231200Smm
1420231200Smm/*
1421231200Smm * Parse a string as a mode field.  Returns true if
1422231200Smm * the string is non-empty and consists only of mode characters,
1423231200Smm * false otherwise.
1424231200Smm */
1425231200Smmstatic int
1426231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1427231200Smm{
1428231200Smm	const wchar_t *p;
1429231200Smm
1430231200Smm	if (start >= end)
1431231200Smm		return (0);
1432231200Smm	p = start;
1433231200Smm	*permset = 0;
1434231200Smm	while (p < end) {
1435231200Smm		switch (*p++) {
1436313570Smm		case L'r': case L'R':
1437231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1438231200Smm			break;
1439313570Smm		case L'w': case L'W':
1440231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1441231200Smm			break;
1442313570Smm		case L'x': case L'X':
1443231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1444231200Smm			break;
1445313570Smm		case L'-':
1446231200Smm			break;
1447231200Smm		default:
1448231200Smm			return (0);
1449231200Smm		}
1450231200Smm	}
1451231200Smm	return (1);
1452231200Smm}
1453231200Smm
1454231200Smm/*
1455313570Smm * Parse a string as a NFS4 ACL permission field.
1456313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1457313570Smm * permission characters, false otherwise
1458313570Smm */
1459313570Smmstatic int
1460313570Smmis_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1461313570Smm{
1462313926Smm	const wchar_t *p = start;
1463313570Smm
1464313570Smm	while (p < end) {
1465313570Smm		switch (*p++) {
1466313570Smm		case L'r':
1467313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1468313570Smm			break;
1469313570Smm		case L'w':
1470313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1471313570Smm			break;
1472313570Smm		case L'x':
1473313570Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1474313570Smm			break;
1475313570Smm		case L'p':
1476313570Smm			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1477313570Smm			break;
1478313570Smm		case L'D':
1479313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1480313570Smm			break;
1481313570Smm		case L'd':
1482313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1483313570Smm			break;
1484313570Smm		case L'a':
1485313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1486313570Smm			break;
1487313570Smm		case L'A':
1488313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1489313570Smm			break;
1490313570Smm		case L'R':
1491313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1492313570Smm			break;
1493313570Smm		case L'W':
1494313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1495313570Smm			break;
1496313570Smm		case L'c':
1497313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1498313570Smm			break;
1499313570Smm		case L'C':
1500313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1501313570Smm			break;
1502313570Smm		case L'o':
1503313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1504313570Smm			break;
1505313570Smm		case L's':
1506313570Smm			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1507313570Smm			break;
1508313570Smm		case L'-':
1509313570Smm			break;
1510313570Smm		default:
1511313570Smm			return(0);
1512313570Smm		}
1513313570Smm	}
1514313570Smm	return (1);
1515313570Smm}
1516313570Smm
1517313570Smm/*
1518313570Smm * Parse a string as a NFS4 ACL flags field.
1519313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1520313570Smm * flag characters, false otherwise
1521313570Smm */
1522313570Smmstatic int
1523313570Smmis_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1524313570Smm{
1525313926Smm	const wchar_t *p = start;
1526313570Smm
1527313570Smm	while (p < end) {
1528313570Smm		switch(*p++) {
1529313570Smm		case L'f':
1530313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1531313570Smm			break;
1532313570Smm		case L'd':
1533313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1534313570Smm			break;
1535313570Smm		case L'i':
1536313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1537313570Smm			break;
1538313570Smm		case L'n':
1539313570Smm			*permset |=
1540313570Smm			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1541313570Smm			break;
1542313570Smm		case L'S':
1543313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1544313570Smm			break;
1545313570Smm		case L'F':
1546313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1547313570Smm			break;
1548313570Smm		case L'I':
1549313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1550313570Smm			break;
1551313570Smm		case L'-':
1552313570Smm			break;
1553313570Smm		default:
1554313570Smm			return (0);
1555313570Smm		}
1556313570Smm	}
1557313570Smm	return (1);
1558313570Smm}
1559313570Smm
1560313570Smm/*
1561231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1562231200Smm * to point to just after the separator.  *start points to the first
1563231200Smm * character of the matched text and *end just after the last
1564231200Smm * character of the matched identifier.  In particular *end - *start
1565231200Smm * is the length of the field body, not including leading or trailing
1566231200Smm * whitespace.
1567231200Smm */
1568231200Smmstatic void
1569231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start,
1570231200Smm    const wchar_t **end, wchar_t *sep)
1571231200Smm{
1572231200Smm	/* Skip leading whitespace to find start of field. */
1573231200Smm	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1574231200Smm		(*wp)++;
1575231200Smm	}
1576231200Smm	*start = *wp;
1577231200Smm
1578231200Smm	/* Scan for the separator. */
1579231200Smm	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1580342360Smm	    **wp != L'\n' && **wp != L'#') {
1581231200Smm		(*wp)++;
1582231200Smm	}
1583231200Smm	*sep = **wp;
1584231200Smm
1585342360Smm	/* Locate end of field, trim trailing whitespace if necessary */
1586342360Smm	if (*wp == *start) {
1587342360Smm		*end = *wp;
1588342360Smm	} else {
1589342360Smm		*end = *wp - 1;
1590342360Smm		while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1591342360Smm			(*end)--;
1592342360Smm		}
1593342360Smm		(*end)++;
1594231200Smm	}
1595231200Smm
1596342360Smm	/* Handle in-field comments */
1597342360Smm	if (*sep == L'#') {
1598342360Smm		while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
1599342360Smm			(*wp)++;
1600342360Smm		}
1601342360Smm		*sep = **wp;
1602342360Smm	}
1603342360Smm
1604231200Smm	/* Adjust scanner location. */
1605231200Smm	if (**wp != L'\0')
1606231200Smm		(*wp)++;
1607231200Smm}
1608231200Smm
1609231200Smm/*
1610313570Smm * Parse an ACL text string.
1611313570Smm *
1612313570Smm * The want_type argument may be one of the following:
1613313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1614313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1615313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1616313570Smm *
1617313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as
1618313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1619231200Smm */
1620231200Smmint
1621313570Smmarchive_acl_from_text_l(struct archive_acl *acl, const char *text,
1622313570Smm    int want_type, struct archive_string_conv *sc)
1623231200Smm{
1624231200Smm	struct {
1625231200Smm		const char *start;
1626231200Smm		const char *end;
1627313570Smm	} field[6], name;
1628231200Smm
1629313570Smm	const char *s, *st;
1630313570Smm	int numfields, fields, n, r, sol, ret;
1631313570Smm	int type, types, tag, permset, id;
1632313570Smm	size_t len;
1633231200Smm	char sep;
1634231200Smm
1635313570Smm	switch (want_type) {
1636313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1637313570Smm		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1638328827Smm		__LA_FALLTHROUGH;
1639313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1640313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1641313570Smm		numfields = 5;
1642313570Smm		break;
1643313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1644313570Smm		numfields = 6;
1645313570Smm		break;
1646313570Smm	default:
1647313570Smm		return (ARCHIVE_FATAL);
1648313570Smm	}
1649313570Smm
1650313570Smm	ret = ARCHIVE_OK;
1651313570Smm	types = 0;
1652313570Smm
1653342360Smm	while (text != NULL &&  *text != '\0') {
1654231200Smm		/*
1655231200Smm		 * Parse the fields out of the next entry,
1656231200Smm		 * advance 'text' to start of next entry.
1657231200Smm		 */
1658231200Smm		fields = 0;
1659231200Smm		do {
1660231200Smm			const char *start, *end;
1661231200Smm			next_field(&text, &start, &end, &sep);
1662313570Smm			if (fields < numfields) {
1663231200Smm				field[fields].start = start;
1664231200Smm				field[fields].end = end;
1665231200Smm			}
1666231200Smm			++fields;
1667231200Smm		} while (sep == ':');
1668231200Smm
1669231200Smm		/* Set remaining fields to blank. */
1670313570Smm		for (n = fields; n < numfields; ++n)
1671231200Smm			field[n].start = field[n].end = NULL;
1672231200Smm
1673313570Smm		if (field[0].start != NULL && *(field[0].start) == '#') {
1674313570Smm			/* Comment, skip entry */
1675313570Smm			continue;
1676313570Smm		}
1677313570Smm
1678313570Smm		n = 0;
1679313570Smm		sol = 0;
1680231200Smm		id = -1;
1681313570Smm		permset = 0;
1682313570Smm		name.start = name.end = NULL;
1683231200Smm
1684313570Smm		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1685313570Smm			/* POSIX.1e ACLs */
1686313570Smm			/*
1687313570Smm			 * Default keyword "default:user::rwx"
1688313570Smm			 * if found, we have one more field
1689313570Smm			 *
1690313570Smm			 * We also support old Solaris extension:
1691313570Smm			 * "defaultuser::rwx" is the default ACL corresponding
1692313570Smm			 * to "user::rwx", etc. valid only for first field
1693313570Smm			 */
1694313570Smm			s = field[0].start;
1695313570Smm			len = field[0].end - field[0].start;
1696313570Smm			if (*s == 'd' && (len == 1 || (len >= 7
1697313570Smm			    && memcmp((s + 1), "efault", 6) == 0))) {
1698313570Smm				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1699313570Smm				if (len > 7)
1700313570Smm					field[0].start += 7;
1701313570Smm				else
1702313570Smm					n = 1;
1703313570Smm			} else
1704313570Smm				type = want_type;
1705231200Smm
1706313570Smm			/* Check for a numeric ID in field n+1 or n+3. */
1707313570Smm			isint(field[n + 1].start, field[n + 1].end, &id);
1708313570Smm			/* Field n+3 is optional. */
1709313570Smm			if (id == -1 && fields > (n + 3))
1710313570Smm				isint(field[n + 3].start, field[n + 3].end,
1711313570Smm				    &id);
1712313570Smm
1713313570Smm			tag = 0;
1714313570Smm			s = field[n].start;
1715313570Smm			st = field[n].start + 1;
1716313570Smm			len = field[n].end - field[n].start;
1717313570Smm
1718342360Smm			if (len == 0) {
1719342360Smm				ret = ARCHIVE_WARN;
1720342360Smm				continue;
1721342360Smm			}
1722342360Smm
1723313570Smm			switch (*s) {
1724313570Smm			case 'u':
1725313570Smm				if (len == 1 || (len == 4
1726313570Smm				    && memcmp(st, "ser", 3) == 0))
1727313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1728313570Smm				break;
1729313570Smm			case 'g':
1730313570Smm				if (len == 1 || (len == 5
1731313570Smm				    && memcmp(st, "roup", 4) == 0))
1732313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1733313570Smm				break;
1734313570Smm			case 'o':
1735313570Smm				if (len == 1 || (len == 5
1736313570Smm				    && memcmp(st, "ther", 4) == 0))
1737313570Smm					tag = ARCHIVE_ENTRY_ACL_OTHER;
1738313570Smm				break;
1739313570Smm			case 'm':
1740313570Smm				if (len == 1 || (len == 4
1741313570Smm				    && memcmp(st, "ask", 3) == 0))
1742313570Smm					tag = ARCHIVE_ENTRY_ACL_MASK;
1743313570Smm				break;
1744313570Smm			default:
1745313570Smm					break;
1746313570Smm			}
1747313570Smm
1748313570Smm			switch (tag) {
1749313570Smm			case ARCHIVE_ENTRY_ACL_OTHER:
1750313570Smm			case ARCHIVE_ENTRY_ACL_MASK:
1751313570Smm				if (fields == (n + 2)
1752313570Smm				    && field[n + 1].start < field[n + 1].end
1753313570Smm				    && ismode(field[n + 1].start,
1754313570Smm				    field[n + 1].end, &permset)) {
1755313570Smm					/* This is Solaris-style "other:rwx" */
1756313570Smm					sol = 1;
1757313570Smm				} else if (fields == (n + 3) &&
1758313570Smm				    field[n + 1].start < field[n + 1].end) {
1759313570Smm					/* Invalid mask or other field */
1760313570Smm					ret = ARCHIVE_WARN;
1761313570Smm					continue;
1762313570Smm				}
1763313570Smm				break;
1764313570Smm			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1765313570Smm			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1766313570Smm				if (id != -1 ||
1767313570Smm				    field[n + 1].start < field[n + 1].end) {
1768313570Smm					name = field[n + 1];
1769313570Smm					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1770313570Smm						tag = ARCHIVE_ENTRY_ACL_USER;
1771313570Smm					else
1772313570Smm						tag = ARCHIVE_ENTRY_ACL_GROUP;
1773313570Smm				}
1774313570Smm				break;
1775313570Smm			default:
1776313570Smm				/* Invalid tag, skip entry */
1777313570Smm				ret = ARCHIVE_WARN;
1778313570Smm				continue;
1779313570Smm			}
1780313570Smm
1781313570Smm			/*
1782313570Smm			 * Without "default:" we expect mode in field 3
1783313570Smm			 * Exception: Solaris other and mask fields
1784313570Smm			 */
1785313570Smm			if (permset == 0 && !ismode(field[n + 2 - sol].start,
1786313570Smm			    field[n + 2 - sol].end, &permset)) {
1787313570Smm				/* Invalid mode, skip entry */
1788313570Smm				ret = ARCHIVE_WARN;
1789313570Smm				continue;
1790313570Smm			}
1791313570Smm		} else {
1792313570Smm			/* NFS4 ACLs */
1793313570Smm			s = field[0].start;
1794313570Smm			len = field[0].end - field[0].start;
1795313570Smm			tag = 0;
1796313570Smm
1797313570Smm			switch (len) {
1798313570Smm			case 4:
1799313570Smm				if (memcmp(s, "user", 4) == 0)
1800313570Smm					tag = ARCHIVE_ENTRY_ACL_USER;
1801313570Smm				break;
1802313570Smm			case 5:
1803313570Smm				if (memcmp(s, "group", 5) == 0)
1804313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP;
1805313570Smm				break;
1806313570Smm			case 6:
1807313570Smm				if (memcmp(s, "owner@", 6) == 0)
1808313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1809313570Smm				else if (memcmp(s, "group@", 6) == 0)
1810313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1811313570Smm				break;
1812313570Smm			case 9:
1813313570Smm				if (memcmp(s, "everyone@", 9) == 0)
1814313570Smm					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1815313570Smm				break;
1816313570Smm			default:
1817313570Smm				break;
1818313570Smm			}
1819313570Smm
1820313570Smm			if (tag == 0) {
1821313570Smm				/* Invalid tag, skip entry */
1822313570Smm				ret = ARCHIVE_WARN;
1823313570Smm				continue;
1824313570Smm			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1825313570Smm			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1826313570Smm				n = 1;
1827231200Smm				name = field[1];
1828313570Smm				isint(name.start, name.end, &id);
1829231200Smm			} else
1830313570Smm				n = 0;
1831231200Smm
1832313570Smm			if (!is_nfs4_perms(field[1 + n].start,
1833313570Smm			    field[1 + n].end, &permset)) {
1834313570Smm				/* Invalid NFSv4 perms, skip entry */
1835313570Smm				ret = ARCHIVE_WARN;
1836313570Smm				continue;
1837313570Smm			}
1838313570Smm			if (!is_nfs4_flags(field[2 + n].start,
1839313570Smm			    field[2 + n].end, &permset)) {
1840313570Smm				/* Invalid NFSv4 flags, skip entry */
1841313570Smm				ret = ARCHIVE_WARN;
1842313570Smm				continue;
1843313570Smm			}
1844313570Smm			s = field[3 + n].start;
1845313570Smm			len = field[3 + n].end - field[3 + n].start;
1846313570Smm			type = 0;
1847313570Smm			if (len == 4) {
1848313570Smm				if (memcmp(s, "deny", 4) == 0)
1849313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1850313570Smm			} else if (len == 5) {
1851313570Smm				if (memcmp(s, "allow", 5) == 0)
1852313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1853313570Smm				else if (memcmp(s, "audit", 5) == 0)
1854313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1855313570Smm				else if (memcmp(s, "alarm", 5) == 0)
1856313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1857313570Smm			}
1858313570Smm			if (type == 0) {
1859313570Smm				/* Invalid entry type, skip entry */
1860313570Smm				ret = ARCHIVE_WARN;
1861313570Smm				continue;
1862313570Smm			}
1863313570Smm			isint(field[4 + n].start, field[4 + n].end,
1864313570Smm			    &id);
1865313570Smm		}
1866313570Smm
1867231200Smm		/* Add entry to the internal list. */
1868231200Smm		r = archive_acl_add_entry_len_l(acl, type, permset,
1869231200Smm		    tag, id, name.start, name.end - name.start, sc);
1870231200Smm		if (r < ARCHIVE_WARN)
1871231200Smm			return (r);
1872231200Smm		if (r != ARCHIVE_OK)
1873231200Smm			ret = ARCHIVE_WARN;
1874313570Smm		types |= type;
1875231200Smm	}
1876313570Smm
1877313570Smm	/* Reset ACL */
1878313570Smm	archive_acl_reset(acl, types);
1879313570Smm
1880231200Smm	return (ret);
1881231200Smm}
1882231200Smm
1883231200Smm/*
1884231200Smm * Parse a string to a positive decimal integer.  Returns true if
1885231200Smm * the string is non-empty and consists only of decimal digits,
1886231200Smm * false otherwise.
1887231200Smm */
1888231200Smmstatic int
1889231200Smmisint(const char *start, const char *end, int *result)
1890231200Smm{
1891231200Smm	int n = 0;
1892231200Smm	if (start >= end)
1893231200Smm		return (0);
1894231200Smm	while (start < end) {
1895231200Smm		if (*start < '0' || *start > '9')
1896231200Smm			return (0);
1897231200Smm		if (n > (INT_MAX / 10) ||
1898231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1899231200Smm			n = INT_MAX;
1900231200Smm		} else {
1901231200Smm			n *= 10;
1902231200Smm			n += *start - '0';
1903231200Smm		}
1904231200Smm		start++;
1905231200Smm	}
1906231200Smm	*result = n;
1907231200Smm	return (1);
1908231200Smm}
1909231200Smm
1910231200Smm/*
1911231200Smm * Parse a string as a mode field.  Returns true if
1912231200Smm * the string is non-empty and consists only of mode characters,
1913231200Smm * false otherwise.
1914231200Smm */
1915231200Smmstatic int
1916231200Smmismode(const char *start, const char *end, int *permset)
1917231200Smm{
1918231200Smm	const char *p;
1919231200Smm
1920231200Smm	if (start >= end)
1921231200Smm		return (0);
1922231200Smm	p = start;
1923231200Smm	*permset = 0;
1924231200Smm	while (p < end) {
1925231200Smm		switch (*p++) {
1926231200Smm		case 'r': case 'R':
1927231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1928231200Smm			break;
1929231200Smm		case 'w': case 'W':
1930231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1931231200Smm			break;
1932231200Smm		case 'x': case 'X':
1933231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1934231200Smm			break;
1935231200Smm		case '-':
1936231200Smm			break;
1937231200Smm		default:
1938231200Smm			return (0);
1939231200Smm		}
1940231200Smm	}
1941231200Smm	return (1);
1942231200Smm}
1943231200Smm
1944231200Smm/*
1945313570Smm * Parse a string as a NFS4 ACL permission field.
1946313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1947313570Smm * permission characters, false otherwise
1948313570Smm */
1949313570Smmstatic int
1950313570Smmis_nfs4_perms(const char *start, const char *end, int *permset)
1951313570Smm{
1952313926Smm	const char *p = start;
1953313570Smm
1954313570Smm	while (p < end) {
1955313570Smm		switch (*p++) {
1956313570Smm		case 'r':
1957313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1958313570Smm			break;
1959313570Smm		case 'w':
1960313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1961313570Smm			break;
1962313570Smm		case 'x':
1963313570Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1964313570Smm			break;
1965313570Smm		case 'p':
1966313570Smm			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1967313570Smm			break;
1968313570Smm		case 'D':
1969313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1970313570Smm			break;
1971313570Smm		case 'd':
1972313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1973313570Smm			break;
1974313570Smm		case 'a':
1975313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1976313570Smm			break;
1977313570Smm		case 'A':
1978313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1979313570Smm			break;
1980313570Smm		case 'R':
1981313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1982313570Smm			break;
1983313570Smm		case 'W':
1984313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1985313570Smm			break;
1986313570Smm		case 'c':
1987313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1988313570Smm			break;
1989313570Smm		case 'C':
1990313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1991313570Smm			break;
1992313570Smm		case 'o':
1993313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1994313570Smm			break;
1995313570Smm		case 's':
1996313570Smm			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1997313570Smm			break;
1998313570Smm		case '-':
1999313570Smm			break;
2000313570Smm		default:
2001313570Smm			return(0);
2002313570Smm		}
2003313570Smm	}
2004313570Smm	return (1);
2005313570Smm}
2006313570Smm
2007313570Smm/*
2008313570Smm * Parse a string as a NFS4 ACL flags field.
2009313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
2010313570Smm * flag characters, false otherwise
2011313570Smm */
2012313570Smmstatic int
2013313570Smmis_nfs4_flags(const char *start, const char *end, int *permset)
2014313570Smm{
2015313926Smm	const char *p = start;
2016313570Smm
2017313570Smm	while (p < end) {
2018313570Smm		switch(*p++) {
2019313570Smm		case 'f':
2020313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2021313570Smm			break;
2022313570Smm		case 'd':
2023313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2024313570Smm			break;
2025313570Smm		case 'i':
2026313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2027313570Smm			break;
2028313570Smm		case 'n':
2029313570Smm			*permset |=
2030313570Smm			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2031313570Smm			break;
2032313570Smm		case 'S':
2033313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2034313570Smm			break;
2035313570Smm		case 'F':
2036313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2037313570Smm			break;
2038313570Smm		case 'I':
2039313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2040313570Smm			break;
2041313570Smm		case '-':
2042313570Smm			break;
2043313570Smm		default:
2044313570Smm			return (0);
2045313570Smm		}
2046313570Smm	}
2047313570Smm	return (1);
2048313570Smm}
2049313570Smm
2050313570Smm/*
2051231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2052231200Smm * to point to just after the separator.  *start points to the first
2053231200Smm * character of the matched text and *end just after the last
2054231200Smm * character of the matched identifier.  In particular *end - *start
2055231200Smm * is the length of the field body, not including leading or trailing
2056231200Smm * whitespace.
2057231200Smm */
2058231200Smmstatic void
2059231200Smmnext_field(const char **p, const char **start,
2060231200Smm    const char **end, char *sep)
2061231200Smm{
2062231200Smm	/* Skip leading whitespace to find start of field. */
2063231200Smm	while (**p == ' ' || **p == '\t' || **p == '\n') {
2064231200Smm		(*p)++;
2065231200Smm	}
2066231200Smm	*start = *p;
2067231200Smm
2068231200Smm	/* Scan for the separator. */
2069342360Smm	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
2070342360Smm	    **p != '#') {
2071231200Smm		(*p)++;
2072231200Smm	}
2073231200Smm	*sep = **p;
2074231200Smm
2075342360Smm	/* Locate end of field, trim trailing whitespace if necessary */
2076342360Smm	if (*p == *start) {
2077339005Smm		*end = *p;
2078342360Smm	} else {
2079342360Smm		*end = *p - 1;
2080342360Smm		while (**end == ' ' || **end == '\t' || **end == '\n') {
2081342360Smm			(*end)--;
2082342360Smm		}
2083342360Smm		(*end)++;
2084339005Smm	}
2085339005Smm
2086342360Smm	/* Handle in-field comments */
2087342360Smm	if (*sep == '#') {
2088342360Smm		while (**p != '\0' && **p != ',' && **p != '\n') {
2089342360Smm			(*p)++;
2090342360Smm		}
2091342360Smm		*sep = **p;
2092231200Smm	}
2093231200Smm
2094231200Smm	/* Adjust scanner location. */
2095231200Smm	if (**p != '\0')
2096231200Smm		(*p)++;
2097231200Smm}
2098