1/*-
2 * Copyright (c) 2003-2010 Tim Kientzle
3 * Copyright (c) 2016 Martin Matuska
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "archive_platform.h"
28__FBSDID("$FreeBSD$");
29
30#ifdef HAVE_ERRNO_H
31#include <errno.h>
32#endif
33#ifdef HAVE_LIMITS_H
34#include <limits.h>
35#endif
36#ifdef HAVE_WCHAR_H
37#include <wchar.h>
38#endif
39
40#include "archive_acl_private.h"
41#include "archive_entry.h"
42#include "archive_private.h"
43
44#undef max
45#define	max(a, b)	((a)>(b)?(a):(b))
46
47#ifndef HAVE_WMEMCMP
48/* Good enough for simple equality testing, but not for sorting. */
49#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
50#endif
51
52static int	acl_special(struct archive_acl *acl,
53		    int type, int permset, int tag);
54static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
55		    int type, int permset, int tag, int id);
56static int	archive_acl_add_entry_len_l(struct archive_acl *acl,
57		    int type, int permset, int tag, int id, const char *name,
58		    size_t len, struct archive_string_conv *sc);
59static int	archive_acl_text_want_type(struct archive_acl *acl, int flags);
60static ssize_t	archive_acl_text_len(struct archive_acl *acl, int want_type,
61		    int flags, int wide, struct archive *a,
62		    struct archive_string_conv *sc);
63static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
64static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
65static int	is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
66		    int *result);
67static int	is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
68		    int *result);
69static void	next_field_w(const wchar_t **wp, const wchar_t **start,
70		    const wchar_t **end, wchar_t *sep);
71static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
72		    int tag, int flags, const wchar_t *wname, int perm, int id);
73static void	append_id_w(wchar_t **wp, int id);
74static int	isint(const char *start, const char *end, int *result);
75static int	ismode(const char *start, const char *end, int *result);
76static int	is_nfs4_flags(const char *start, const char *end,
77		    int *result);
78static int	is_nfs4_perms(const char *start, const char *end,
79		    int *result);
80static void	next_field(const char **p, const char **start,
81		    const char **end, char *sep);
82static void	append_entry(char **p, const char *prefix, int type,
83		    int tag, int flags, const char *name, int perm, int id);
84static void	append_id(char **p, int id);
85
86static const struct {
87	const int perm;
88	const char c;
89	const wchar_t wc;
90} nfsv4_acl_perm_map[] = {
91	{ ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
92	    L'r' },
93	{ ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
94	    L'w' },
95	{ ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
96	{ ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
97	    'p', L'p' },
98	{ ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
99	{ ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
100	{ ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
101	{ ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
102	{ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
103	{ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
104	{ ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
105	{ ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
106	{ ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
107	{ ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
108};
109
110static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
111    sizeof(nfsv4_acl_perm_map[0]));
112
113static const struct {
114	const int perm;
115	const char c;
116	const wchar_t wc;
117} nfsv4_acl_flag_map[] = {
118	{ ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
119	{ ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
120	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
121	{ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
122	{ ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
123	{ ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
124	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
125};
126
127static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
128    sizeof(nfsv4_acl_flag_map[0]));
129
130void
131archive_acl_clear(struct archive_acl *acl)
132{
133	struct archive_acl_entry *ap;
134
135	while (acl->acl_head != NULL) {
136		ap = acl->acl_head->next;
137		archive_mstring_clean(&acl->acl_head->name);
138		free(acl->acl_head);
139		acl->acl_head = ap;
140	}
141	free(acl->acl_text_w);
142	acl->acl_text_w = NULL;
143	free(acl->acl_text);
144	acl->acl_text = NULL;
145	acl->acl_p = NULL;
146	acl->acl_types = 0;
147	acl->acl_state = 0; /* Not counting. */
148}
149
150void
151archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
152{
153	struct archive_acl_entry *ap, *ap2;
154
155	archive_acl_clear(dest);
156
157	dest->mode = src->mode;
158	ap = src->acl_head;
159	while (ap != NULL) {
160		ap2 = acl_new_entry(dest,
161		    ap->type, ap->permset, ap->tag, ap->id);
162		if (ap2 != NULL)
163			archive_mstring_copy(&ap2->name, &ap->name);
164		ap = ap->next;
165	}
166}
167
168int
169archive_acl_add_entry(struct archive_acl *acl,
170    int type, int permset, int tag, int id, const char *name)
171{
172	struct archive_acl_entry *ap;
173
174	if (acl_special(acl, type, permset, tag) == 0)
175		return ARCHIVE_OK;
176	ap = acl_new_entry(acl, type, permset, tag, id);
177	if (ap == NULL) {
178		/* XXX Error XXX */
179		return ARCHIVE_FAILED;
180	}
181	if (name != NULL  &&  *name != '\0')
182		archive_mstring_copy_mbs(&ap->name, name);
183	else
184		archive_mstring_clean(&ap->name);
185	return ARCHIVE_OK;
186}
187
188int
189archive_acl_add_entry_w_len(struct archive_acl *acl,
190    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
191{
192	struct archive_acl_entry *ap;
193
194	if (acl_special(acl, type, permset, tag) == 0)
195		return ARCHIVE_OK;
196	ap = acl_new_entry(acl, type, permset, tag, id);
197	if (ap == NULL) {
198		/* XXX Error XXX */
199		return ARCHIVE_FAILED;
200	}
201	if (name != NULL  &&  *name != L'\0' && len > 0)
202		archive_mstring_copy_wcs_len(&ap->name, name, len);
203	else
204		archive_mstring_clean(&ap->name);
205	return ARCHIVE_OK;
206}
207
208static int
209archive_acl_add_entry_len_l(struct archive_acl *acl,
210    int type, int permset, int tag, int id, const char *name, size_t len,
211    struct archive_string_conv *sc)
212{
213	struct archive_acl_entry *ap;
214	int r;
215
216	if (acl_special(acl, type, permset, tag) == 0)
217		return ARCHIVE_OK;
218	ap = acl_new_entry(acl, type, permset, tag, id);
219	if (ap == NULL) {
220		/* XXX Error XXX */
221		return ARCHIVE_FAILED;
222	}
223	if (name != NULL  &&  *name != '\0' && len > 0) {
224		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
225	} else {
226		r = 0;
227		archive_mstring_clean(&ap->name);
228	}
229	if (r == 0)
230		return (ARCHIVE_OK);
231	else if (errno == ENOMEM)
232		return (ARCHIVE_FATAL);
233	else
234		return (ARCHIVE_WARN);
235}
236
237/*
238 * If this ACL entry is part of the standard POSIX permissions set,
239 * store the permissions in the stat structure and return zero.
240 */
241static int
242acl_special(struct archive_acl *acl, int type, int permset, int tag)
243{
244	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
245	    && ((permset & ~007) == 0)) {
246		switch (tag) {
247		case ARCHIVE_ENTRY_ACL_USER_OBJ:
248			acl->mode &= ~0700;
249			acl->mode |= (permset & 7) << 6;
250			return (0);
251		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
252			acl->mode &= ~0070;
253			acl->mode |= (permset & 7) << 3;
254			return (0);
255		case ARCHIVE_ENTRY_ACL_OTHER:
256			acl->mode &= ~0007;
257			acl->mode |= permset & 7;
258			return (0);
259		}
260	}
261	return (1);
262}
263
264/*
265 * Allocate and populate a new ACL entry with everything but the
266 * name.
267 */
268static struct archive_acl_entry *
269acl_new_entry(struct archive_acl *acl,
270    int type, int permset, int tag, int id)
271{
272	struct archive_acl_entry *ap, *aq;
273
274	/* Type argument must be a valid NFS4 or POSIX.1e type.
275	 * The type must agree with anything already set and
276	 * the permset must be compatible. */
277	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
278		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
279			return (NULL);
280		}
281		if (permset &
282		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
283			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
284			return (NULL);
285		}
286	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
287		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
288			return (NULL);
289		}
290		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
291			return (NULL);
292		}
293	} else {
294		return (NULL);
295	}
296
297	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
298	switch (tag) {
299	case ARCHIVE_ENTRY_ACL_USER:
300	case ARCHIVE_ENTRY_ACL_USER_OBJ:
301	case ARCHIVE_ENTRY_ACL_GROUP:
302	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
303		/* Tags valid in both NFS4 and POSIX.1e */
304		break;
305	case ARCHIVE_ENTRY_ACL_MASK:
306	case ARCHIVE_ENTRY_ACL_OTHER:
307		/* Tags valid only in POSIX.1e. */
308		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
309			return (NULL);
310		}
311		break;
312	case ARCHIVE_ENTRY_ACL_EVERYONE:
313		/* Tags valid only in NFS4. */
314		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
315			return (NULL);
316		}
317		break;
318	default:
319		/* No other values are valid. */
320		return (NULL);
321	}
322
323	free(acl->acl_text_w);
324	acl->acl_text_w = NULL;
325	free(acl->acl_text);
326	acl->acl_text = NULL;
327
328	/*
329	 * If there's a matching entry already in the list, overwrite it.
330	 * NFSv4 entries may be repeated and are not overwritten.
331	 *
332	 * TODO: compare names of no id is provided (needs more rework)
333	 */
334	ap = acl->acl_head;
335	aq = NULL;
336	while (ap != NULL) {
337		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
338		    ap->type == type && ap->tag == tag && ap->id == id) {
339			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
340			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {
341				ap->permset = permset;
342				return (ap);
343			}
344		}
345		aq = ap;
346		ap = ap->next;
347	}
348
349	/* Add a new entry to the end of the list. */
350	ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
351	if (ap == NULL)
352		return (NULL);
353	if (aq == NULL)
354		acl->acl_head = ap;
355	else
356		aq->next = ap;
357	ap->type = type;
358	ap->tag = tag;
359	ap->id = id;
360	ap->permset = permset;
361	acl->acl_types |= type;
362	return (ap);
363}
364
365/*
366 * Return a count of entries matching "want_type".
367 */
368int
369archive_acl_count(struct archive_acl *acl, int want_type)
370{
371	int count;
372	struct archive_acl_entry *ap;
373
374	count = 0;
375	ap = acl->acl_head;
376	while (ap != NULL) {
377		if ((ap->type & want_type) != 0)
378			count++;
379		ap = ap->next;
380	}
381
382	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
383		count += 3;
384	return (count);
385}
386
387/*
388 * Return a bitmask of stored ACL types in an ACL list
389 */
390int
391archive_acl_types(struct archive_acl *acl)
392{
393	return (acl->acl_types);
394}
395
396/*
397 * Prepare for reading entries from the ACL data.  Returns a count
398 * of entries matching "want_type", or zero if there are no
399 * non-extended ACL entries of that type.
400 */
401int
402archive_acl_reset(struct archive_acl *acl, int want_type)
403{
404	int count, cutoff;
405
406	count = archive_acl_count(acl, want_type);
407
408	/*
409	 * If the only entries are the three standard ones,
410	 * then don't return any ACL data.  (In this case,
411	 * client can just use chmod(2) to set permissions.)
412	 */
413	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
414		cutoff = 3;
415	else
416		cutoff = 0;
417
418	if (count > cutoff)
419		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
420	else
421		acl->acl_state = 0;
422	acl->acl_p = acl->acl_head;
423	return (count);
424}
425
426
427/*
428 * Return the next ACL entry in the list.  Fake entries for the
429 * standard permissions and include them in the returned list.
430 */
431int
432archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
433    int *type, int *permset, int *tag, int *id, const char **name)
434{
435	*name = NULL;
436	*id = -1;
437
438	/*
439	 * The acl_state is either zero (no entries available), -1
440	 * (reading from list), or an entry type (retrieve that type
441	 * from ae_stat.aest_mode).
442	 */
443	if (acl->acl_state == 0)
444		return (ARCHIVE_WARN);
445
446	/* The first three access entries are special. */
447	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
448		switch (acl->acl_state) {
449		case ARCHIVE_ENTRY_ACL_USER_OBJ:
450			*permset = (acl->mode >> 6) & 7;
451			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
452			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
453			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
454			return (ARCHIVE_OK);
455		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
456			*permset = (acl->mode >> 3) & 7;
457			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
458			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
459			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
460			return (ARCHIVE_OK);
461		case ARCHIVE_ENTRY_ACL_OTHER:
462			*permset = acl->mode & 7;
463			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
464			*tag = ARCHIVE_ENTRY_ACL_OTHER;
465			acl->acl_state = -1;
466			acl->acl_p = acl->acl_head;
467			return (ARCHIVE_OK);
468		default:
469			break;
470		}
471	}
472
473	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
474		acl->acl_p = acl->acl_p->next;
475	if (acl->acl_p == NULL) {
476		acl->acl_state = 0;
477		*type = 0;
478		*permset = 0;
479		*tag = 0;
480		*id = -1;
481		*name = NULL;
482		return (ARCHIVE_EOF); /* End of ACL entries. */
483	}
484	*type = acl->acl_p->type;
485	*permset = acl->acl_p->permset;
486	*tag = acl->acl_p->tag;
487	*id = acl->acl_p->id;
488	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
489		if (errno == ENOMEM)
490			return (ARCHIVE_FATAL);
491		*name = NULL;
492	}
493	acl->acl_p = acl->acl_p->next;
494	return (ARCHIVE_OK);
495}
496
497/*
498 * Determine what type of ACL do we want
499 */
500static int
501archive_acl_text_want_type(struct archive_acl *acl, int flags)
502{
503	int want_type;
504
505	/* Check if ACL is NFSv4 */
506	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
507		/* NFSv4 should never mix with POSIX.1e */
508		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
509			return (0);
510		else
511			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
512	}
513
514	/* Now deal with POSIX.1e ACLs */
515
516	want_type = 0;
517	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
518		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
519	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
520		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
521
522	/* By default we want both access and default ACLs */
523	if (want_type == 0)
524		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
525
526	return (want_type);
527}
528
529/*
530 * Calculate ACL text string length
531 */
532static ssize_t
533archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
534    int wide, struct archive *a, struct archive_string_conv *sc) {
535	struct archive_acl_entry *ap;
536	const char *name;
537	const wchar_t *wname;
538	int count, idlen, tmp, r;
539	ssize_t length;
540	size_t len;
541
542	count = 0;
543	length = 0;
544	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
545		if ((ap->type & want_type) == 0)
546			continue;
547		/*
548		 * Filemode-mapping ACL entries are stored exclusively in
549		 * ap->mode so they should not be in the list
550		 */
551		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
552		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
553		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
554		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
555			continue;
556		count++;
557		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
558		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
559			length += 8; /* "default:" */
560		switch (ap->tag) {
561		case ARCHIVE_ENTRY_ACL_USER_OBJ:
562			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
563				length += 6; /* "owner@" */
564				break;
565			}
566			/* FALLTHROUGH */
567		case ARCHIVE_ENTRY_ACL_USER:
568		case ARCHIVE_ENTRY_ACL_MASK:
569			length += 4; /* "user", "mask" */
570			break;
571		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
572			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
573				length += 6; /* "group@" */
574				break;
575			}
576			/* FALLTHROUGH */
577		case ARCHIVE_ENTRY_ACL_GROUP:
578		case ARCHIVE_ENTRY_ACL_OTHER:
579			length += 5; /* "group", "other" */
580			break;
581		case ARCHIVE_ENTRY_ACL_EVERYONE:
582			length += 9; /* "everyone@" */
583			break;
584		}
585		length += 1; /* colon after tag */
586		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
587		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
588			if (wide) {
589				r = archive_mstring_get_wcs(a, &ap->name,
590				    &wname);
591				if (r == 0 && wname != NULL)
592					length += wcslen(wname);
593				else if (r < 0 && errno == ENOMEM)
594					return (0);
595				else
596					length += sizeof(uid_t) * 3 + 1;
597			} else {
598				r = archive_mstring_get_mbs_l(a, &ap->name, &name,
599				    &len, sc);
600				if (r != 0)
601					return (0);
602				if (len > 0 && name != NULL)
603					length += len;
604				else
605					length += sizeof(uid_t) * 3 + 1;
606			}
607			length += 1; /* colon after user or group name */
608		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
609			length += 1; /* 2nd colon empty user,group or other */
610
611		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
612		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
613		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
614		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
615			/* Solaris has no colon after other: and mask: */
616			length = length - 1;
617		}
618
619		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
620			/* rwxpdDaARWcCos:fdinSFI:deny */
621			length += 27;
622			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
623				length += 1; /* allow, alarm, audit */
624		} else
625			length += 3; /* rwx */
626
627		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
628		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
629		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
630			length += 1; /* colon */
631			/* ID digit count */
632			idlen = 1;
633			tmp = ap->id;
634			while (tmp > 9) {
635				tmp = tmp / 10;
636				idlen++;
637			}
638			length += idlen;
639		}
640		length ++; /* entry separator */
641	}
642
643	/* Add filemode-mapping access entries to the length */
644	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
645		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
646			/* "user::rwx\ngroup::rwx\nother:rwx\n" */
647			length += 31;
648		} else {
649			/* "user::rwx\ngroup::rwx\nother::rwx\n" */
650			length += 32;
651		}
652	} else if (count == 0)
653		return (0);
654
655	/* The terminating character is included in count */
656	return (length);
657}
658
659/*
660 * Generate a wide text version of the ACL. The flags parameter controls
661 * the type and style of the generated ACL.
662 */
663wchar_t *
664archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
665    struct archive *a)
666{
667	int count;
668	ssize_t length;
669	size_t len;
670	const wchar_t *wname;
671	const wchar_t *prefix;
672	wchar_t separator;
673	struct archive_acl_entry *ap;
674	int id, r, want_type;
675	wchar_t *wp, *ws;
676
677	want_type = archive_acl_text_want_type(acl, flags);
678
679	/* Both NFSv4 and POSIX.1 types found */
680	if (want_type == 0)
681		return (NULL);
682
683	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
684		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
685
686	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
687
688	if (length == 0)
689		return (NULL);
690
691	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
692		separator = L',';
693	else
694		separator = L'\n';
695
696	/* Now, allocate the string and actually populate it. */
697	wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
698	if (wp == NULL) {
699		if (errno == ENOMEM)
700			__archive_errx(1, "No memory");
701		return (NULL);
702	}
703	count = 0;
704
705	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
706		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
707		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
708		    acl->mode & 0700, -1);
709		*wp++ = separator;
710		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
711		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
712		    acl->mode & 0070, -1);
713		*wp++ = separator;
714		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
715		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
716		    acl->mode & 0007, -1);
717		count += 3;
718	}
719
720	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
721		if ((ap->type & want_type) == 0)
722			continue;
723		/*
724		 * Filemode-mapping ACL entries are stored exclusively in
725		 * ap->mode so they should not be in the list
726		 */
727		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
728		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
729		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
730		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
731			continue;
732		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
733		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
734			prefix = L"default:";
735		else
736			prefix = NULL;
737		r = archive_mstring_get_wcs(a, &ap->name, &wname);
738		if (r == 0) {
739			if (count > 0)
740				*wp++ = separator;
741			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
742				id = ap->id;
743			else
744				id = -1;
745			append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
746			    wname, ap->permset, id);
747			count++;
748		} else if (r < 0 && errno == ENOMEM)
749			return (NULL);
750	}
751
752	/* Add terminating character */
753	*wp++ = L'\0';
754
755	len = wcslen(ws);
756
757	if ((ssize_t)len > (length - 1))
758		__archive_errx(1, "Buffer overrun");
759
760	if (text_len != NULL)
761		*text_len = len;
762
763	return (ws);
764}
765
766static void
767append_id_w(wchar_t **wp, int id)
768{
769	if (id < 0)
770		id = 0;
771	if (id > 9)
772		append_id_w(wp, id / 10);
773	*(*wp)++ = L"0123456789"[id % 10];
774}
775
776static void
777append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
778    int tag, int flags, const wchar_t *wname, int perm, int id)
779{
780	int i;
781
782	if (prefix != NULL) {
783		wcscpy(*wp, prefix);
784		*wp += wcslen(*wp);
785	}
786	switch (tag) {
787	case ARCHIVE_ENTRY_ACL_USER_OBJ:
788		wname = NULL;
789		id = -1;
790		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
791			wcscpy(*wp, L"owner@");
792			break;
793		}
794		/* FALLTHROUGH */
795	case ARCHIVE_ENTRY_ACL_USER:
796		wcscpy(*wp, L"user");
797		break;
798	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
799		wname = NULL;
800		id = -1;
801		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
802			wcscpy(*wp, L"group@");
803			break;
804		}
805		/* FALLTHROUGH */
806	case ARCHIVE_ENTRY_ACL_GROUP:
807		wcscpy(*wp, L"group");
808		break;
809	case ARCHIVE_ENTRY_ACL_MASK:
810		wcscpy(*wp, L"mask");
811		wname = NULL;
812		id = -1;
813		break;
814	case ARCHIVE_ENTRY_ACL_OTHER:
815		wcscpy(*wp, L"other");
816		wname = NULL;
817		id = -1;
818		break;
819	case ARCHIVE_ENTRY_ACL_EVERYONE:
820		wcscpy(*wp, L"everyone@");
821		wname = NULL;
822		id = -1;
823		break;
824	}
825	*wp += wcslen(*wp);
826	*(*wp)++ = L':';
827	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
828	    tag == ARCHIVE_ENTRY_ACL_USER ||
829	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
830		if (wname != NULL) {
831			wcscpy(*wp, wname);
832			*wp += wcslen(*wp);
833		} else if (tag == ARCHIVE_ENTRY_ACL_USER
834		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
835			append_id_w(wp, id);
836			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
837				id = -1;
838		}
839		/* Solaris style has no second colon after other and mask */
840		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
841		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
842		    && tag != ARCHIVE_ENTRY_ACL_MASK))
843			*(*wp)++ = L':';
844	}
845	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
846		/* POSIX.1e ACL perms */
847		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
848		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
849		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
850	} else {
851		/* NFSv4 ACL perms */
852		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
853			if (perm & nfsv4_acl_perm_map[i].perm)
854				*(*wp)++ = nfsv4_acl_perm_map[i].wc;
855			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
856				*(*wp)++ = L'-';
857		}
858		*(*wp)++ = L':';
859		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
860			if (perm & nfsv4_acl_flag_map[i].perm)
861				*(*wp)++ = nfsv4_acl_flag_map[i].wc;
862			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
863				*(*wp)++ = L'-';
864		}
865		*(*wp)++ = L':';
866		switch (type) {
867		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
868			wcscpy(*wp, L"allow");
869			break;
870		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
871			wcscpy(*wp, L"deny");
872			break;
873		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
874			wcscpy(*wp, L"audit");
875			break;
876		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
877			wcscpy(*wp, L"alarm");
878			break;
879		default:
880			break;
881		}
882		*wp += wcslen(*wp);
883	}
884	if (id != -1) {
885		*(*wp)++ = L':';
886		append_id_w(wp, id);
887	}
888}
889
890/*
891 * Generate a text version of the ACL. The flags parameter controls
892 * the type and style of the generated ACL.
893 */
894char *
895archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
896    struct archive_string_conv *sc)
897{
898	int count;
899	ssize_t length;
900	size_t len;
901	const char *name;
902	const char *prefix;
903	char separator;
904	struct archive_acl_entry *ap;
905	int id, r, want_type;
906	char *p, *s;
907
908	want_type = archive_acl_text_want_type(acl, flags);
909
910	/* Both NFSv4 and POSIX.1 types found */
911	if (want_type == 0)
912		return (NULL);
913
914	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
915		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
916
917	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
918
919	if (length == 0)
920		return (NULL);
921
922	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
923		separator = ',';
924	else
925		separator = '\n';
926
927	/* Now, allocate the string and actually populate it. */
928	p = s = (char *)malloc(length * sizeof(char));
929	if (p == NULL) {
930		if (errno == ENOMEM)
931			__archive_errx(1, "No memory");
932		return (NULL);
933	}
934	count = 0;
935
936	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
937		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
938		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
939		    acl->mode & 0700, -1);
940		*p++ = separator;
941		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
942		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
943		    acl->mode & 0070, -1);
944		*p++ = separator;
945		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
946		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
947		    acl->mode & 0007, -1);
948		count += 3;
949	}
950
951	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
952		if ((ap->type & want_type) == 0)
953			continue;
954		/*
955		 * Filemode-mapping ACL entries are stored exclusively in
956		 * ap->mode so they should not be in the list
957		 */
958		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
959		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
960		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
961		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
962			continue;
963		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
964		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
965			prefix = "default:";
966		else
967			prefix = NULL;
968		r = archive_mstring_get_mbs_l(
969		    NULL, &ap->name, &name, &len, sc);
970		if (r != 0)
971			return (NULL);
972		if (count > 0)
973			*p++ = separator;
974		if (name == NULL ||
975		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
976			id = ap->id;
977		} else {
978			id = -1;
979		}
980		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
981		    ap->permset, id);
982		count++;
983	}
984
985	/* Add terminating character */
986	*p++ = '\0';
987
988	len = strlen(s);
989
990	if ((ssize_t)len > (length - 1))
991		__archive_errx(1, "Buffer overrun");
992
993	if (text_len != NULL)
994		*text_len = len;
995
996	return (s);
997}
998
999static void
1000append_id(char **p, int id)
1001{
1002	if (id < 0)
1003		id = 0;
1004	if (id > 9)
1005		append_id(p, id / 10);
1006	*(*p)++ = "0123456789"[id % 10];
1007}
1008
1009static void
1010append_entry(char **p, const char *prefix, int type,
1011    int tag, int flags, const char *name, int perm, int id)
1012{
1013	int i;
1014
1015	if (prefix != NULL) {
1016		strcpy(*p, prefix);
1017		*p += strlen(*p);
1018	}
1019	switch (tag) {
1020	case ARCHIVE_ENTRY_ACL_USER_OBJ:
1021		name = NULL;
1022		id = -1;
1023		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1024			strcpy(*p, "owner@");
1025			break;
1026		}
1027		/* FALLTHROUGH */
1028	case ARCHIVE_ENTRY_ACL_USER:
1029		strcpy(*p, "user");
1030		break;
1031	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1032		name = NULL;
1033		id = -1;
1034		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1035			strcpy(*p, "group@");
1036			break;
1037		}
1038		/* FALLTHROUGH */
1039	case ARCHIVE_ENTRY_ACL_GROUP:
1040		strcpy(*p, "group");
1041		break;
1042	case ARCHIVE_ENTRY_ACL_MASK:
1043		strcpy(*p, "mask");
1044		name = NULL;
1045		id = -1;
1046		break;
1047	case ARCHIVE_ENTRY_ACL_OTHER:
1048		strcpy(*p, "other");
1049		name = NULL;
1050		id = -1;
1051		break;
1052	case ARCHIVE_ENTRY_ACL_EVERYONE:
1053		strcpy(*p, "everyone@");
1054		name = NULL;
1055		id = -1;
1056		break;
1057	}
1058	*p += strlen(*p);
1059	*(*p)++ = ':';
1060	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1061	    tag == ARCHIVE_ENTRY_ACL_USER ||
1062	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1063		if (name != NULL) {
1064			strcpy(*p, name);
1065			*p += strlen(*p);
1066		} else if (tag == ARCHIVE_ENTRY_ACL_USER
1067		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1068			append_id(p, id);
1069			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1070				id = -1;
1071		}
1072		/* Solaris style has no second colon after other and mask */
1073		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1074		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
1075		    && tag != ARCHIVE_ENTRY_ACL_MASK))
1076			*(*p)++ = ':';
1077	}
1078	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1079		/* POSIX.1e ACL perms */
1080		*(*p)++ = (perm & 0444) ? 'r' : '-';
1081		*(*p)++ = (perm & 0222) ? 'w' : '-';
1082		*(*p)++ = (perm & 0111) ? 'x' : '-';
1083	} else {
1084		/* NFSv4 ACL perms */
1085		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1086			if (perm & nfsv4_acl_perm_map[i].perm)
1087				*(*p)++ = nfsv4_acl_perm_map[i].c;
1088			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1089				*(*p)++ = '-';
1090		}
1091		*(*p)++ = ':';
1092		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1093			if (perm & nfsv4_acl_flag_map[i].perm)
1094				*(*p)++ = nfsv4_acl_flag_map[i].c;
1095			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1096				*(*p)++ = '-';
1097		}
1098		*(*p)++ = ':';
1099		switch (type) {
1100		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1101			strcpy(*p, "allow");
1102			break;
1103		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1104			strcpy(*p, "deny");
1105			break;
1106		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1107			strcpy(*p, "audit");
1108			break;
1109		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1110			strcpy(*p, "alarm");
1111			break;
1112		}
1113		*p += strlen(*p);
1114	}
1115	if (id != -1) {
1116		*(*p)++ = ':';
1117		append_id(p, id);
1118	}
1119}
1120
1121/*
1122 * Parse a wide ACL text string.
1123 *
1124 * The want_type argument may be one of the following:
1125 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1126 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1127 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1128 *
1129 * POSIX.1e ACL entries prefixed with "default:" are treated as
1130 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1131 */
1132int
1133archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1134    int want_type)
1135{
1136	struct {
1137		const wchar_t *start;
1138		const wchar_t *end;
1139	} field[6], name;
1140
1141	const wchar_t *s, *st;
1142
1143	int numfields, fields, n, r, sol, ret;
1144	int type, types, tag, permset, id;
1145	size_t len;
1146	wchar_t sep;
1147
1148	ret = ARCHIVE_OK;
1149	types = 0;
1150
1151	switch (want_type) {
1152	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1153		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1154		__LA_FALLTHROUGH;
1155	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1156	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1157		numfields = 5;
1158		break;
1159	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1160		numfields = 6;
1161		break;
1162	default:
1163		return (ARCHIVE_FATAL);
1164	}
1165
1166	while (text != NULL && *text != L'\0') {
1167		/*
1168		 * Parse the fields out of the next entry,
1169		 * advance 'text' to start of next entry.
1170		 */
1171		fields = 0;
1172		do {
1173			const wchar_t *start, *end;
1174			next_field_w(&text, &start, &end, &sep);
1175			if (fields < numfields) {
1176				field[fields].start = start;
1177				field[fields].end = end;
1178			}
1179			++fields;
1180		} while (sep == L':');
1181
1182		/* Set remaining fields to blank. */
1183		for (n = fields; n < numfields; ++n)
1184			field[n].start = field[n].end = NULL;
1185
1186		if (field[0].start != NULL && *(field[0].start) == L'#') {
1187			/* Comment, skip entry */
1188			continue;
1189		}
1190
1191		n = 0;
1192		sol = 0;
1193		id = -1;
1194		permset = 0;
1195		name.start = name.end = NULL;
1196
1197		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1198			/* POSIX.1e ACLs */
1199			/*
1200			 * Default keyword "default:user::rwx"
1201			 * if found, we have one more field
1202			 *
1203			 * We also support old Solaris extension:
1204			 * "defaultuser::rwx" is the default ACL corresponding
1205			 * to "user::rwx", etc. valid only for first field
1206			 */
1207			s = field[0].start;
1208			len = field[0].end - field[0].start;
1209			if (*s == L'd' && (len == 1 || (len >= 7
1210			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
1211				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1212				if (len > 7)
1213					field[0].start += 7;
1214				else
1215					n = 1;
1216			} else
1217				type = want_type;
1218
1219			/* Check for a numeric ID in field n+1 or n+3. */
1220			isint_w(field[n + 1].start, field[n + 1].end, &id);
1221			/* Field n+3 is optional. */
1222			if (id == -1 && fields > n+3)
1223				isint_w(field[n + 3].start, field[n + 3].end,
1224				    &id);
1225
1226			tag = 0;
1227			s = field[n].start;
1228			st = field[n].start + 1;
1229			len = field[n].end - field[n].start;
1230
1231			switch (*s) {
1232			case L'u':
1233				if (len == 1 || (len == 4
1234				    && wmemcmp(st, L"ser", 3) == 0))
1235					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1236				break;
1237			case L'g':
1238				if (len == 1 || (len == 5
1239				    && wmemcmp(st, L"roup", 4) == 0))
1240					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1241				break;
1242			case L'o':
1243				if (len == 1 || (len == 5
1244				    && wmemcmp(st, L"ther", 4) == 0))
1245					tag = ARCHIVE_ENTRY_ACL_OTHER;
1246				break;
1247			case L'm':
1248				if (len == 1 || (len == 4
1249				    && wmemcmp(st, L"ask", 3) == 0))
1250					tag = ARCHIVE_ENTRY_ACL_MASK;
1251				break;
1252			default:
1253					break;
1254			}
1255
1256			switch (tag) {
1257			case ARCHIVE_ENTRY_ACL_OTHER:
1258			case ARCHIVE_ENTRY_ACL_MASK:
1259				if (fields == (n + 2)
1260				    && field[n + 1].start < field[n + 1].end
1261				    && ismode_w(field[n + 1].start,
1262				    field[n + 1].end, &permset)) {
1263					/* This is Solaris-style "other:rwx" */
1264					sol = 1;
1265				} else if (fields == (n + 3) &&
1266				    field[n + 1].start < field[n + 1].end) {
1267					/* Invalid mask or other field */
1268					ret = ARCHIVE_WARN;
1269					continue;
1270				}
1271				break;
1272			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1273			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1274				if (id != -1 ||
1275				    field[n + 1].start < field[n + 1].end) {
1276					name = field[n + 1];
1277					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1278						tag = ARCHIVE_ENTRY_ACL_USER;
1279					else
1280						tag = ARCHIVE_ENTRY_ACL_GROUP;
1281				}
1282				break;
1283			default:
1284				/* Invalid tag, skip entry */
1285				ret = ARCHIVE_WARN;
1286				continue;
1287			}
1288
1289			/*
1290			 * Without "default:" we expect mode in field 2
1291			 * Exception: Solaris other and mask fields
1292			 */
1293			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1294			    field[n + 2 - sol].end, &permset)) {
1295				/* Invalid mode, skip entry */
1296				ret = ARCHIVE_WARN;
1297				continue;
1298			}
1299		} else {
1300			/* NFS4 ACLs */
1301			s = field[0].start;
1302			len = field[0].end - field[0].start;
1303			tag = 0;
1304
1305			switch (len) {
1306			case 4:
1307				if (wmemcmp(s, L"user", 4) == 0)
1308					tag = ARCHIVE_ENTRY_ACL_USER;
1309				break;
1310			case 5:
1311				if (wmemcmp(s, L"group", 5) == 0)
1312					tag = ARCHIVE_ENTRY_ACL_GROUP;
1313				break;
1314			case 6:
1315				if (wmemcmp(s, L"owner@", 6) == 0)
1316					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1317				else if (wmemcmp(s, L"group@", len) == 0)
1318					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1319				break;
1320			case 9:
1321				if (wmemcmp(s, L"everyone@", 9) == 0)
1322					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1323			default:
1324				break;
1325			}
1326
1327			if (tag == 0) {
1328				/* Invalid tag, skip entry */
1329				ret = ARCHIVE_WARN;
1330				continue;
1331			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1332			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1333				n = 1;
1334				name = field[1];
1335				isint_w(name.start, name.end, &id);
1336			} else
1337				n = 0;
1338
1339			if (!is_nfs4_perms_w(field[1 + n].start,
1340			    field[1 + n].end, &permset)) {
1341				/* Invalid NFSv4 perms, skip entry */
1342				ret = ARCHIVE_WARN;
1343				continue;
1344			}
1345			if (!is_nfs4_flags_w(field[2 + n].start,
1346			    field[2 + n].end, &permset)) {
1347				/* Invalid NFSv4 flags, skip entry */
1348				ret = ARCHIVE_WARN;
1349				continue;
1350			}
1351			s = field[3 + n].start;
1352			len = field[3 + n].end - field[3 + n].start;
1353			type = 0;
1354			if (len == 4) {
1355				if (wmemcmp(s, L"deny", 4) == 0)
1356					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1357			} else if (len == 5) {
1358				if (wmemcmp(s, L"allow", 5) == 0)
1359					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1360				else if (wmemcmp(s, L"audit", 5) == 0)
1361					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1362				else if (wmemcmp(s, L"alarm", 5) == 0)
1363					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1364			}
1365			if (type == 0) {
1366				/* Invalid entry type, skip entry */
1367				ret = ARCHIVE_WARN;
1368				continue;
1369			}
1370			isint_w(field[4 + n].start, field[4 + n].end, &id);
1371		}
1372
1373		/* Add entry to the internal list. */
1374		r = archive_acl_add_entry_w_len(acl, type, permset,
1375		    tag, id, name.start, name.end - name.start);
1376		if (r < ARCHIVE_WARN)
1377			return (r);
1378		if (r != ARCHIVE_OK)
1379			ret = ARCHIVE_WARN;
1380		types |= type;
1381	}
1382
1383	/* Reset ACL */
1384	archive_acl_reset(acl, types);
1385
1386	return (ret);
1387}
1388
1389/*
1390 * Parse a string to a positive decimal integer.  Returns true if
1391 * the string is non-empty and consists only of decimal digits,
1392 * false otherwise.
1393 */
1394static int
1395isint_w(const wchar_t *start, const wchar_t *end, int *result)
1396{
1397	int n = 0;
1398	if (start >= end)
1399		return (0);
1400	while (start < end) {
1401		if (*start < L'0' || *start > L'9')
1402			return (0);
1403		if (n > (INT_MAX / 10) ||
1404		    (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
1405			n = INT_MAX;
1406		} else {
1407			n *= 10;
1408			n += *start - L'0';
1409		}
1410		start++;
1411	}
1412	*result = n;
1413	return (1);
1414}
1415
1416/*
1417 * Parse a string as a mode field.  Returns true if
1418 * the string is non-empty and consists only of mode characters,
1419 * false otherwise.
1420 */
1421static int
1422ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1423{
1424	const wchar_t *p;
1425
1426	if (start >= end)
1427		return (0);
1428	p = start;
1429	*permset = 0;
1430	while (p < end) {
1431		switch (*p++) {
1432		case L'r': case L'R':
1433			*permset |= ARCHIVE_ENTRY_ACL_READ;
1434			break;
1435		case L'w': case L'W':
1436			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1437			break;
1438		case L'x': case L'X':
1439			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1440			break;
1441		case L'-':
1442			break;
1443		default:
1444			return (0);
1445		}
1446	}
1447	return (1);
1448}
1449
1450/*
1451 * Parse a string as a NFS4 ACL permission field.
1452 * Returns true if the string is non-empty and consists only of NFS4 ACL
1453 * permission characters, false otherwise
1454 */
1455static int
1456is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1457{
1458	const wchar_t *p = start;
1459
1460	while (p < end) {
1461		switch (*p++) {
1462		case L'r':
1463			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1464			break;
1465		case L'w':
1466			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1467			break;
1468		case L'x':
1469			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1470			break;
1471		case L'p':
1472			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1473			break;
1474		case L'D':
1475			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1476			break;
1477		case L'd':
1478			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1479			break;
1480		case L'a':
1481			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1482			break;
1483		case L'A':
1484			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1485			break;
1486		case L'R':
1487			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1488			break;
1489		case L'W':
1490			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1491			break;
1492		case L'c':
1493			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1494			break;
1495		case L'C':
1496			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1497			break;
1498		case L'o':
1499			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1500			break;
1501		case L's':
1502			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1503			break;
1504		case L'-':
1505			break;
1506		default:
1507			return(0);
1508		}
1509	}
1510	return (1);
1511}
1512
1513/*
1514 * Parse a string as a NFS4 ACL flags field.
1515 * Returns true if the string is non-empty and consists only of NFS4 ACL
1516 * flag characters, false otherwise
1517 */
1518static int
1519is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1520{
1521	const wchar_t *p = start;
1522
1523	while (p < end) {
1524		switch(*p++) {
1525		case L'f':
1526			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1527			break;
1528		case L'd':
1529			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1530			break;
1531		case L'i':
1532			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1533			break;
1534		case L'n':
1535			*permset |=
1536			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1537			break;
1538		case L'S':
1539			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1540			break;
1541		case L'F':
1542			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1543			break;
1544		case L'I':
1545			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1546			break;
1547		case L'-':
1548			break;
1549		default:
1550			return (0);
1551		}
1552	}
1553	return (1);
1554}
1555
1556/*
1557 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1558 * to point to just after the separator.  *start points to the first
1559 * character of the matched text and *end just after the last
1560 * character of the matched identifier.  In particular *end - *start
1561 * is the length of the field body, not including leading or trailing
1562 * whitespace.
1563 */
1564static void
1565next_field_w(const wchar_t **wp, const wchar_t **start,
1566    const wchar_t **end, wchar_t *sep)
1567{
1568	/* Skip leading whitespace to find start of field. */
1569	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1570		(*wp)++;
1571	}
1572	*start = *wp;
1573
1574	/* Scan for the separator. */
1575	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1576	    **wp != L'\n') {
1577		(*wp)++;
1578	}
1579	*sep = **wp;
1580
1581	/* Trim trailing whitespace to locate end of field. */
1582	*end = *wp - 1;
1583	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1584		(*end)--;
1585	}
1586	(*end)++;
1587
1588	/* Adjust scanner location. */
1589	if (**wp != L'\0')
1590		(*wp)++;
1591}
1592
1593/*
1594 * Parse an ACL text string.
1595 *
1596 * The want_type argument may be one of the following:
1597 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1598 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1599 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1600 *
1601 * POSIX.1e ACL entries prefixed with "default:" are treated as
1602 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1603 */
1604int
1605archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1606    int want_type, struct archive_string_conv *sc)
1607{
1608	struct {
1609		const char *start;
1610		const char *end;
1611	} field[6], name;
1612
1613	const char *s, *st;
1614	int numfields, fields, n, r, sol, ret;
1615	int type, types, tag, permset, id;
1616	size_t len;
1617	char sep;
1618
1619	switch (want_type) {
1620	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1621		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1622		__LA_FALLTHROUGH;
1623	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1624	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1625		numfields = 5;
1626		break;
1627	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1628		numfields = 6;
1629		break;
1630	default:
1631		return (ARCHIVE_FATAL);
1632	}
1633
1634	ret = ARCHIVE_OK;
1635	types = 0;
1636
1637	while (text != NULL  &&  *text != '\0') {
1638		/*
1639		 * Parse the fields out of the next entry,
1640		 * advance 'text' to start of next entry.
1641		 */
1642		fields = 0;
1643		do {
1644			const char *start, *end;
1645			next_field(&text, &start, &end, &sep);
1646			if (fields < numfields) {
1647				field[fields].start = start;
1648				field[fields].end = end;
1649			}
1650			++fields;
1651		} while (sep == ':');
1652
1653		/* Set remaining fields to blank. */
1654		for (n = fields; n < numfields; ++n)
1655			field[n].start = field[n].end = NULL;
1656
1657		if (field[0].start != NULL && *(field[0].start) == '#') {
1658			/* Comment, skip entry */
1659			continue;
1660		}
1661
1662		n = 0;
1663		sol = 0;
1664		id = -1;
1665		permset = 0;
1666		name.start = name.end = NULL;
1667
1668		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1669			/* POSIX.1e ACLs */
1670			/*
1671			 * Default keyword "default:user::rwx"
1672			 * if found, we have one more field
1673			 *
1674			 * We also support old Solaris extension:
1675			 * "defaultuser::rwx" is the default ACL corresponding
1676			 * to "user::rwx", etc. valid only for first field
1677			 */
1678			s = field[0].start;
1679			len = field[0].end - field[0].start;
1680			if (*s == 'd' && (len == 1 || (len >= 7
1681			    && memcmp((s + 1), "efault", 6) == 0))) {
1682				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1683				if (len > 7)
1684					field[0].start += 7;
1685				else
1686					n = 1;
1687			} else
1688				type = want_type;
1689
1690			/* Check for a numeric ID in field n+1 or n+3. */
1691			isint(field[n + 1].start, field[n + 1].end, &id);
1692			/* Field n+3 is optional. */
1693			if (id == -1 && fields > (n + 3))
1694				isint(field[n + 3].start, field[n + 3].end,
1695				    &id);
1696
1697			tag = 0;
1698			s = field[n].start;
1699			st = field[n].start + 1;
1700			len = field[n].end - field[n].start;
1701
1702			switch (*s) {
1703			case 'u':
1704				if (len == 1 || (len == 4
1705				    && memcmp(st, "ser", 3) == 0))
1706					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1707				break;
1708			case 'g':
1709				if (len == 1 || (len == 5
1710				    && memcmp(st, "roup", 4) == 0))
1711					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1712				break;
1713			case 'o':
1714				if (len == 1 || (len == 5
1715				    && memcmp(st, "ther", 4) == 0))
1716					tag = ARCHIVE_ENTRY_ACL_OTHER;
1717				break;
1718			case 'm':
1719				if (len == 1 || (len == 4
1720				    && memcmp(st, "ask", 3) == 0))
1721					tag = ARCHIVE_ENTRY_ACL_MASK;
1722				break;
1723			default:
1724					break;
1725			}
1726
1727			switch (tag) {
1728			case ARCHIVE_ENTRY_ACL_OTHER:
1729			case ARCHIVE_ENTRY_ACL_MASK:
1730				if (fields == (n + 2)
1731				    && field[n + 1].start < field[n + 1].end
1732				    && ismode(field[n + 1].start,
1733				    field[n + 1].end, &permset)) {
1734					/* This is Solaris-style "other:rwx" */
1735					sol = 1;
1736				} else if (fields == (n + 3) &&
1737				    field[n + 1].start < field[n + 1].end) {
1738					/* Invalid mask or other field */
1739					ret = ARCHIVE_WARN;
1740					continue;
1741				}
1742				break;
1743			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1744			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1745				if (id != -1 ||
1746				    field[n + 1].start < field[n + 1].end) {
1747					name = field[n + 1];
1748					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1749						tag = ARCHIVE_ENTRY_ACL_USER;
1750					else
1751						tag = ARCHIVE_ENTRY_ACL_GROUP;
1752				}
1753				break;
1754			default:
1755				/* Invalid tag, skip entry */
1756				ret = ARCHIVE_WARN;
1757				continue;
1758			}
1759
1760			/*
1761			 * Without "default:" we expect mode in field 3
1762			 * Exception: Solaris other and mask fields
1763			 */
1764			if (permset == 0 && !ismode(field[n + 2 - sol].start,
1765			    field[n + 2 - sol].end, &permset)) {
1766				/* Invalid mode, skip entry */
1767				ret = ARCHIVE_WARN;
1768				continue;
1769			}
1770		} else {
1771			/* NFS4 ACLs */
1772			s = field[0].start;
1773			len = field[0].end - field[0].start;
1774			tag = 0;
1775
1776			switch (len) {
1777			case 4:
1778				if (memcmp(s, "user", 4) == 0)
1779					tag = ARCHIVE_ENTRY_ACL_USER;
1780				break;
1781			case 5:
1782				if (memcmp(s, "group", 5) == 0)
1783					tag = ARCHIVE_ENTRY_ACL_GROUP;
1784				break;
1785			case 6:
1786				if (memcmp(s, "owner@", 6) == 0)
1787					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1788				else if (memcmp(s, "group@", 6) == 0)
1789					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1790				break;
1791			case 9:
1792				if (memcmp(s, "everyone@", 9) == 0)
1793					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1794				break;
1795			default:
1796				break;
1797			}
1798
1799			if (tag == 0) {
1800				/* Invalid tag, skip entry */
1801				ret = ARCHIVE_WARN;
1802				continue;
1803			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1804			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1805				n = 1;
1806				name = field[1];
1807				isint(name.start, name.end, &id);
1808			} else
1809				n = 0;
1810
1811			if (!is_nfs4_perms(field[1 + n].start,
1812			    field[1 + n].end, &permset)) {
1813				/* Invalid NFSv4 perms, skip entry */
1814				ret = ARCHIVE_WARN;
1815				continue;
1816			}
1817			if (!is_nfs4_flags(field[2 + n].start,
1818			    field[2 + n].end, &permset)) {
1819				/* Invalid NFSv4 flags, skip entry */
1820				ret = ARCHIVE_WARN;
1821				continue;
1822			}
1823			s = field[3 + n].start;
1824			len = field[3 + n].end - field[3 + n].start;
1825			type = 0;
1826			if (len == 4) {
1827				if (memcmp(s, "deny", 4) == 0)
1828					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1829			} else if (len == 5) {
1830				if (memcmp(s, "allow", 5) == 0)
1831					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1832				else if (memcmp(s, "audit", 5) == 0)
1833					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1834				else if (memcmp(s, "alarm", 5) == 0)
1835					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1836			}
1837			if (type == 0) {
1838				/* Invalid entry type, skip entry */
1839				ret = ARCHIVE_WARN;
1840				continue;
1841			}
1842			isint(field[4 + n].start, field[4 + n].end,
1843			    &id);
1844		}
1845
1846		/* Add entry to the internal list. */
1847		r = archive_acl_add_entry_len_l(acl, type, permset,
1848		    tag, id, name.start, name.end - name.start, sc);
1849		if (r < ARCHIVE_WARN)
1850			return (r);
1851		if (r != ARCHIVE_OK)
1852			ret = ARCHIVE_WARN;
1853		types |= type;
1854	}
1855
1856	/* Reset ACL */
1857	archive_acl_reset(acl, types);
1858
1859	return (ret);
1860}
1861
1862/*
1863 * Parse a string to a positive decimal integer.  Returns true if
1864 * the string is non-empty and consists only of decimal digits,
1865 * false otherwise.
1866 */
1867static int
1868isint(const char *start, const char *end, int *result)
1869{
1870	int n = 0;
1871	if (start >= end)
1872		return (0);
1873	while (start < end) {
1874		if (*start < '0' || *start > '9')
1875			return (0);
1876		if (n > (INT_MAX / 10) ||
1877		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1878			n = INT_MAX;
1879		} else {
1880			n *= 10;
1881			n += *start - '0';
1882		}
1883		start++;
1884	}
1885	*result = n;
1886	return (1);
1887}
1888
1889/*
1890 * Parse a string as a mode field.  Returns true if
1891 * the string is non-empty and consists only of mode characters,
1892 * false otherwise.
1893 */
1894static int
1895ismode(const char *start, const char *end, int *permset)
1896{
1897	const char *p;
1898
1899	if (start >= end)
1900		return (0);
1901	p = start;
1902	*permset = 0;
1903	while (p < end) {
1904		switch (*p++) {
1905		case 'r': case 'R':
1906			*permset |= ARCHIVE_ENTRY_ACL_READ;
1907			break;
1908		case 'w': case 'W':
1909			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1910			break;
1911		case 'x': case 'X':
1912			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1913			break;
1914		case '-':
1915			break;
1916		default:
1917			return (0);
1918		}
1919	}
1920	return (1);
1921}
1922
1923/*
1924 * Parse a string as a NFS4 ACL permission field.
1925 * Returns true if the string is non-empty and consists only of NFS4 ACL
1926 * permission characters, false otherwise
1927 */
1928static int
1929is_nfs4_perms(const char *start, const char *end, int *permset)
1930{
1931	const char *p = start;
1932
1933	while (p < end) {
1934		switch (*p++) {
1935		case 'r':
1936			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1937			break;
1938		case 'w':
1939			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1940			break;
1941		case 'x':
1942			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1943			break;
1944		case 'p':
1945			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1946			break;
1947		case 'D':
1948			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1949			break;
1950		case 'd':
1951			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1952			break;
1953		case 'a':
1954			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1955			break;
1956		case 'A':
1957			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1958			break;
1959		case 'R':
1960			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1961			break;
1962		case 'W':
1963			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1964			break;
1965		case 'c':
1966			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1967			break;
1968		case 'C':
1969			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1970			break;
1971		case 'o':
1972			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1973			break;
1974		case 's':
1975			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1976			break;
1977		case '-':
1978			break;
1979		default:
1980			return(0);
1981		}
1982	}
1983	return (1);
1984}
1985
1986/*
1987 * Parse a string as a NFS4 ACL flags field.
1988 * Returns true if the string is non-empty and consists only of NFS4 ACL
1989 * flag characters, false otherwise
1990 */
1991static int
1992is_nfs4_flags(const char *start, const char *end, int *permset)
1993{
1994	const char *p = start;
1995
1996	while (p < end) {
1997		switch(*p++) {
1998		case 'f':
1999			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2000			break;
2001		case 'd':
2002			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2003			break;
2004		case 'i':
2005			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2006			break;
2007		case 'n':
2008			*permset |=
2009			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2010			break;
2011		case 'S':
2012			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2013			break;
2014		case 'F':
2015			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2016			break;
2017		case 'I':
2018			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2019			break;
2020		case '-':
2021			break;
2022		default:
2023			return (0);
2024		}
2025	}
2026	return (1);
2027}
2028
2029/*
2030 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2031 * to point to just after the separator.  *start points to the first
2032 * character of the matched text and *end just after the last
2033 * character of the matched identifier.  In particular *end - *start
2034 * is the length of the field body, not including leading or trailing
2035 * whitespace.
2036 */
2037static void
2038next_field(const char **p, const char **start,
2039    const char **end, char *sep)
2040{
2041	/* Skip leading whitespace to find start of field. */
2042	while (**p == ' ' || **p == '\t' || **p == '\n') {
2043		(*p)++;
2044	}
2045	*start = *p;
2046
2047	/* Scan for the separator. */
2048	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
2049		(*p)++;
2050	}
2051	*sep = **p;
2052
2053	/* If the field is only whitespace, bail out now. */
2054	if (**p == '\0') {
2055		*end = *p;
2056		return;
2057	}
2058
2059	/* Trim trailing whitespace to locate end of field. */
2060	*end = *p - 1;
2061	while (**end == ' ' || **end == '\t' || **end == '\n') {
2062		(*end)--;
2063	}
2064	(*end)++;
2065
2066	/* Adjust scanner location. */
2067	if (**p != '\0')
2068		(*p)++;
2069}
2070