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