archive_acl.c revision 313570
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
86231200Smmvoid
87231200Smmarchive_acl_clear(struct archive_acl *acl)
88231200Smm{
89231200Smm	struct archive_acl_entry *ap;
90231200Smm
91231200Smm	while (acl->acl_head != NULL) {
92231200Smm		ap = acl->acl_head->next;
93231200Smm		archive_mstring_clean(&acl->acl_head->name);
94231200Smm		free(acl->acl_head);
95231200Smm		acl->acl_head = ap;
96231200Smm	}
97231200Smm	if (acl->acl_text_w != NULL) {
98231200Smm		free(acl->acl_text_w);
99231200Smm		acl->acl_text_w = NULL;
100231200Smm	}
101231200Smm	if (acl->acl_text != NULL) {
102231200Smm		free(acl->acl_text);
103231200Smm		acl->acl_text = NULL;
104231200Smm	}
105231200Smm	acl->acl_p = NULL;
106311041Smm	acl->acl_types = 0;
107231200Smm	acl->acl_state = 0; /* Not counting. */
108231200Smm}
109231200Smm
110231200Smmvoid
111231200Smmarchive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
112231200Smm{
113231200Smm	struct archive_acl_entry *ap, *ap2;
114231200Smm
115231200Smm	archive_acl_clear(dest);
116231200Smm
117231200Smm	dest->mode = src->mode;
118231200Smm	ap = src->acl_head;
119231200Smm	while (ap != NULL) {
120231200Smm		ap2 = acl_new_entry(dest,
121231200Smm		    ap->type, ap->permset, ap->tag, ap->id);
122231200Smm		if (ap2 != NULL)
123231200Smm			archive_mstring_copy(&ap2->name, &ap->name);
124231200Smm		ap = ap->next;
125231200Smm	}
126231200Smm}
127231200Smm
128231200Smmint
129231200Smmarchive_acl_add_entry(struct archive_acl *acl,
130231200Smm    int type, int permset, int tag, int id, const char *name)
131231200Smm{
132231200Smm	struct archive_acl_entry *ap;
133231200Smm
134231200Smm	if (acl_special(acl, type, permset, tag) == 0)
135231200Smm		return ARCHIVE_OK;
136231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
137231200Smm	if (ap == NULL) {
138231200Smm		/* XXX Error XXX */
139231200Smm		return ARCHIVE_FAILED;
140231200Smm	}
141231200Smm	if (name != NULL  &&  *name != '\0')
142231200Smm		archive_mstring_copy_mbs(&ap->name, name);
143231200Smm	else
144231200Smm		archive_mstring_clean(&ap->name);
145231200Smm	return ARCHIVE_OK;
146231200Smm}
147231200Smm
148231200Smmint
149231200Smmarchive_acl_add_entry_w_len(struct archive_acl *acl,
150231200Smm    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
151231200Smm{
152231200Smm	struct archive_acl_entry *ap;
153231200Smm
154231200Smm	if (acl_special(acl, type, permset, tag) == 0)
155231200Smm		return ARCHIVE_OK;
156231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
157231200Smm	if (ap == NULL) {
158231200Smm		/* XXX Error XXX */
159231200Smm		return ARCHIVE_FAILED;
160231200Smm	}
161231200Smm	if (name != NULL  &&  *name != L'\0' && len > 0)
162231200Smm		archive_mstring_copy_wcs_len(&ap->name, name, len);
163231200Smm	else
164231200Smm		archive_mstring_clean(&ap->name);
165231200Smm	return ARCHIVE_OK;
166231200Smm}
167231200Smm
168232153Smmstatic int
169231200Smmarchive_acl_add_entry_len_l(struct archive_acl *acl,
170231200Smm    int type, int permset, int tag, int id, const char *name, size_t len,
171231200Smm    struct archive_string_conv *sc)
172231200Smm{
173231200Smm	struct archive_acl_entry *ap;
174231200Smm	int r;
175231200Smm
176231200Smm	if (acl_special(acl, type, permset, tag) == 0)
177231200Smm		return ARCHIVE_OK;
178231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
179231200Smm	if (ap == NULL) {
180231200Smm		/* XXX Error XXX */
181231200Smm		return ARCHIVE_FAILED;
182231200Smm	}
183231200Smm	if (name != NULL  &&  *name != '\0' && len > 0) {
184231200Smm		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
185231200Smm	} else {
186231200Smm		r = 0;
187231200Smm		archive_mstring_clean(&ap->name);
188231200Smm	}
189231200Smm	if (r == 0)
190231200Smm		return (ARCHIVE_OK);
191231200Smm	else if (errno == ENOMEM)
192231200Smm		return (ARCHIVE_FATAL);
193231200Smm	else
194231200Smm		return (ARCHIVE_WARN);
195231200Smm}
196231200Smm
197231200Smm/*
198231200Smm * If this ACL entry is part of the standard POSIX permissions set,
199231200Smm * store the permissions in the stat structure and return zero.
200231200Smm */
201231200Smmstatic int
202231200Smmacl_special(struct archive_acl *acl, int type, int permset, int tag)
203231200Smm{
204231200Smm	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
205231200Smm	    && ((permset & ~007) == 0)) {
206231200Smm		switch (tag) {
207231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
208231200Smm			acl->mode &= ~0700;
209231200Smm			acl->mode |= (permset & 7) << 6;
210231200Smm			return (0);
211231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
212231200Smm			acl->mode &= ~0070;
213231200Smm			acl->mode |= (permset & 7) << 3;
214231200Smm			return (0);
215231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
216231200Smm			acl->mode &= ~0007;
217231200Smm			acl->mode |= permset & 7;
218231200Smm			return (0);
219231200Smm		}
220231200Smm	}
221231200Smm	return (1);
222231200Smm}
223231200Smm
224231200Smm/*
225231200Smm * Allocate and populate a new ACL entry with everything but the
226231200Smm * name.
227231200Smm */
228231200Smmstatic struct archive_acl_entry *
229231200Smmacl_new_entry(struct archive_acl *acl,
230231200Smm    int type, int permset, int tag, int id)
231231200Smm{
232231200Smm	struct archive_acl_entry *ap, *aq;
233231200Smm
234231200Smm	/* Type argument must be a valid NFS4 or POSIX.1e type.
235231200Smm	 * The type must agree with anything already set and
236231200Smm	 * the permset must be compatible. */
237231200Smm	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
238231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
239231200Smm			return (NULL);
240231200Smm		}
241231200Smm		if (permset &
242231200Smm		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
243231200Smm			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
244231200Smm			return (NULL);
245231200Smm		}
246231200Smm	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
247231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
248231200Smm			return (NULL);
249231200Smm		}
250231200Smm		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
251231200Smm			return (NULL);
252231200Smm		}
253231200Smm	} else {
254231200Smm		return (NULL);
255231200Smm	}
256231200Smm
257231200Smm	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
258231200Smm	switch (tag) {
259231200Smm	case ARCHIVE_ENTRY_ACL_USER:
260231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
261231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
262231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
263231200Smm		/* Tags valid in both NFS4 and POSIX.1e */
264231200Smm		break;
265231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
266231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
267231200Smm		/* Tags valid only in POSIX.1e. */
268231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
269231200Smm			return (NULL);
270231200Smm		}
271231200Smm		break;
272231200Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
273231200Smm		/* Tags valid only in NFS4. */
274231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
275231200Smm			return (NULL);
276231200Smm		}
277231200Smm		break;
278231200Smm	default:
279231200Smm		/* No other values are valid. */
280231200Smm		return (NULL);
281231200Smm	}
282231200Smm
283231200Smm	if (acl->acl_text_w != NULL) {
284231200Smm		free(acl->acl_text_w);
285231200Smm		acl->acl_text_w = NULL;
286231200Smm	}
287231200Smm	if (acl->acl_text != NULL) {
288231200Smm		free(acl->acl_text);
289231200Smm		acl->acl_text = NULL;
290231200Smm	}
291231200Smm
292311041Smm	/*
293311041Smm	 * If there's a matching entry already in the list, overwrite it.
294311041Smm	 * NFSv4 entries may be repeated and are not overwritten.
295311041Smm	 *
296311041Smm	 * TODO: compare names of no id is provided (needs more rework)
297311041Smm	 */
298231200Smm	ap = acl->acl_head;
299231200Smm	aq = NULL;
300231200Smm	while (ap != NULL) {
301311041Smm		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
302311041Smm		    ap->type == type && ap->tag == tag && ap->id == id) {
303311041Smm			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
304311041Smm			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {
305311041Smm				ap->permset = permset;
306311041Smm				return (ap);
307311041Smm			}
308231200Smm		}
309231200Smm		aq = ap;
310231200Smm		ap = ap->next;
311231200Smm	}
312231200Smm
313231200Smm	/* Add a new entry to the end of the list. */
314311041Smm	ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
315231200Smm	if (ap == NULL)
316231200Smm		return (NULL);
317231200Smm	if (aq == NULL)
318231200Smm		acl->acl_head = ap;
319231200Smm	else
320231200Smm		aq->next = ap;
321231200Smm	ap->type = type;
322231200Smm	ap->tag = tag;
323231200Smm	ap->id = id;
324231200Smm	ap->permset = permset;
325231200Smm	acl->acl_types |= type;
326231200Smm	return (ap);
327231200Smm}
328231200Smm
329231200Smm/*
330231200Smm * Return a count of entries matching "want_type".
331231200Smm */
332231200Smmint
333231200Smmarchive_acl_count(struct archive_acl *acl, int want_type)
334231200Smm{
335231200Smm	int count;
336231200Smm	struct archive_acl_entry *ap;
337231200Smm
338231200Smm	count = 0;
339231200Smm	ap = acl->acl_head;
340231200Smm	while (ap != NULL) {
341231200Smm		if ((ap->type & want_type) != 0)
342231200Smm			count++;
343231200Smm		ap = ap->next;
344231200Smm	}
345231200Smm
346231200Smm	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
347231200Smm		count += 3;
348231200Smm	return (count);
349231200Smm}
350231200Smm
351231200Smm/*
352313570Smm * Return a bitmask of stored ACL types in an ACL list
353313570Smm */
354313570Smmint
355313570Smmarchive_acl_types(struct archive_acl *acl)
356313570Smm{
357313570Smm	return (acl->acl_types);
358313570Smm}
359313570Smm
360313570Smm/*
361231200Smm * Prepare for reading entries from the ACL data.  Returns a count
362231200Smm * of entries matching "want_type", or zero if there are no
363231200Smm * non-extended ACL entries of that type.
364231200Smm */
365231200Smmint
366231200Smmarchive_acl_reset(struct archive_acl *acl, int want_type)
367231200Smm{
368231200Smm	int count, cutoff;
369231200Smm
370231200Smm	count = archive_acl_count(acl, want_type);
371231200Smm
372231200Smm	/*
373231200Smm	 * If the only entries are the three standard ones,
374231200Smm	 * then don't return any ACL data.  (In this case,
375231200Smm	 * client can just use chmod(2) to set permissions.)
376231200Smm	 */
377231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
378231200Smm		cutoff = 3;
379231200Smm	else
380231200Smm		cutoff = 0;
381231200Smm
382231200Smm	if (count > cutoff)
383231200Smm		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
384231200Smm	else
385231200Smm		acl->acl_state = 0;
386231200Smm	acl->acl_p = acl->acl_head;
387231200Smm	return (count);
388231200Smm}
389231200Smm
390231200Smm
391231200Smm/*
392231200Smm * Return the next ACL entry in the list.  Fake entries for the
393231200Smm * standard permissions and include them in the returned list.
394231200Smm */
395231200Smmint
396313570Smmarchive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
397313570Smm    int *type, int *permset, int *tag, int *id, const char **name)
398231200Smm{
399231200Smm	*name = NULL;
400231200Smm	*id = -1;
401231200Smm
402231200Smm	/*
403231200Smm	 * The acl_state is either zero (no entries available), -1
404231200Smm	 * (reading from list), or an entry type (retrieve that type
405231200Smm	 * from ae_stat.aest_mode).
406231200Smm	 */
407231200Smm	if (acl->acl_state == 0)
408231200Smm		return (ARCHIVE_WARN);
409231200Smm
410231200Smm	/* The first three access entries are special. */
411231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
412231200Smm		switch (acl->acl_state) {
413231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
414231200Smm			*permset = (acl->mode >> 6) & 7;
415231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
416231200Smm			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
417231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
418231200Smm			return (ARCHIVE_OK);
419231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
420231200Smm			*permset = (acl->mode >> 3) & 7;
421231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
422231200Smm			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
423231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
424231200Smm			return (ARCHIVE_OK);
425231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
426231200Smm			*permset = acl->mode & 7;
427231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
428231200Smm			*tag = ARCHIVE_ENTRY_ACL_OTHER;
429231200Smm			acl->acl_state = -1;
430231200Smm			acl->acl_p = acl->acl_head;
431231200Smm			return (ARCHIVE_OK);
432231200Smm		default:
433231200Smm			break;
434231200Smm		}
435231200Smm	}
436231200Smm
437231200Smm	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
438231200Smm		acl->acl_p = acl->acl_p->next;
439231200Smm	if (acl->acl_p == NULL) {
440231200Smm		acl->acl_state = 0;
441231200Smm		*type = 0;
442231200Smm		*permset = 0;
443231200Smm		*tag = 0;
444231200Smm		*id = -1;
445231200Smm		*name = NULL;
446231200Smm		return (ARCHIVE_EOF); /* End of ACL entries. */
447231200Smm	}
448231200Smm	*type = acl->acl_p->type;
449231200Smm	*permset = acl->acl_p->permset;
450231200Smm	*tag = acl->acl_p->tag;
451231200Smm	*id = acl->acl_p->id;
452238856Smm	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
453238856Smm		if (errno == ENOMEM)
454238856Smm			return (ARCHIVE_FATAL);
455231200Smm		*name = NULL;
456238856Smm	}
457231200Smm	acl->acl_p = acl->acl_p->next;
458231200Smm	return (ARCHIVE_OK);
459231200Smm}
460231200Smm
461231200Smm/*
462313570Smm * Determine what type of ACL do we want
463231200Smm */
464313570Smmstatic int
465313570Smmarchive_acl_text_want_type(struct archive_acl *acl, int flags)
466231200Smm{
467313570Smm	int want_type;
468231200Smm
469313570Smm	/* Check if ACL is NFSv4 */
470313570Smm	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
471313570Smm		/* NFSv4 should never mix with POSIX.1e */
472313570Smm		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
473313570Smm			return (0);
474313570Smm		else
475313570Smm			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
476231200Smm	}
477231200Smm
478313570Smm	/* Now deal with POSIX.1e ACLs */
479313570Smm
480313570Smm	want_type = 0;
481313570Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
482313570Smm		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
483313570Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
484313570Smm		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
485313570Smm
486313570Smm	/* By default we want both access and default ACLs */
487313570Smm	if (want_type == 0)
488313570Smm		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
489313570Smm
490313570Smm	return (want_type);
491313570Smm}
492313570Smm
493313570Smm/*
494313570Smm * Calculate ACL text string length
495313570Smm */
496313570Smmstatic ssize_t
497313570Smmarchive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
498313570Smm    int wide, struct archive *a, struct archive_string_conv *sc) {
499313570Smm	struct archive_acl_entry *ap;
500313570Smm	const char *name;
501313570Smm	const wchar_t *wname;
502313570Smm	int count, idlen, tmp, r;
503313570Smm	ssize_t length;
504313570Smm	size_t len;
505313570Smm
506231200Smm	count = 0;
507231200Smm	length = 0;
508313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
509313570Smm		if ((ap->type & want_type) == 0)
510313570Smm			continue;
511313570Smm		/*
512313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
513313570Smm		 * ap->mode so they should not be in the list
514313570Smm		 */
515313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
516313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
517313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
518313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
519313570Smm			continue;
520313570Smm		count++;
521313570Smm		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
522313570Smm		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
523313570Smm			length += 8; /* "default:" */
524313570Smm		switch (ap->tag) {
525313570Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
526313570Smm			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
527313570Smm				length += 6; /* "owner@" */
528313570Smm				break;
529313570Smm			}
530313570Smm			/* FALLTHROUGH */
531313570Smm		case ARCHIVE_ENTRY_ACL_USER:
532313570Smm		case ARCHIVE_ENTRY_ACL_MASK:
533313570Smm			length += 4; /* "user", "mask" */
534313570Smm			break;
535313570Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
536313570Smm			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
537313570Smm				length += 6; /* "group@" */
538313570Smm				break;
539313570Smm			}
540313570Smm			/* FALLTHROUGH */
541313570Smm		case ARCHIVE_ENTRY_ACL_GROUP:
542313570Smm		case ARCHIVE_ENTRY_ACL_OTHER:
543313570Smm			length += 5; /* "group", "other" */
544313570Smm			break;
545313570Smm		case ARCHIVE_ENTRY_ACL_EVERYONE:
546313570Smm			length += 9; /* "everyone@" */
547313570Smm			break;
548313570Smm		}
549313570Smm		length += 1; /* colon after tag */
550313570Smm		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
551313570Smm		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
552313570Smm			if (wide) {
553313570Smm				r = archive_mstring_get_wcs(a, &ap->name,
554313570Smm				    &wname);
555313570Smm				if (r == 0 && wname != NULL)
556313570Smm					length += wcslen(wname);
557313570Smm				else if (r < 0 && errno == ENOMEM)
558313570Smm					return (0);
559313570Smm				else
560313570Smm					length += sizeof(uid_t) * 3 + 1;
561313570Smm			} else {
562313570Smm				r = archive_mstring_get_mbs_l(&ap->name, &name,
563313570Smm				    &len, sc);
564313570Smm				if (r != 0)
565313570Smm					return (0);
566313570Smm				if (len > 0 && name != NULL)
567313570Smm					length += len;
568313570Smm				else
569313570Smm					length += sizeof(uid_t) * 3 + 1;
570313570Smm			}
571313570Smm			length += 1; /* colon after user or group name */
572313570Smm		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
573313570Smm			length += 1; /* 2nd colon empty user,group or other */
574313570Smm
575313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
576313570Smm		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
577313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
578313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
579313570Smm			/* Solaris has no colon after other: and mask: */
580313570Smm			length = length - 1;
581313570Smm		}
582313570Smm
583313570Smm		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
584313570Smm			/* rwxpdDaARWcCos:fdinSFI:deny */
585313570Smm			length += 27;
586313570Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
587313570Smm				length += 1; /* allow, alarm, audit */
588313570Smm		} else
589231200Smm			length += 3; /* rwx */
590313570Smm
591313570Smm		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
592313570Smm		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
593313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
594231200Smm			length += 1; /* colon */
595313570Smm			/* ID digit count */
596313570Smm			idlen = 1;
597313570Smm			tmp = ap->id;
598313570Smm			while (tmp > 9) {
599313570Smm				tmp = tmp / 10;
600313570Smm				idlen++;
601313570Smm			}
602313570Smm			length += idlen;
603231200Smm		}
604313570Smm		length ++; /* entry separator */
605231200Smm	}
606231200Smm
607313570Smm	/* Add filemode-mapping access entries to the length */
608313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
609313570Smm		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
610313570Smm			/* "user::rwx\ngroup::rwx\nother:rwx\n" */
611313570Smm			length += 31;
612313570Smm		} else {
613313570Smm			/* "user::rwx\ngroup::rwx\nother::rwx\n" */
614313570Smm			length += 32;
615313570Smm		}
616313570Smm	} else if (count == 0)
617313570Smm		return (0);
618231200Smm
619313570Smm	/* The terminating character is included in count */
620313570Smm	return (length);
621313570Smm}
622313570Smm
623313570Smm/*
624313570Smm * Generate a wide text version of the ACL. The flags parameter controls
625313570Smm * the type and style of the generated ACL.
626313570Smm */
627313570Smmwchar_t *
628313570Smmarchive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
629313570Smm    struct archive *a)
630313570Smm{
631313570Smm	int count;
632313570Smm	ssize_t length;
633313570Smm	size_t len;
634313570Smm	const wchar_t *wname;
635313570Smm	const wchar_t *prefix;
636313570Smm	wchar_t separator;
637313570Smm	struct archive_acl_entry *ap;
638313570Smm	int id, r, want_type;
639313570Smm	wchar_t *wp, *ws;
640313570Smm
641313570Smm	want_type = archive_acl_text_want_type(acl, flags);
642313570Smm
643313570Smm	/* Both NFSv4 and POSIX.1 types found */
644313570Smm	if (want_type == 0)
645231200Smm		return (NULL);
646231200Smm
647313570Smm	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
648313570Smm		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
649313570Smm
650313570Smm	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
651313570Smm
652313570Smm	if (length == 0)
653313570Smm		return (NULL);
654313570Smm
655313570Smm	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
656313570Smm		separator = L',';
657313570Smm	else
658313570Smm		separator = L'\n';
659313570Smm
660231200Smm	/* Now, allocate the string and actually populate it. */
661313570Smm	wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
662313570Smm	if (wp == NULL) {
663313570Smm		if (errno == ENOMEM)
664313570Smm			__archive_errx(1, "No memory");
665238856Smm		return (NULL);
666313570Smm	}
667231200Smm	count = 0;
668313570Smm
669313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
670313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
671313570Smm		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
672231200Smm		    acl->mode & 0700, -1);
673313570Smm		*wp++ = separator;
674313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
675313570Smm		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
676231200Smm		    acl->mode & 0070, -1);
677313570Smm		*wp++ = separator;
678313570Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
679313570Smm		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
680231200Smm		    acl->mode & 0007, -1);
681231200Smm		count += 3;
682231200Smm	}
683231200Smm
684313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
685313570Smm		if ((ap->type & want_type) == 0)
686313570Smm			continue;
687313570Smm		/*
688313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
689313570Smm		 * ap->mode so they should not be in the list
690313570Smm		 */
691313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
692313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
693313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
694313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
695313570Smm			continue;
696313570Smm		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
697313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
698231200Smm			prefix = L"default:";
699231200Smm		else
700231200Smm			prefix = NULL;
701313570Smm		r = archive_mstring_get_wcs(a, &ap->name, &wname);
702313570Smm		if (r == 0) {
703313570Smm			if (count > 0)
704313570Smm				*wp++ = separator;
705313570Smm			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
706313570Smm				id = ap->id;
707313570Smm			else
708313570Smm				id = -1;
709313570Smm			append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
710313570Smm			    wname, ap->permset, id);
711313570Smm			count++;
712313570Smm		} else if (r < 0 && errno == ENOMEM)
713313570Smm			return (NULL);
714231200Smm	}
715231200Smm
716313570Smm	/* Add terminating character */
717313570Smm	*wp++ = L'\0';
718313570Smm
719313570Smm	len = wcslen(ws);
720313570Smm
721313570Smm	if ((ssize_t)len > (length - 1))
722313570Smm		__archive_errx(1, "Buffer overrun");
723313570Smm
724313570Smm	if (text_len != NULL)
725313570Smm		*text_len = len;
726313570Smm
727313570Smm	return (ws);
728231200Smm}
729231200Smm
730231200Smmstatic void
731231200Smmappend_id_w(wchar_t **wp, int id)
732231200Smm{
733231200Smm	if (id < 0)
734231200Smm		id = 0;
735231200Smm	if (id > 9)
736231200Smm		append_id_w(wp, id / 10);
737231200Smm	*(*wp)++ = L"0123456789"[id % 10];
738231200Smm}
739231200Smm
740231200Smmstatic void
741313570Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
742313570Smm    int tag, int flags, const wchar_t *wname, int perm, int id)
743231200Smm{
744231200Smm	if (prefix != NULL) {
745231200Smm		wcscpy(*wp, prefix);
746231200Smm		*wp += wcslen(*wp);
747231200Smm	}
748231200Smm	switch (tag) {
749231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
750231200Smm		wname = NULL;
751231200Smm		id = -1;
752313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
753313570Smm			wcscpy(*wp, L"owner@");
754313570Smm			break;
755313570Smm		}
756231200Smm		/* FALLTHROUGH */
757231200Smm	case ARCHIVE_ENTRY_ACL_USER:
758231200Smm		wcscpy(*wp, L"user");
759231200Smm		break;
760231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
761231200Smm		wname = NULL;
762231200Smm		id = -1;
763313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
764313570Smm			wcscpy(*wp, L"group@");
765313570Smm			break;
766313570Smm		}
767231200Smm		/* FALLTHROUGH */
768231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
769231200Smm		wcscpy(*wp, L"group");
770231200Smm		break;
771231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
772231200Smm		wcscpy(*wp, L"mask");
773231200Smm		wname = NULL;
774231200Smm		id = -1;
775231200Smm		break;
776231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
777231200Smm		wcscpy(*wp, L"other");
778231200Smm		wname = NULL;
779231200Smm		id = -1;
780231200Smm		break;
781313570Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
782313570Smm		wcscpy(*wp, L"everyone@");
783313570Smm		wname = NULL;
784313570Smm		id = -1;
785313570Smm		break;
786231200Smm	}
787231200Smm	*wp += wcslen(*wp);
788231200Smm	*(*wp)++ = L':';
789313570Smm	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
790313570Smm	    tag == ARCHIVE_ENTRY_ACL_USER ||
791313570Smm	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
792313570Smm		if (wname != NULL) {
793313570Smm			wcscpy(*wp, wname);
794313570Smm			*wp += wcslen(*wp);
795313570Smm		} else if (tag == ARCHIVE_ENTRY_ACL_USER
796313570Smm		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
797313570Smm			append_id_w(wp, id);
798313570Smm			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
799313570Smm				id = -1;
800313570Smm		}
801313570Smm		/* Solaris style has no second colon after other and mask */
802313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
803313570Smm		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
804313570Smm		    && tag != ARCHIVE_ENTRY_ACL_MASK))
805313570Smm			*(*wp)++ = L':';
806313570Smm	}
807313570Smm	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
808313570Smm		/* POSIX.1e ACL perms */
809313570Smm		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
810313570Smm		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
811313570Smm		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
812313570Smm	} else {
813313570Smm		/* NFS4 ACL perms */
814313570Smm		*(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA |
815313570Smm		    ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? L'r' : L'-';
816313570Smm		*(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA |
817313570Smm		    ARCHIVE_ENTRY_ACL_ADD_FILE)) ? L'w' : L'-';
818313570Smm		*(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_EXECUTE) ? L'x' : L'-';
819313570Smm		*(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA |
820313570Smm		    ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? L'p' : L'-';
821313570Smm		*(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? L'd' : L'-';
822313570Smm		*(*wp)++ = (perm &
823313570Smm		    ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? L'D' : L'-';
824313570Smm		*(*wp)++ = (perm &
825313570Smm		    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? L'a' : L'-';
826313570Smm		*(*wp)++ = (perm &
827313570Smm		    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? L'A' : L'-';
828313570Smm		*(*wp)++ = (perm &
829313570Smm		    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? L'R' : L'-';
830313570Smm		*(*wp)++ = (perm &
831313570Smm		    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? L'W' : L'-';
832313570Smm		*(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ACL) ? L'c' : L'-';
833313570Smm		*(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ACL) ? L'C' : L'-';
834313570Smm		*(*wp)++ = (perm &
835313570Smm		    ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? L'o' : L'-';
836313570Smm		*(*wp)++ = (perm &
837313570Smm		    ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? L's' : L'-';
838313570Smm		*(*wp)++ = L':';
839313570Smm		*(*wp)++ = (perm &
840313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? L'f' : L'-';
841313570Smm		*(*wp)++ = (perm &
842313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? L'd' : L'-';
843313570Smm		*(*wp)++ = (perm &
844313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? L'i' : L'-';
845313570Smm		*(*wp)++ = (perm &
846313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? L'n' : L'-';
847313570Smm		*(*wp)++ = (perm &
848313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? L'S' : L'-';
849313570Smm		*(*wp)++ = (perm &
850313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? L'F' : L'-';
851313570Smm		*(*wp)++ = (perm &
852313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? L'I' : L'-';
853313570Smm		*(*wp)++ = L':';
854313570Smm		switch (type) {
855313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
856313570Smm			wcscpy(*wp, L"allow");
857313570Smm			break;
858313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
859313570Smm			wcscpy(*wp, L"deny");
860313570Smm			break;
861313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
862313570Smm			wcscpy(*wp, L"audit");
863313570Smm			break;
864313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
865313570Smm			wcscpy(*wp, L"alarm");
866313570Smm			break;
867313570Smm		default:
868313570Smm			break;
869313570Smm		}
870231200Smm		*wp += wcslen(*wp);
871231200Smm	}
872231200Smm	if (id != -1) {
873231200Smm		*(*wp)++ = L':';
874231200Smm		append_id_w(wp, id);
875231200Smm	}
876231200Smm}
877231200Smm
878313570Smm/*
879313570Smm * Generate a text version of the ACL. The flags parameter controls
880313570Smm * the type and style of the generated ACL.
881313570Smm */
882313570Smmchar *
883313570Smmarchive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
884231200Smm    struct archive_string_conv *sc)
885231200Smm{
886231200Smm	int count;
887313570Smm	ssize_t length;
888313570Smm	size_t len;
889231200Smm	const char *name;
890231200Smm	const char *prefix;
891231200Smm	char separator;
892231200Smm	struct archive_acl_entry *ap;
893313570Smm	int id, r, want_type;
894313570Smm	char *p, *s;
895231200Smm
896313570Smm	want_type = archive_acl_text_want_type(acl, flags);
897231200Smm
898313570Smm	/* Both NFSv4 and POSIX.1 types found */
899313570Smm	if (want_type == 0)
900313570Smm		return (NULL);
901231200Smm
902313570Smm	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
903313570Smm		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
904231200Smm
905313570Smm	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
906231200Smm
907313570Smm	if (length == 0)
908313570Smm		return (NULL);
909313570Smm
910313570Smm	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
911313570Smm		separator = ',';
912313570Smm	else
913313570Smm		separator = '\n';
914313570Smm
915231200Smm	/* Now, allocate the string and actually populate it. */
916313570Smm	p = s = (char *)malloc(length * sizeof(char));
917313570Smm	if (p == NULL) {
918313570Smm		if (errno == ENOMEM)
919313570Smm			__archive_errx(1, "No memory");
920313570Smm		return (NULL);
921313570Smm	}
922231200Smm	count = 0;
923313570Smm
924313570Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
925313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
926313570Smm		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
927231200Smm		    acl->mode & 0700, -1);
928313570Smm		*p++ = separator;
929313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
930313570Smm		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
931231200Smm		    acl->mode & 0070, -1);
932313570Smm		*p++ = separator;
933313570Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
934313570Smm		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
935231200Smm		    acl->mode & 0007, -1);
936231200Smm		count += 3;
937231200Smm	}
938231200Smm
939313570Smm	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
940313570Smm		if ((ap->type & want_type) == 0)
941313570Smm			continue;
942313570Smm		/*
943313570Smm		 * Filemode-mapping ACL entries are stored exclusively in
944313570Smm		 * ap->mode so they should not be in the list
945313570Smm		 */
946313570Smm		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
947313570Smm		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
948313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
949313570Smm		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
950313570Smm			continue;
951313570Smm		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
952313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
953231200Smm			prefix = "default:";
954231200Smm		else
955231200Smm			prefix = NULL;
956313570Smm		r = archive_mstring_get_mbs_l(
957313570Smm		    &ap->name, &name, &len, sc);
958313570Smm		if (r != 0)
959313570Smm			return (NULL);
960313570Smm		if (count > 0)
961313570Smm			*p++ = separator;
962313570Smm		if (name == NULL ||
963313570Smm		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
964313570Smm			id = ap->id;
965313570Smm		} else {
966313570Smm			id = -1;
967231200Smm		}
968313570Smm		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
969313570Smm		    ap->permset, id);
970313570Smm		count++;
971231200Smm	}
972231200Smm
973313570Smm	/* Add terminating character */
974313570Smm	*p++ = '\0';
975313570Smm
976313570Smm	len = strlen(s);
977313570Smm
978313570Smm	if ((ssize_t)len > (length - 1))
979313570Smm		__archive_errx(1, "Buffer overrun");
980313570Smm
981313570Smm	if (text_len != NULL)
982313570Smm		*text_len = len;
983313570Smm
984313570Smm	return (s);
985231200Smm}
986231200Smm
987231200Smmstatic void
988231200Smmappend_id(char **p, int id)
989231200Smm{
990231200Smm	if (id < 0)
991231200Smm		id = 0;
992231200Smm	if (id > 9)
993231200Smm		append_id(p, id / 10);
994231200Smm	*(*p)++ = "0123456789"[id % 10];
995231200Smm}
996231200Smm
997231200Smmstatic void
998313570Smmappend_entry(char **p, const char *prefix, int type,
999313570Smm    int tag, int flags, const char *name, int perm, int id)
1000231200Smm{
1001231200Smm	if (prefix != NULL) {
1002231200Smm		strcpy(*p, prefix);
1003231200Smm		*p += strlen(*p);
1004231200Smm	}
1005231200Smm	switch (tag) {
1006231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
1007231200Smm		name = NULL;
1008231200Smm		id = -1;
1009313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1010313570Smm			strcpy(*p, "owner@");
1011313570Smm			break;
1012313570Smm		}
1013231200Smm		/* FALLTHROUGH */
1014231200Smm	case ARCHIVE_ENTRY_ACL_USER:
1015231200Smm		strcpy(*p, "user");
1016231200Smm		break;
1017231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1018231200Smm		name = NULL;
1019231200Smm		id = -1;
1020313570Smm		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1021313570Smm			strcpy(*p, "group@");
1022313570Smm			break;
1023313570Smm		}
1024231200Smm		/* FALLTHROUGH */
1025231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
1026231200Smm		strcpy(*p, "group");
1027231200Smm		break;
1028231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
1029231200Smm		strcpy(*p, "mask");
1030231200Smm		name = NULL;
1031231200Smm		id = -1;
1032231200Smm		break;
1033231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
1034231200Smm		strcpy(*p, "other");
1035231200Smm		name = NULL;
1036231200Smm		id = -1;
1037231200Smm		break;
1038313570Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
1039313570Smm		strcpy(*p, "everyone@");
1040313570Smm		name = NULL;
1041313570Smm		id = -1;
1042313570Smm		break;
1043231200Smm	}
1044231200Smm	*p += strlen(*p);
1045231200Smm	*(*p)++ = ':';
1046313570Smm	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1047313570Smm	    tag == ARCHIVE_ENTRY_ACL_USER ||
1048313570Smm	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1049313570Smm		if (name != NULL) {
1050313570Smm			strcpy(*p, name);
1051313570Smm			*p += strlen(*p);
1052313570Smm		} else if (tag == ARCHIVE_ENTRY_ACL_USER
1053313570Smm		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1054313570Smm			append_id(p, id);
1055313570Smm			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1056313570Smm				id = -1;
1057313570Smm		}
1058313570Smm		/* Solaris style has no second colon after other and mask */
1059313570Smm		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1060313570Smm		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
1061313570Smm		    && tag != ARCHIVE_ENTRY_ACL_MASK))
1062313570Smm			*(*p)++ = ':';
1063313570Smm	}
1064313570Smm	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1065313570Smm		/* POSIX.1e ACL perms */
1066313570Smm		*(*p)++ = (perm & 0444) ? 'r' : '-';
1067313570Smm		*(*p)++ = (perm & 0222) ? 'w' : '-';
1068313570Smm		*(*p)++ = (perm & 0111) ? 'x' : '-';
1069313570Smm	} else {
1070313570Smm		/* NFS4 ACL perms */
1071313570Smm		*(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA |
1072313570Smm		    ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? 'r' : '-';
1073313570Smm		*(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA |
1074313570Smm		    ARCHIVE_ENTRY_ACL_ADD_FILE)) ? 'w' : '-';
1075313570Smm		*(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_EXECUTE)) ? 'x' : '-';
1076313570Smm		*(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA |
1077313570Smm		    ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? 'p' : '-';
1078313570Smm		*(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? 'd' : '-';
1079313570Smm		*(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? 'D' : '-';
1080313570Smm		*(*p)++ = (perm &
1081313570Smm		    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? 'a' : '-';
1082313570Smm		*(*p)++ = (perm &
1083313570Smm		    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? 'A' : '-';
1084313570Smm		*(*p)++ = (perm &
1085313570Smm		    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? 'R' : '-';
1086313570Smm		*(*p)++ = (perm &
1087313570Smm		    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? 'W' : '-';
1088313570Smm		*(*p)++ = (perm &
1089313570Smm		    ARCHIVE_ENTRY_ACL_READ_ACL) ? 'c' : '-';
1090313570Smm		*(*p)++ = (perm &
1091313570Smm		    ARCHIVE_ENTRY_ACL_WRITE_ACL) ? 'C' : '-';
1092313570Smm		*(*p)++ = (perm &
1093313570Smm		    ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? 'o' : '-';
1094313570Smm		*(*p)++ = (perm &
1095313570Smm		    ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? 's' : '-';
1096313570Smm		*(*p)++ = ':';
1097313570Smm		*(*p)++ = (perm &
1098313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? 'f' : '-';
1099313570Smm		*(*p)++ = (perm &
1100313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? 'd' : '-';
1101313570Smm		*(*p)++ = (perm &
1102313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? 'i' : '-';
1103313570Smm		*(*p)++ = (perm &
1104313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? 'n' : '-';
1105313570Smm		*(*p)++ = (perm &
1106313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? 'S' : '-';
1107313570Smm		*(*p)++ = (perm &
1108313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? 'F' : '-';
1109313570Smm		*(*p)++ = (perm &
1110313570Smm		    ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? 'I' : '-';
1111313570Smm		*(*p)++ = ':';
1112313570Smm		switch (type) {
1113313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1114313570Smm			strcpy(*p, "allow");
1115313570Smm			break;
1116313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1117313570Smm			strcpy(*p, "deny");
1118313570Smm			break;
1119313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1120313570Smm			strcpy(*p, "audit");
1121313570Smm			break;
1122313570Smm		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1123313570Smm			strcpy(*p, "alarm");
1124313570Smm			break;
1125313570Smm		}
1126231200Smm		*p += strlen(*p);
1127231200Smm	}
1128231200Smm	if (id != -1) {
1129231200Smm		*(*p)++ = ':';
1130231200Smm		append_id(p, id);
1131231200Smm	}
1132231200Smm}
1133231200Smm
1134231200Smm/*
1135313570Smm * Parse a wide ACL text string.
1136313570Smm *
1137313570Smm * The want_type argument may be one of the following:
1138313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1139313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1140313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1141313570Smm *
1142313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as
1143313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1144231200Smm */
1145231200Smmint
1146313570Smmarchive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1147313570Smm    int want_type)
1148231200Smm{
1149231200Smm	struct {
1150231200Smm		const wchar_t *start;
1151231200Smm		const wchar_t *end;
1152313570Smm	} field[6], name;
1153231200Smm
1154313570Smm	const wchar_t *s, *st;
1155313570Smm
1156313570Smm	int numfields, fields, n, r, sol, ret;
1157313570Smm	int type, types, tag, permset, id;
1158313570Smm	size_t len;
1159231200Smm	wchar_t sep;
1160231200Smm
1161313570Smm	ret = ARCHIVE_OK;
1162313570Smm	types = 0;
1163313570Smm
1164313570Smm	switch (want_type) {
1165313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1166313570Smm		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1167313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1168313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1169313570Smm		numfields = 5;
1170313570Smm		break;
1171313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1172313570Smm		numfields = 6;
1173313570Smm		break;
1174313570Smm	default:
1175313570Smm		return (ARCHIVE_FATAL);
1176313570Smm	}
1177313570Smm
1178313570Smm	while (text != NULL && *text != L'\0') {
1179231200Smm		/*
1180231200Smm		 * Parse the fields out of the next entry,
1181231200Smm		 * advance 'text' to start of next entry.
1182231200Smm		 */
1183231200Smm		fields = 0;
1184231200Smm		do {
1185231200Smm			const wchar_t *start, *end;
1186231200Smm			next_field_w(&text, &start, &end, &sep);
1187313570Smm			if (fields < numfields) {
1188231200Smm				field[fields].start = start;
1189231200Smm				field[fields].end = end;
1190231200Smm			}
1191231200Smm			++fields;
1192231200Smm		} while (sep == L':');
1193231200Smm
1194231200Smm		/* Set remaining fields to blank. */
1195313570Smm		for (n = fields; n < numfields; ++n)
1196231200Smm			field[n].start = field[n].end = NULL;
1197231200Smm
1198313570Smm		if (field[0].start != NULL && *(field[0].start) == L'#') {
1199313570Smm			/* Comment, skip entry */
1200313570Smm			continue;
1201313570Smm		}
1202313570Smm
1203313570Smm		n = 0;
1204313570Smm		sol = 0;
1205231200Smm		id = -1;
1206313570Smm		permset = 0;
1207313570Smm		name.start = name.end = NULL;
1208231200Smm
1209313570Smm		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1210313570Smm			/* POSIX.1e ACLs */
1211313570Smm			/*
1212313570Smm			 * Default keyword "default:user::rwx"
1213313570Smm			 * if found, we have one more field
1214313570Smm			 *
1215313570Smm			 * We also support old Solaris extension:
1216313570Smm			 * "defaultuser::rwx" is the default ACL corresponding
1217313570Smm			 * to "user::rwx", etc. valid only for first field
1218313570Smm			 */
1219313570Smm			s = field[0].start;
1220313570Smm			len = field[0].end - field[0].start;
1221313570Smm			if (*s == L'd' && (len == 1 || (len >= 7
1222313570Smm			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
1223313570Smm				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1224313570Smm				if (len > 7)
1225313570Smm					field[0].start += 7;
1226313570Smm				else
1227313570Smm					n = 1;
1228313570Smm			} else
1229313570Smm				type = want_type;
1230231200Smm
1231313570Smm			/* Check for a numeric ID in field n+1 or n+3. */
1232313570Smm			isint_w(field[n + 1].start, field[n + 1].end, &id);
1233313570Smm			/* Field n+3 is optional. */
1234313570Smm			if (id == -1 && fields > n+3)
1235313570Smm				isint_w(field[n + 3].start, field[n + 3].end,
1236313570Smm				    &id);
1237313570Smm
1238313570Smm			tag = 0;
1239313570Smm			s = field[n].start;
1240313570Smm			st = field[n].start + 1;
1241313570Smm			len = field[n].end - field[n].start;
1242313570Smm
1243313570Smm			switch (*s) {
1244313570Smm			case L'u':
1245313570Smm				if (len == 1 || (len == 4
1246313570Smm				    && wmemcmp(st, L"ser", 3) == 0))
1247313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1248313570Smm				break;
1249313570Smm			case L'g':
1250313570Smm				if (len == 1 || (len == 5
1251313570Smm				    && wmemcmp(st, L"roup", 4) == 0))
1252313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1253313570Smm				break;
1254313570Smm			case L'o':
1255313570Smm				if (len == 1 || (len == 5
1256313570Smm				    && wmemcmp(st, L"ther", 4) == 0))
1257313570Smm					tag = ARCHIVE_ENTRY_ACL_OTHER;
1258313570Smm				break;
1259313570Smm			case L'm':
1260313570Smm				if (len == 1 || (len == 4
1261313570Smm				    && wmemcmp(st, L"ask", 3) == 0))
1262313570Smm					tag = ARCHIVE_ENTRY_ACL_MASK;
1263313570Smm				break;
1264313570Smm			default:
1265313570Smm					break;
1266313570Smm			}
1267313570Smm
1268313570Smm			switch (tag) {
1269313570Smm			case ARCHIVE_ENTRY_ACL_OTHER:
1270313570Smm			case ARCHIVE_ENTRY_ACL_MASK:
1271313570Smm				if (fields == (n + 2)
1272313570Smm				    && field[n + 1].start < field[n + 1].end
1273313570Smm				    && ismode_w(field[n + 1].start,
1274313570Smm				    field[n + 1].end, &permset)) {
1275313570Smm					/* This is Solaris-style "other:rwx" */
1276313570Smm					sol = 1;
1277313570Smm				} else if (fields == (n + 3) &&
1278313570Smm				    field[n + 1].start < field[n + 1].end) {
1279313570Smm					/* Invalid mask or other field */
1280313570Smm					ret = ARCHIVE_WARN;
1281313570Smm					continue;
1282313570Smm				}
1283313570Smm				break;
1284313570Smm			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1285313570Smm			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1286313570Smm				if (id != -1 ||
1287313570Smm				    field[n + 1].start < field[n + 1].end) {
1288313570Smm					name = field[n + 1];
1289313570Smm					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1290313570Smm						tag = ARCHIVE_ENTRY_ACL_USER;
1291313570Smm					else
1292313570Smm						tag = ARCHIVE_ENTRY_ACL_GROUP;
1293313570Smm				}
1294313570Smm				break;
1295313570Smm			default:
1296313570Smm				/* Invalid tag, skip entry */
1297313570Smm				ret = ARCHIVE_WARN;
1298313570Smm				continue;
1299313570Smm			}
1300313570Smm
1301313570Smm			/*
1302313570Smm			 * Without "default:" we expect mode in field 2
1303313570Smm			 * Exception: Solaris other and mask fields
1304313570Smm			 */
1305313570Smm			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1306313570Smm			    field[n + 2 - sol].end, &permset)) {
1307313570Smm				/* Invalid mode, skip entry */
1308313570Smm				ret = ARCHIVE_WARN;
1309313570Smm				continue;
1310313570Smm			}
1311313570Smm		} else {
1312313570Smm			/* NFS4 ACLs */
1313313570Smm			s = field[0].start;
1314313570Smm			len = field[0].end - field[0].start;
1315313570Smm			tag = 0;
1316313570Smm
1317313570Smm			switch (len) {
1318313570Smm			case 4:
1319313570Smm				if (wmemcmp(s, L"user", 4) == 0)
1320313570Smm					tag = ARCHIVE_ENTRY_ACL_USER;
1321313570Smm				break;
1322313570Smm			case 5:
1323313570Smm				if (wmemcmp(s, L"group", 5) == 0)
1324313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP;
1325313570Smm				break;
1326313570Smm			case 6:
1327313570Smm				if (wmemcmp(s, L"owner@", 6) == 0)
1328313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1329313570Smm				else if (wmemcmp(s, L"group@", len) == 0)
1330313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1331313570Smm				break;
1332313570Smm			case 9:
1333313570Smm				if (wmemcmp(s, L"everyone@", 9) == 0)
1334313570Smm					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1335313570Smm			default:
1336313570Smm				break;
1337313570Smm			}
1338313570Smm
1339313570Smm			if (tag == 0) {
1340313570Smm				/* Invalid tag, skip entry */
1341313570Smm				ret = ARCHIVE_WARN;
1342313570Smm				continue;
1343313570Smm			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1344313570Smm			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1345313570Smm				n = 1;
1346231200Smm				name = field[1];
1347313570Smm				isint_w(name.start, name.end, &id);
1348231200Smm			} else
1349313570Smm				n = 0;
1350231200Smm
1351313570Smm			if (!is_nfs4_perms_w(field[1 + n].start,
1352313570Smm			    field[1 + n].end, &permset)) {
1353313570Smm				/* Invalid NFSv4 perms, skip entry */
1354313570Smm				ret = ARCHIVE_WARN;
1355313570Smm				continue;
1356313570Smm			}
1357313570Smm			if (!is_nfs4_flags_w(field[2 + n].start,
1358313570Smm			    field[2 + n].end, &permset)) {
1359313570Smm				/* Invalid NFSv4 flags, skip entry */
1360313570Smm				ret = ARCHIVE_WARN;
1361313570Smm				continue;
1362313570Smm			}
1363313570Smm			s = field[3 + n].start;
1364313570Smm			len = field[3 + n].end - field[3 + n].start;
1365313570Smm			type = 0;
1366313570Smm			if (len == 4) {
1367313570Smm				if (wmemcmp(s, L"deny", 4) == 0)
1368313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1369313570Smm			} else if (len == 5) {
1370313570Smm				if (wmemcmp(s, L"allow", 5) == 0)
1371313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1372313570Smm				else if (wmemcmp(s, L"audit", 5) == 0)
1373313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1374313570Smm				else if (wmemcmp(s, L"alarm", 5) == 0)
1375313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1376313570Smm			}
1377313570Smm			if (type == 0) {
1378313570Smm				/* Invalid entry type, skip entry */
1379313570Smm				ret = ARCHIVE_WARN;
1380313570Smm				continue;
1381313570Smm			}
1382313570Smm			isint_w(field[4 + n].start, field[4 + n].end, &id);
1383313570Smm		}
1384313570Smm
1385231200Smm		/* Add entry to the internal list. */
1386313570Smm		r = archive_acl_add_entry_w_len(acl, type, permset,
1387231200Smm		    tag, id, name.start, name.end - name.start);
1388313570Smm		if (r < ARCHIVE_WARN)
1389313570Smm			return (r);
1390313570Smm		if (r != ARCHIVE_OK)
1391313570Smm			ret = ARCHIVE_WARN;
1392313570Smm		types |= type;
1393231200Smm	}
1394313570Smm
1395313570Smm	/* Reset ACL */
1396313570Smm	archive_acl_reset(acl, types);
1397313570Smm
1398313570Smm	return (ret);
1399231200Smm}
1400231200Smm
1401231200Smm/*
1402231200Smm * Parse a string to a positive decimal integer.  Returns true if
1403231200Smm * the string is non-empty and consists only of decimal digits,
1404231200Smm * false otherwise.
1405231200Smm */
1406231200Smmstatic int
1407231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result)
1408231200Smm{
1409231200Smm	int n = 0;
1410231200Smm	if (start >= end)
1411231200Smm		return (0);
1412231200Smm	while (start < end) {
1413231200Smm		if (*start < '0' || *start > '9')
1414231200Smm			return (0);
1415231200Smm		if (n > (INT_MAX / 10) ||
1416231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1417231200Smm			n = INT_MAX;
1418231200Smm		} else {
1419231200Smm			n *= 10;
1420231200Smm			n += *start - '0';
1421231200Smm		}
1422231200Smm		start++;
1423231200Smm	}
1424231200Smm	*result = n;
1425231200Smm	return (1);
1426231200Smm}
1427231200Smm
1428231200Smm/*
1429231200Smm * Parse a string as a mode field.  Returns true if
1430231200Smm * the string is non-empty and consists only of mode characters,
1431231200Smm * false otherwise.
1432231200Smm */
1433231200Smmstatic int
1434231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1435231200Smm{
1436231200Smm	const wchar_t *p;
1437231200Smm
1438231200Smm	if (start >= end)
1439231200Smm		return (0);
1440231200Smm	p = start;
1441231200Smm	*permset = 0;
1442231200Smm	while (p < end) {
1443231200Smm		switch (*p++) {
1444313570Smm		case L'r': case L'R':
1445231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1446231200Smm			break;
1447313570Smm		case L'w': case L'W':
1448231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1449231200Smm			break;
1450313570Smm		case L'x': case L'X':
1451231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1452231200Smm			break;
1453313570Smm		case L'-':
1454231200Smm			break;
1455231200Smm		default:
1456231200Smm			return (0);
1457231200Smm		}
1458231200Smm	}
1459231200Smm	return (1);
1460231200Smm}
1461231200Smm
1462231200Smm/*
1463313570Smm * Parse a string as a NFS4 ACL permission field.
1464313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1465313570Smm * permission characters, false otherwise
1466313570Smm */
1467313570Smmstatic int
1468313570Smmis_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1469313570Smm{
1470313570Smm	const wchar_t *p;
1471313570Smm
1472313570Smm	if (start >= end)
1473313570Smm		return (0);
1474313570Smm	p = start;
1475313570Smm	while (p < end) {
1476313570Smm		switch (*p++) {
1477313570Smm		case L'r':
1478313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1479313570Smm			break;
1480313570Smm		case L'w':
1481313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1482313570Smm			break;
1483313570Smm		case L'x':
1484313570Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1485313570Smm			break;
1486313570Smm		case L'p':
1487313570Smm			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1488313570Smm			break;
1489313570Smm		case L'D':
1490313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1491313570Smm			break;
1492313570Smm		case L'd':
1493313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1494313570Smm			break;
1495313570Smm		case L'a':
1496313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1497313570Smm			break;
1498313570Smm		case L'A':
1499313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1500313570Smm			break;
1501313570Smm		case L'R':
1502313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1503313570Smm			break;
1504313570Smm		case L'W':
1505313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1506313570Smm			break;
1507313570Smm		case L'c':
1508313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1509313570Smm			break;
1510313570Smm		case L'C':
1511313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1512313570Smm			break;
1513313570Smm		case L'o':
1514313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1515313570Smm			break;
1516313570Smm		case L's':
1517313570Smm			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1518313570Smm			break;
1519313570Smm		case L'-':
1520313570Smm			break;
1521313570Smm		default:
1522313570Smm			return(0);
1523313570Smm		}
1524313570Smm	}
1525313570Smm	return (1);
1526313570Smm}
1527313570Smm
1528313570Smm/*
1529313570Smm * Parse a string as a NFS4 ACL flags field.
1530313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1531313570Smm * flag characters, false otherwise
1532313570Smm */
1533313570Smmstatic int
1534313570Smmis_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1535313570Smm{
1536313570Smm	const wchar_t *p;
1537313570Smm
1538313570Smm	if (start >= end)
1539313570Smm		return (0);
1540313570Smm	p = start;
1541313570Smm	while (p < end) {
1542313570Smm		switch(*p++) {
1543313570Smm		case L'f':
1544313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1545313570Smm			break;
1546313570Smm		case L'd':
1547313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1548313570Smm			break;
1549313570Smm		case L'i':
1550313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1551313570Smm			break;
1552313570Smm		case L'n':
1553313570Smm			*permset |=
1554313570Smm			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1555313570Smm			break;
1556313570Smm		case L'S':
1557313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1558313570Smm			break;
1559313570Smm		case L'F':
1560313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1561313570Smm			break;
1562313570Smm		case L'I':
1563313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1564313570Smm			break;
1565313570Smm		case L'-':
1566313570Smm			break;
1567313570Smm		default:
1568313570Smm			return (0);
1569313570Smm		}
1570313570Smm	}
1571313570Smm	return (1);
1572313570Smm}
1573313570Smm
1574313570Smm/*
1575231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1576231200Smm * to point to just after the separator.  *start points to the first
1577231200Smm * character of the matched text and *end just after the last
1578231200Smm * character of the matched identifier.  In particular *end - *start
1579231200Smm * is the length of the field body, not including leading or trailing
1580231200Smm * whitespace.
1581231200Smm */
1582231200Smmstatic void
1583231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start,
1584231200Smm    const wchar_t **end, wchar_t *sep)
1585231200Smm{
1586231200Smm	/* Skip leading whitespace to find start of field. */
1587231200Smm	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1588231200Smm		(*wp)++;
1589231200Smm	}
1590231200Smm	*start = *wp;
1591231200Smm
1592231200Smm	/* Scan for the separator. */
1593231200Smm	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1594231200Smm	    **wp != L'\n') {
1595231200Smm		(*wp)++;
1596231200Smm	}
1597231200Smm	*sep = **wp;
1598231200Smm
1599231200Smm	/* Trim trailing whitespace to locate end of field. */
1600231200Smm	*end = *wp - 1;
1601231200Smm	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1602231200Smm		(*end)--;
1603231200Smm	}
1604231200Smm	(*end)++;
1605231200Smm
1606231200Smm	/* Adjust scanner location. */
1607231200Smm	if (**wp != L'\0')
1608231200Smm		(*wp)++;
1609231200Smm}
1610231200Smm
1611231200Smm/*
1612313570Smm * Parse an ACL text string.
1613313570Smm *
1614313570Smm * The want_type argument may be one of the following:
1615313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1616313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1617313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1618313570Smm *
1619313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as
1620313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1621231200Smm */
1622231200Smmint
1623313570Smmarchive_acl_from_text_l(struct archive_acl *acl, const char *text,
1624313570Smm    int want_type, struct archive_string_conv *sc)
1625231200Smm{
1626231200Smm	struct {
1627231200Smm		const char *start;
1628231200Smm		const char *end;
1629313570Smm	} field[6], name;
1630231200Smm
1631313570Smm	const char *s, *st;
1632313570Smm	int numfields, fields, n, r, sol, ret;
1633313570Smm	int type, types, tag, permset, id;
1634313570Smm	size_t len;
1635231200Smm	char sep;
1636231200Smm
1637313570Smm	switch (want_type) {
1638313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1639313570Smm		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1640313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1641313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1642313570Smm		numfields = 5;
1643313570Smm		break;
1644313570Smm	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1645313570Smm		numfields = 6;
1646313570Smm		break;
1647313570Smm	default:
1648313570Smm		return (ARCHIVE_FATAL);
1649313570Smm	}
1650313570Smm
1651313570Smm	ret = ARCHIVE_OK;
1652313570Smm	types = 0;
1653313570Smm
1654231200Smm	while (text != NULL  &&  *text != '\0') {
1655231200Smm		/*
1656231200Smm		 * Parse the fields out of the next entry,
1657231200Smm		 * advance 'text' to start of next entry.
1658231200Smm		 */
1659231200Smm		fields = 0;
1660231200Smm		do {
1661231200Smm			const char *start, *end;
1662231200Smm			next_field(&text, &start, &end, &sep);
1663313570Smm			if (fields < numfields) {
1664231200Smm				field[fields].start = start;
1665231200Smm				field[fields].end = end;
1666231200Smm			}
1667231200Smm			++fields;
1668231200Smm		} while (sep == ':');
1669231200Smm
1670231200Smm		/* Set remaining fields to blank. */
1671313570Smm		for (n = fields; n < numfields; ++n)
1672231200Smm			field[n].start = field[n].end = NULL;
1673231200Smm
1674313570Smm		if (field[0].start != NULL && *(field[0].start) == '#') {
1675313570Smm			/* Comment, skip entry */
1676313570Smm			continue;
1677313570Smm		}
1678313570Smm
1679313570Smm		n = 0;
1680313570Smm		sol = 0;
1681231200Smm		id = -1;
1682313570Smm		permset = 0;
1683313570Smm		name.start = name.end = NULL;
1684231200Smm
1685313570Smm		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1686313570Smm			/* POSIX.1e ACLs */
1687313570Smm			/*
1688313570Smm			 * Default keyword "default:user::rwx"
1689313570Smm			 * if found, we have one more field
1690313570Smm			 *
1691313570Smm			 * We also support old Solaris extension:
1692313570Smm			 * "defaultuser::rwx" is the default ACL corresponding
1693313570Smm			 * to "user::rwx", etc. valid only for first field
1694313570Smm			 */
1695313570Smm			s = field[0].start;
1696313570Smm			len = field[0].end - field[0].start;
1697313570Smm			if (*s == 'd' && (len == 1 || (len >= 7
1698313570Smm			    && memcmp((s + 1), "efault", 6) == 0))) {
1699313570Smm				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1700313570Smm				if (len > 7)
1701313570Smm					field[0].start += 7;
1702313570Smm				else
1703313570Smm					n = 1;
1704313570Smm			} else
1705313570Smm				type = want_type;
1706231200Smm
1707313570Smm			/* Check for a numeric ID in field n+1 or n+3. */
1708313570Smm			isint(field[n + 1].start, field[n + 1].end, &id);
1709313570Smm			/* Field n+3 is optional. */
1710313570Smm			if (id == -1 && fields > (n + 3))
1711313570Smm				isint(field[n + 3].start, field[n + 3].end,
1712313570Smm				    &id);
1713313570Smm
1714313570Smm			tag = 0;
1715313570Smm			s = field[n].start;
1716313570Smm			st = field[n].start + 1;
1717313570Smm			len = field[n].end - field[n].start;
1718313570Smm
1719313570Smm			switch (*s) {
1720313570Smm			case 'u':
1721313570Smm				if (len == 1 || (len == 4
1722313570Smm				    && memcmp(st, "ser", 3) == 0))
1723313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1724313570Smm				break;
1725313570Smm			case 'g':
1726313570Smm				if (len == 1 || (len == 5
1727313570Smm				    && memcmp(st, "roup", 4) == 0))
1728313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1729313570Smm				break;
1730313570Smm			case 'o':
1731313570Smm				if (len == 1 || (len == 5
1732313570Smm				    && memcmp(st, "ther", 4) == 0))
1733313570Smm					tag = ARCHIVE_ENTRY_ACL_OTHER;
1734313570Smm				break;
1735313570Smm			case 'm':
1736313570Smm				if (len == 1 || (len == 4
1737313570Smm				    && memcmp(st, "ask", 3) == 0))
1738313570Smm					tag = ARCHIVE_ENTRY_ACL_MASK;
1739313570Smm				break;
1740313570Smm			default:
1741313570Smm					break;
1742313570Smm			}
1743313570Smm
1744313570Smm			switch (tag) {
1745313570Smm			case ARCHIVE_ENTRY_ACL_OTHER:
1746313570Smm			case ARCHIVE_ENTRY_ACL_MASK:
1747313570Smm				if (fields == (n + 2)
1748313570Smm				    && field[n + 1].start < field[n + 1].end
1749313570Smm				    && ismode(field[n + 1].start,
1750313570Smm				    field[n + 1].end, &permset)) {
1751313570Smm					/* This is Solaris-style "other:rwx" */
1752313570Smm					sol = 1;
1753313570Smm				} else if (fields == (n + 3) &&
1754313570Smm				    field[n + 1].start < field[n + 1].end) {
1755313570Smm					/* Invalid mask or other field */
1756313570Smm					ret = ARCHIVE_WARN;
1757313570Smm					continue;
1758313570Smm				}
1759313570Smm				break;
1760313570Smm			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1761313570Smm			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1762313570Smm				if (id != -1 ||
1763313570Smm				    field[n + 1].start < field[n + 1].end) {
1764313570Smm					name = field[n + 1];
1765313570Smm					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1766313570Smm						tag = ARCHIVE_ENTRY_ACL_USER;
1767313570Smm					else
1768313570Smm						tag = ARCHIVE_ENTRY_ACL_GROUP;
1769313570Smm				}
1770313570Smm				break;
1771313570Smm			default:
1772313570Smm				/* Invalid tag, skip entry */
1773313570Smm				ret = ARCHIVE_WARN;
1774313570Smm				continue;
1775313570Smm			}
1776313570Smm
1777313570Smm			/*
1778313570Smm			 * Without "default:" we expect mode in field 3
1779313570Smm			 * Exception: Solaris other and mask fields
1780313570Smm			 */
1781313570Smm			if (permset == 0 && !ismode(field[n + 2 - sol].start,
1782313570Smm			    field[n + 2 - sol].end, &permset)) {
1783313570Smm				/* Invalid mode, skip entry */
1784313570Smm				ret = ARCHIVE_WARN;
1785313570Smm				continue;
1786313570Smm			}
1787313570Smm		} else {
1788313570Smm			/* NFS4 ACLs */
1789313570Smm			s = field[0].start;
1790313570Smm			len = field[0].end - field[0].start;
1791313570Smm			tag = 0;
1792313570Smm
1793313570Smm			switch (len) {
1794313570Smm			case 4:
1795313570Smm				if (memcmp(s, "user", 4) == 0)
1796313570Smm					tag = ARCHIVE_ENTRY_ACL_USER;
1797313570Smm				break;
1798313570Smm			case 5:
1799313570Smm				if (memcmp(s, "group", 5) == 0)
1800313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP;
1801313570Smm				break;
1802313570Smm			case 6:
1803313570Smm				if (memcmp(s, "owner@", 6) == 0)
1804313570Smm					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1805313570Smm				else if (memcmp(s, "group@", 6) == 0)
1806313570Smm					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1807313570Smm				break;
1808313570Smm			case 9:
1809313570Smm				if (memcmp(s, "everyone@", 9) == 0)
1810313570Smm					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1811313570Smm				break;
1812313570Smm			default:
1813313570Smm				break;
1814313570Smm			}
1815313570Smm
1816313570Smm			if (tag == 0) {
1817313570Smm				/* Invalid tag, skip entry */
1818313570Smm				ret = ARCHIVE_WARN;
1819313570Smm				continue;
1820313570Smm			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1821313570Smm			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1822313570Smm				n = 1;
1823231200Smm				name = field[1];
1824313570Smm				isint(name.start, name.end, &id);
1825231200Smm			} else
1826313570Smm				n = 0;
1827231200Smm
1828313570Smm			if (!is_nfs4_perms(field[1 + n].start,
1829313570Smm			    field[1 + n].end, &permset)) {
1830313570Smm				/* Invalid NFSv4 perms, skip entry */
1831313570Smm				ret = ARCHIVE_WARN;
1832313570Smm				continue;
1833313570Smm			}
1834313570Smm			if (!is_nfs4_flags(field[2 + n].start,
1835313570Smm			    field[2 + n].end, &permset)) {
1836313570Smm				/* Invalid NFSv4 flags, skip entry */
1837313570Smm				ret = ARCHIVE_WARN;
1838313570Smm				continue;
1839313570Smm			}
1840313570Smm			s = field[3 + n].start;
1841313570Smm			len = field[3 + n].end - field[3 + n].start;
1842313570Smm			type = 0;
1843313570Smm			if (len == 4) {
1844313570Smm				if (memcmp(s, "deny", 4) == 0)
1845313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1846313570Smm			} else if (len == 5) {
1847313570Smm				if (memcmp(s, "allow", 5) == 0)
1848313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1849313570Smm				else if (memcmp(s, "audit", 5) == 0)
1850313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1851313570Smm				else if (memcmp(s, "alarm", 5) == 0)
1852313570Smm					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1853313570Smm			}
1854313570Smm			if (type == 0) {
1855313570Smm				/* Invalid entry type, skip entry */
1856313570Smm				ret = ARCHIVE_WARN;
1857313570Smm				continue;
1858313570Smm			}
1859313570Smm			isint(field[4 + n].start, field[4 + n].end,
1860313570Smm			    &id);
1861313570Smm		}
1862313570Smm
1863231200Smm		/* Add entry to the internal list. */
1864231200Smm		r = archive_acl_add_entry_len_l(acl, type, permset,
1865231200Smm		    tag, id, name.start, name.end - name.start, sc);
1866231200Smm		if (r < ARCHIVE_WARN)
1867231200Smm			return (r);
1868231200Smm		if (r != ARCHIVE_OK)
1869231200Smm			ret = ARCHIVE_WARN;
1870313570Smm		types |= type;
1871231200Smm	}
1872313570Smm
1873313570Smm	/* Reset ACL */
1874313570Smm	archive_acl_reset(acl, types);
1875313570Smm
1876231200Smm	return (ret);
1877231200Smm}
1878231200Smm
1879231200Smm/*
1880231200Smm * Parse a string to a positive decimal integer.  Returns true if
1881231200Smm * the string is non-empty and consists only of decimal digits,
1882231200Smm * false otherwise.
1883231200Smm */
1884231200Smmstatic int
1885231200Smmisint(const char *start, const char *end, int *result)
1886231200Smm{
1887231200Smm	int n = 0;
1888231200Smm	if (start >= end)
1889231200Smm		return (0);
1890231200Smm	while (start < end) {
1891231200Smm		if (*start < '0' || *start > '9')
1892231200Smm			return (0);
1893231200Smm		if (n > (INT_MAX / 10) ||
1894231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1895231200Smm			n = INT_MAX;
1896231200Smm		} else {
1897231200Smm			n *= 10;
1898231200Smm			n += *start - '0';
1899231200Smm		}
1900231200Smm		start++;
1901231200Smm	}
1902231200Smm	*result = n;
1903231200Smm	return (1);
1904231200Smm}
1905231200Smm
1906231200Smm/*
1907231200Smm * Parse a string as a mode field.  Returns true if
1908231200Smm * the string is non-empty and consists only of mode characters,
1909231200Smm * false otherwise.
1910231200Smm */
1911231200Smmstatic int
1912231200Smmismode(const char *start, const char *end, int *permset)
1913231200Smm{
1914231200Smm	const char *p;
1915231200Smm
1916231200Smm	if (start >= end)
1917231200Smm		return (0);
1918231200Smm	p = start;
1919231200Smm	*permset = 0;
1920231200Smm	while (p < end) {
1921231200Smm		switch (*p++) {
1922231200Smm		case 'r': case 'R':
1923231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1924231200Smm			break;
1925231200Smm		case 'w': case 'W':
1926231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1927231200Smm			break;
1928231200Smm		case 'x': case 'X':
1929231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1930231200Smm			break;
1931231200Smm		case '-':
1932231200Smm			break;
1933231200Smm		default:
1934231200Smm			return (0);
1935231200Smm		}
1936231200Smm	}
1937231200Smm	return (1);
1938231200Smm}
1939231200Smm
1940231200Smm/*
1941313570Smm * Parse a string as a NFS4 ACL permission field.
1942313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
1943313570Smm * permission characters, false otherwise
1944313570Smm */
1945313570Smmstatic int
1946313570Smmis_nfs4_perms(const char *start, const char *end, int *permset)
1947313570Smm{
1948313570Smm	const char *p;
1949313570Smm
1950313570Smm	if (start >= end)
1951313570Smm		return (0);
1952313570Smm	p = start;
1953313570Smm	while (p < end) {
1954313570Smm		switch (*p++) {
1955313570Smm		case 'r':
1956313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1957313570Smm			break;
1958313570Smm		case 'w':
1959313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1960313570Smm			break;
1961313570Smm		case 'x':
1962313570Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1963313570Smm			break;
1964313570Smm		case 'p':
1965313570Smm			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1966313570Smm			break;
1967313570Smm		case 'D':
1968313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1969313570Smm			break;
1970313570Smm		case 'd':
1971313570Smm			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1972313570Smm			break;
1973313570Smm		case 'a':
1974313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1975313570Smm			break;
1976313570Smm		case 'A':
1977313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1978313570Smm			break;
1979313570Smm		case 'R':
1980313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1981313570Smm			break;
1982313570Smm		case 'W':
1983313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1984313570Smm			break;
1985313570Smm		case 'c':
1986313570Smm			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1987313570Smm			break;
1988313570Smm		case 'C':
1989313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1990313570Smm			break;
1991313570Smm		case 'o':
1992313570Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1993313570Smm			break;
1994313570Smm		case 's':
1995313570Smm			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1996313570Smm			break;
1997313570Smm		case '-':
1998313570Smm			break;
1999313570Smm		default:
2000313570Smm			return(0);
2001313570Smm		}
2002313570Smm	}
2003313570Smm	return (1);
2004313570Smm}
2005313570Smm
2006313570Smm/*
2007313570Smm * Parse a string as a NFS4 ACL flags field.
2008313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL
2009313570Smm * flag characters, false otherwise
2010313570Smm */
2011313570Smmstatic int
2012313570Smmis_nfs4_flags(const char *start, const char *end, int *permset)
2013313570Smm{
2014313570Smm	const char *p;
2015313570Smm
2016313570Smm	if (start >= end)
2017313570Smm		return (0);
2018313570Smm	p = start;
2019313570Smm	while (p < end) {
2020313570Smm		switch(*p++) {
2021313570Smm		case 'f':
2022313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2023313570Smm			break;
2024313570Smm		case 'd':
2025313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2026313570Smm			break;
2027313570Smm		case 'i':
2028313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2029313570Smm			break;
2030313570Smm		case 'n':
2031313570Smm			*permset |=
2032313570Smm			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2033313570Smm			break;
2034313570Smm		case 'S':
2035313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2036313570Smm			break;
2037313570Smm		case 'F':
2038313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2039313570Smm			break;
2040313570Smm		case 'I':
2041313570Smm			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2042313570Smm			break;
2043313570Smm		case '-':
2044313570Smm			break;
2045313570Smm		default:
2046313570Smm			return (0);
2047313570Smm		}
2048313570Smm	}
2049313570Smm	return (1);
2050313570Smm}
2051313570Smm
2052313570Smm/*
2053231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2054231200Smm * to point to just after the separator.  *start points to the first
2055231200Smm * character of the matched text and *end just after the last
2056231200Smm * character of the matched identifier.  In particular *end - *start
2057231200Smm * is the length of the field body, not including leading or trailing
2058231200Smm * whitespace.
2059231200Smm */
2060231200Smmstatic void
2061231200Smmnext_field(const char **p, const char **start,
2062231200Smm    const char **end, char *sep)
2063231200Smm{
2064231200Smm	/* Skip leading whitespace to find start of field. */
2065231200Smm	while (**p == ' ' || **p == '\t' || **p == '\n') {
2066231200Smm		(*p)++;
2067231200Smm	}
2068231200Smm	*start = *p;
2069231200Smm
2070231200Smm	/* Scan for the separator. */
2071231200Smm	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
2072231200Smm		(*p)++;
2073231200Smm	}
2074231200Smm	*sep = **p;
2075231200Smm
2076231200Smm	/* Trim trailing whitespace to locate end of field. */
2077231200Smm	*end = *p - 1;
2078231200Smm	while (**end == ' ' || **end == '\t' || **end == '\n') {
2079231200Smm		(*end)--;
2080231200Smm	}
2081231200Smm	(*end)++;
2082231200Smm
2083231200Smm	/* Adjust scanner location. */
2084231200Smm	if (**p != '\0')
2085231200Smm		(*p)++;
2086231200Smm}
2087