archive_acl.c revision 344673
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(&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			free(ws);
750			return (NULL);
751		}
752	}
753
754	/* Add terminating character */
755	*wp++ = L'\0';
756
757	len = wcslen(ws);
758
759	if ((ssize_t)len > (length - 1))
760		__archive_errx(1, "Buffer overrun");
761
762	if (text_len != NULL)
763		*text_len = len;
764
765	return (ws);
766}
767
768static void
769append_id_w(wchar_t **wp, int id)
770{
771	if (id < 0)
772		id = 0;
773	if (id > 9)
774		append_id_w(wp, id / 10);
775	*(*wp)++ = L"0123456789"[id % 10];
776}
777
778static void
779append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
780    int tag, int flags, const wchar_t *wname, int perm, int id)
781{
782	int i;
783
784	if (prefix != NULL) {
785		wcscpy(*wp, prefix);
786		*wp += wcslen(*wp);
787	}
788	switch (tag) {
789	case ARCHIVE_ENTRY_ACL_USER_OBJ:
790		wname = NULL;
791		id = -1;
792		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
793			wcscpy(*wp, L"owner@");
794			break;
795		}
796		/* FALLTHROUGH */
797	case ARCHIVE_ENTRY_ACL_USER:
798		wcscpy(*wp, L"user");
799		break;
800	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
801		wname = NULL;
802		id = -1;
803		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
804			wcscpy(*wp, L"group@");
805			break;
806		}
807		/* FALLTHROUGH */
808	case ARCHIVE_ENTRY_ACL_GROUP:
809		wcscpy(*wp, L"group");
810		break;
811	case ARCHIVE_ENTRY_ACL_MASK:
812		wcscpy(*wp, L"mask");
813		wname = NULL;
814		id = -1;
815		break;
816	case ARCHIVE_ENTRY_ACL_OTHER:
817		wcscpy(*wp, L"other");
818		wname = NULL;
819		id = -1;
820		break;
821	case ARCHIVE_ENTRY_ACL_EVERYONE:
822		wcscpy(*wp, L"everyone@");
823		wname = NULL;
824		id = -1;
825		break;
826	}
827	*wp += wcslen(*wp);
828	*(*wp)++ = L':';
829	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
830	    tag == ARCHIVE_ENTRY_ACL_USER ||
831	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
832		if (wname != NULL) {
833			wcscpy(*wp, wname);
834			*wp += wcslen(*wp);
835		} else if (tag == ARCHIVE_ENTRY_ACL_USER
836		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
837			append_id_w(wp, id);
838			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
839				id = -1;
840		}
841		/* Solaris style has no second colon after other and mask */
842		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
843		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
844		    && tag != ARCHIVE_ENTRY_ACL_MASK))
845			*(*wp)++ = L':';
846	}
847	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
848		/* POSIX.1e ACL perms */
849		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
850		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
851		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
852	} else {
853		/* NFSv4 ACL perms */
854		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
855			if (perm & nfsv4_acl_perm_map[i].perm)
856				*(*wp)++ = nfsv4_acl_perm_map[i].wc;
857			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
858				*(*wp)++ = L'-';
859		}
860		*(*wp)++ = L':';
861		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
862			if (perm & nfsv4_acl_flag_map[i].perm)
863				*(*wp)++ = nfsv4_acl_flag_map[i].wc;
864			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
865				*(*wp)++ = L'-';
866		}
867		*(*wp)++ = L':';
868		switch (type) {
869		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
870			wcscpy(*wp, L"allow");
871			break;
872		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
873			wcscpy(*wp, L"deny");
874			break;
875		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
876			wcscpy(*wp, L"audit");
877			break;
878		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
879			wcscpy(*wp, L"alarm");
880			break;
881		default:
882			break;
883		}
884		*wp += wcslen(*wp);
885	}
886	if (id != -1) {
887		*(*wp)++ = L':';
888		append_id_w(wp, id);
889	}
890}
891
892/*
893 * Generate a text version of the ACL. The flags parameter controls
894 * the type and style of the generated ACL.
895 */
896char *
897archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
898    struct archive_string_conv *sc)
899{
900	int count;
901	ssize_t length;
902	size_t len;
903	const char *name;
904	const char *prefix;
905	char separator;
906	struct archive_acl_entry *ap;
907	int id, r, want_type;
908	char *p, *s;
909
910	want_type = archive_acl_text_want_type(acl, flags);
911
912	/* Both NFSv4 and POSIX.1 types found */
913	if (want_type == 0)
914		return (NULL);
915
916	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
917		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
918
919	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
920
921	if (length == 0)
922		return (NULL);
923
924	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
925		separator = ',';
926	else
927		separator = '\n';
928
929	/* Now, allocate the string and actually populate it. */
930	p = s = (char *)malloc(length * sizeof(char));
931	if (p == NULL) {
932		if (errno == ENOMEM)
933			__archive_errx(1, "No memory");
934		return (NULL);
935	}
936	count = 0;
937
938	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
939		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
940		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
941		    acl->mode & 0700, -1);
942		*p++ = separator;
943		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
944		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
945		    acl->mode & 0070, -1);
946		*p++ = separator;
947		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
948		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
949		    acl->mode & 0007, -1);
950		count += 3;
951	}
952
953	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
954		if ((ap->type & want_type) == 0)
955			continue;
956		/*
957		 * Filemode-mapping ACL entries are stored exclusively in
958		 * ap->mode so they should not be in the list
959		 */
960		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
961		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
962		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
963		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
964			continue;
965		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
966		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
967			prefix = "default:";
968		else
969			prefix = NULL;
970		r = archive_mstring_get_mbs_l(
971		    &ap->name, &name, &len, sc);
972		if (r != 0) {
973			free(s);
974			return (NULL);
975		}
976		if (count > 0)
977			*p++ = separator;
978		if (name == NULL ||
979		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
980			id = ap->id;
981		} else {
982			id = -1;
983		}
984		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
985		    ap->permset, id);
986		count++;
987	}
988
989	/* Add terminating character */
990	*p++ = '\0';
991
992	len = strlen(s);
993
994	if ((ssize_t)len > (length - 1))
995		__archive_errx(1, "Buffer overrun");
996
997	if (text_len != NULL)
998		*text_len = len;
999
1000	return (s);
1001}
1002
1003static void
1004append_id(char **p, int id)
1005{
1006	if (id < 0)
1007		id = 0;
1008	if (id > 9)
1009		append_id(p, id / 10);
1010	*(*p)++ = "0123456789"[id % 10];
1011}
1012
1013static void
1014append_entry(char **p, const char *prefix, int type,
1015    int tag, int flags, const char *name, int perm, int id)
1016{
1017	int i;
1018
1019	if (prefix != NULL) {
1020		strcpy(*p, prefix);
1021		*p += strlen(*p);
1022	}
1023	switch (tag) {
1024	case ARCHIVE_ENTRY_ACL_USER_OBJ:
1025		name = NULL;
1026		id = -1;
1027		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1028			strcpy(*p, "owner@");
1029			break;
1030		}
1031		/* FALLTHROUGH */
1032	case ARCHIVE_ENTRY_ACL_USER:
1033		strcpy(*p, "user");
1034		break;
1035	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1036		name = NULL;
1037		id = -1;
1038		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1039			strcpy(*p, "group@");
1040			break;
1041		}
1042		/* FALLTHROUGH */
1043	case ARCHIVE_ENTRY_ACL_GROUP:
1044		strcpy(*p, "group");
1045		break;
1046	case ARCHIVE_ENTRY_ACL_MASK:
1047		strcpy(*p, "mask");
1048		name = NULL;
1049		id = -1;
1050		break;
1051	case ARCHIVE_ENTRY_ACL_OTHER:
1052		strcpy(*p, "other");
1053		name = NULL;
1054		id = -1;
1055		break;
1056	case ARCHIVE_ENTRY_ACL_EVERYONE:
1057		strcpy(*p, "everyone@");
1058		name = NULL;
1059		id = -1;
1060		break;
1061	}
1062	*p += strlen(*p);
1063	*(*p)++ = ':';
1064	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1065	    tag == ARCHIVE_ENTRY_ACL_USER ||
1066	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1067		if (name != NULL) {
1068			strcpy(*p, name);
1069			*p += strlen(*p);
1070		} else if (tag == ARCHIVE_ENTRY_ACL_USER
1071		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1072			append_id(p, id);
1073			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1074				id = -1;
1075		}
1076		/* Solaris style has no second colon after other and mask */
1077		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1078		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
1079		    && tag != ARCHIVE_ENTRY_ACL_MASK))
1080			*(*p)++ = ':';
1081	}
1082	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1083		/* POSIX.1e ACL perms */
1084		*(*p)++ = (perm & 0444) ? 'r' : '-';
1085		*(*p)++ = (perm & 0222) ? 'w' : '-';
1086		*(*p)++ = (perm & 0111) ? 'x' : '-';
1087	} else {
1088		/* NFSv4 ACL perms */
1089		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1090			if (perm & nfsv4_acl_perm_map[i].perm)
1091				*(*p)++ = nfsv4_acl_perm_map[i].c;
1092			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1093				*(*p)++ = '-';
1094		}
1095		*(*p)++ = ':';
1096		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1097			if (perm & nfsv4_acl_flag_map[i].perm)
1098				*(*p)++ = nfsv4_acl_flag_map[i].c;
1099			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1100				*(*p)++ = '-';
1101		}
1102		*(*p)++ = ':';
1103		switch (type) {
1104		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1105			strcpy(*p, "allow");
1106			break;
1107		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1108			strcpy(*p, "deny");
1109			break;
1110		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1111			strcpy(*p, "audit");
1112			break;
1113		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1114			strcpy(*p, "alarm");
1115			break;
1116		}
1117		*p += strlen(*p);
1118	}
1119	if (id != -1) {
1120		*(*p)++ = ':';
1121		append_id(p, id);
1122	}
1123}
1124
1125/*
1126 * Parse a wide ACL text string.
1127 *
1128 * The want_type argument may be one of the following:
1129 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1130 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1131 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1132 *
1133 * POSIX.1e ACL entries prefixed with "default:" are treated as
1134 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1135 */
1136int
1137archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1138    int want_type)
1139{
1140	struct {
1141		const wchar_t *start;
1142		const wchar_t *end;
1143	} field[6], name;
1144
1145	const wchar_t *s, *st;
1146
1147	int numfields, fields, n, r, sol, ret;
1148	int type, types, tag, permset, id;
1149	size_t len;
1150	wchar_t sep;
1151
1152	ret = ARCHIVE_OK;
1153	types = 0;
1154
1155	switch (want_type) {
1156	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1157		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1158		__LA_FALLTHROUGH;
1159	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1160	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1161		numfields = 5;
1162		break;
1163	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1164		numfields = 6;
1165		break;
1166	default:
1167		return (ARCHIVE_FATAL);
1168	}
1169
1170	while (text != NULL && *text != L'\0') {
1171		/*
1172		 * Parse the fields out of the next entry,
1173		 * advance 'text' to start of next entry.
1174		 */
1175		fields = 0;
1176		do {
1177			const wchar_t *start, *end;
1178			next_field_w(&text, &start, &end, &sep);
1179			if (fields < numfields) {
1180				field[fields].start = start;
1181				field[fields].end = end;
1182			}
1183			++fields;
1184		} while (sep == L':');
1185
1186		/* Set remaining fields to blank. */
1187		for (n = fields; n < numfields; ++n)
1188			field[n].start = field[n].end = NULL;
1189
1190		if (field[0].start != NULL && *(field[0].start) == L'#') {
1191			/* Comment, skip entry */
1192			continue;
1193		}
1194
1195		n = 0;
1196		sol = 0;
1197		id = -1;
1198		permset = 0;
1199		name.start = name.end = NULL;
1200
1201		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1202			/* POSIX.1e ACLs */
1203			/*
1204			 * Default keyword "default:user::rwx"
1205			 * if found, we have one more field
1206			 *
1207			 * We also support old Solaris extension:
1208			 * "defaultuser::rwx" is the default ACL corresponding
1209			 * to "user::rwx", etc. valid only for first field
1210			 */
1211			s = field[0].start;
1212			len = field[0].end - field[0].start;
1213			if (*s == L'd' && (len == 1 || (len >= 7
1214			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
1215				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1216				if (len > 7)
1217					field[0].start += 7;
1218				else
1219					n = 1;
1220			} else
1221				type = want_type;
1222
1223			/* Check for a numeric ID in field n+1 or n+3. */
1224			isint_w(field[n + 1].start, field[n + 1].end, &id);
1225			/* Field n+3 is optional. */
1226			if (id == -1 && fields > n+3)
1227				isint_w(field[n + 3].start, field[n + 3].end,
1228				    &id);
1229
1230			tag = 0;
1231			s = field[n].start;
1232			st = field[n].start + 1;
1233			len = field[n].end - field[n].start;
1234
1235			switch (*s) {
1236			case L'u':
1237				if (len == 1 || (len == 4
1238				    && wmemcmp(st, L"ser", 3) == 0))
1239					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1240				break;
1241			case L'g':
1242				if (len == 1 || (len == 5
1243				    && wmemcmp(st, L"roup", 4) == 0))
1244					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1245				break;
1246			case L'o':
1247				if (len == 1 || (len == 5
1248				    && wmemcmp(st, L"ther", 4) == 0))
1249					tag = ARCHIVE_ENTRY_ACL_OTHER;
1250				break;
1251			case L'm':
1252				if (len == 1 || (len == 4
1253				    && wmemcmp(st, L"ask", 3) == 0))
1254					tag = ARCHIVE_ENTRY_ACL_MASK;
1255				break;
1256			default:
1257					break;
1258			}
1259
1260			switch (tag) {
1261			case ARCHIVE_ENTRY_ACL_OTHER:
1262			case ARCHIVE_ENTRY_ACL_MASK:
1263				if (fields == (n + 2)
1264				    && field[n + 1].start < field[n + 1].end
1265				    && ismode_w(field[n + 1].start,
1266				    field[n + 1].end, &permset)) {
1267					/* This is Solaris-style "other:rwx" */
1268					sol = 1;
1269				} else if (fields == (n + 3) &&
1270				    field[n + 1].start < field[n + 1].end) {
1271					/* Invalid mask or other field */
1272					ret = ARCHIVE_WARN;
1273					continue;
1274				}
1275				break;
1276			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1277			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1278				if (id != -1 ||
1279				    field[n + 1].start < field[n + 1].end) {
1280					name = field[n + 1];
1281					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1282						tag = ARCHIVE_ENTRY_ACL_USER;
1283					else
1284						tag = ARCHIVE_ENTRY_ACL_GROUP;
1285				}
1286				break;
1287			default:
1288				/* Invalid tag, skip entry */
1289				ret = ARCHIVE_WARN;
1290				continue;
1291			}
1292
1293			/*
1294			 * Without "default:" we expect mode in field 2
1295			 * Exception: Solaris other and mask fields
1296			 */
1297			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1298			    field[n + 2 - sol].end, &permset)) {
1299				/* Invalid mode, skip entry */
1300				ret = ARCHIVE_WARN;
1301				continue;
1302			}
1303		} else {
1304			/* NFS4 ACLs */
1305			s = field[0].start;
1306			len = field[0].end - field[0].start;
1307			tag = 0;
1308
1309			switch (len) {
1310			case 4:
1311				if (wmemcmp(s, L"user", 4) == 0)
1312					tag = ARCHIVE_ENTRY_ACL_USER;
1313				break;
1314			case 5:
1315				if (wmemcmp(s, L"group", 5) == 0)
1316					tag = ARCHIVE_ENTRY_ACL_GROUP;
1317				break;
1318			case 6:
1319				if (wmemcmp(s, L"owner@", 6) == 0)
1320					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1321				else if (wmemcmp(s, L"group@", len) == 0)
1322					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1323				break;
1324			case 9:
1325				if (wmemcmp(s, L"everyone@", 9) == 0)
1326					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1327			default:
1328				break;
1329			}
1330
1331			if (tag == 0) {
1332				/* Invalid tag, skip entry */
1333				ret = ARCHIVE_WARN;
1334				continue;
1335			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1336			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1337				n = 1;
1338				name = field[1];
1339				isint_w(name.start, name.end, &id);
1340			} else
1341				n = 0;
1342
1343			if (!is_nfs4_perms_w(field[1 + n].start,
1344			    field[1 + n].end, &permset)) {
1345				/* Invalid NFSv4 perms, skip entry */
1346				ret = ARCHIVE_WARN;
1347				continue;
1348			}
1349			if (!is_nfs4_flags_w(field[2 + n].start,
1350			    field[2 + n].end, &permset)) {
1351				/* Invalid NFSv4 flags, skip entry */
1352				ret = ARCHIVE_WARN;
1353				continue;
1354			}
1355			s = field[3 + n].start;
1356			len = field[3 + n].end - field[3 + n].start;
1357			type = 0;
1358			if (len == 4) {
1359				if (wmemcmp(s, L"deny", 4) == 0)
1360					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1361			} else if (len == 5) {
1362				if (wmemcmp(s, L"allow", 5) == 0)
1363					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1364				else if (wmemcmp(s, L"audit", 5) == 0)
1365					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1366				else if (wmemcmp(s, L"alarm", 5) == 0)
1367					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1368			}
1369			if (type == 0) {
1370				/* Invalid entry type, skip entry */
1371				ret = ARCHIVE_WARN;
1372				continue;
1373			}
1374			isint_w(field[4 + n].start, field[4 + n].end, &id);
1375		}
1376
1377		/* Add entry to the internal list. */
1378		r = archive_acl_add_entry_w_len(acl, type, permset,
1379		    tag, id, name.start, name.end - name.start);
1380		if (r < ARCHIVE_WARN)
1381			return (r);
1382		if (r != ARCHIVE_OK)
1383			ret = ARCHIVE_WARN;
1384		types |= type;
1385	}
1386
1387	/* Reset ACL */
1388	archive_acl_reset(acl, types);
1389
1390	return (ret);
1391}
1392
1393/*
1394 * Parse a string to a positive decimal integer.  Returns true if
1395 * the string is non-empty and consists only of decimal digits,
1396 * false otherwise.
1397 */
1398static int
1399isint_w(const wchar_t *start, const wchar_t *end, int *result)
1400{
1401	int n = 0;
1402	if (start >= end)
1403		return (0);
1404	while (start < end) {
1405		if (*start < '0' || *start > '9')
1406			return (0);
1407		if (n > (INT_MAX / 10) ||
1408		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1409			n = INT_MAX;
1410		} else {
1411			n *= 10;
1412			n += *start - '0';
1413		}
1414		start++;
1415	}
1416	*result = n;
1417	return (1);
1418}
1419
1420/*
1421 * Parse a string as a mode field.  Returns true if
1422 * the string is non-empty and consists only of mode characters,
1423 * false otherwise.
1424 */
1425static int
1426ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1427{
1428	const wchar_t *p;
1429
1430	if (start >= end)
1431		return (0);
1432	p = start;
1433	*permset = 0;
1434	while (p < end) {
1435		switch (*p++) {
1436		case L'r': case L'R':
1437			*permset |= ARCHIVE_ENTRY_ACL_READ;
1438			break;
1439		case L'w': case L'W':
1440			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1441			break;
1442		case L'x': case L'X':
1443			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1444			break;
1445		case L'-':
1446			break;
1447		default:
1448			return (0);
1449		}
1450	}
1451	return (1);
1452}
1453
1454/*
1455 * Parse a string as a NFS4 ACL permission field.
1456 * Returns true if the string is non-empty and consists only of NFS4 ACL
1457 * permission characters, false otherwise
1458 */
1459static int
1460is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1461{
1462	const wchar_t *p = start;
1463
1464	while (p < end) {
1465		switch (*p++) {
1466		case L'r':
1467			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1468			break;
1469		case L'w':
1470			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1471			break;
1472		case L'x':
1473			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1474			break;
1475		case L'p':
1476			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1477			break;
1478		case L'D':
1479			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1480			break;
1481		case L'd':
1482			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1483			break;
1484		case L'a':
1485			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1486			break;
1487		case L'A':
1488			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1489			break;
1490		case L'R':
1491			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1492			break;
1493		case L'W':
1494			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1495			break;
1496		case L'c':
1497			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1498			break;
1499		case L'C':
1500			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1501			break;
1502		case L'o':
1503			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1504			break;
1505		case L's':
1506			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1507			break;
1508		case L'-':
1509			break;
1510		default:
1511			return(0);
1512		}
1513	}
1514	return (1);
1515}
1516
1517/*
1518 * Parse a string as a NFS4 ACL flags field.
1519 * Returns true if the string is non-empty and consists only of NFS4 ACL
1520 * flag characters, false otherwise
1521 */
1522static int
1523is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1524{
1525	const wchar_t *p = start;
1526
1527	while (p < end) {
1528		switch(*p++) {
1529		case L'f':
1530			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1531			break;
1532		case L'd':
1533			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1534			break;
1535		case L'i':
1536			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1537			break;
1538		case L'n':
1539			*permset |=
1540			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1541			break;
1542		case L'S':
1543			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1544			break;
1545		case L'F':
1546			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1547			break;
1548		case L'I':
1549			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1550			break;
1551		case L'-':
1552			break;
1553		default:
1554			return (0);
1555		}
1556	}
1557	return (1);
1558}
1559
1560/*
1561 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1562 * to point to just after the separator.  *start points to the first
1563 * character of the matched text and *end just after the last
1564 * character of the matched identifier.  In particular *end - *start
1565 * is the length of the field body, not including leading or trailing
1566 * whitespace.
1567 */
1568static void
1569next_field_w(const wchar_t **wp, const wchar_t **start,
1570    const wchar_t **end, wchar_t *sep)
1571{
1572	/* Skip leading whitespace to find start of field. */
1573	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1574		(*wp)++;
1575	}
1576	*start = *wp;
1577
1578	/* Scan for the separator. */
1579	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1580	    **wp != L'\n' && **wp != L'#') {
1581		(*wp)++;
1582	}
1583	*sep = **wp;
1584
1585	/* Locate end of field, trim trailing whitespace if necessary */
1586	if (*wp == *start) {
1587		*end = *wp;
1588	} else {
1589		*end = *wp - 1;
1590		while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1591			(*end)--;
1592		}
1593		(*end)++;
1594	}
1595
1596	/* Handle in-field comments */
1597	if (*sep == L'#') {
1598		while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
1599			(*wp)++;
1600		}
1601		*sep = **wp;
1602	}
1603
1604	/* Adjust scanner location. */
1605	if (**wp != L'\0')
1606		(*wp)++;
1607}
1608
1609/*
1610 * Parse an ACL text string.
1611 *
1612 * The want_type argument may be one of the following:
1613 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1614 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1615 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1616 *
1617 * POSIX.1e ACL entries prefixed with "default:" are treated as
1618 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1619 */
1620int
1621archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1622    int want_type, struct archive_string_conv *sc)
1623{
1624	struct {
1625		const char *start;
1626		const char *end;
1627	} field[6], name;
1628
1629	const char *s, *st;
1630	int numfields, fields, n, r, sol, ret;
1631	int type, types, tag, permset, id;
1632	size_t len;
1633	char sep;
1634
1635	switch (want_type) {
1636	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1637		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1638		__LA_FALLTHROUGH;
1639	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1640	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1641		numfields = 5;
1642		break;
1643	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1644		numfields = 6;
1645		break;
1646	default:
1647		return (ARCHIVE_FATAL);
1648	}
1649
1650	ret = ARCHIVE_OK;
1651	types = 0;
1652
1653	while (text != NULL &&  *text != '\0') {
1654		/*
1655		 * Parse the fields out of the next entry,
1656		 * advance 'text' to start of next entry.
1657		 */
1658		fields = 0;
1659		do {
1660			const char *start, *end;
1661			next_field(&text, &start, &end, &sep);
1662			if (fields < numfields) {
1663				field[fields].start = start;
1664				field[fields].end = end;
1665			}
1666			++fields;
1667		} while (sep == ':');
1668
1669		/* Set remaining fields to blank. */
1670		for (n = fields; n < numfields; ++n)
1671			field[n].start = field[n].end = NULL;
1672
1673		if (field[0].start != NULL && *(field[0].start) == '#') {
1674			/* Comment, skip entry */
1675			continue;
1676		}
1677
1678		n = 0;
1679		sol = 0;
1680		id = -1;
1681		permset = 0;
1682		name.start = name.end = NULL;
1683
1684		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1685			/* POSIX.1e ACLs */
1686			/*
1687			 * Default keyword "default:user::rwx"
1688			 * if found, we have one more field
1689			 *
1690			 * We also support old Solaris extension:
1691			 * "defaultuser::rwx" is the default ACL corresponding
1692			 * to "user::rwx", etc. valid only for first field
1693			 */
1694			s = field[0].start;
1695			len = field[0].end - field[0].start;
1696			if (*s == 'd' && (len == 1 || (len >= 7
1697			    && memcmp((s + 1), "efault", 6) == 0))) {
1698				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1699				if (len > 7)
1700					field[0].start += 7;
1701				else
1702					n = 1;
1703			} else
1704				type = want_type;
1705
1706			/* Check for a numeric ID in field n+1 or n+3. */
1707			isint(field[n + 1].start, field[n + 1].end, &id);
1708			/* Field n+3 is optional. */
1709			if (id == -1 && fields > (n + 3))
1710				isint(field[n + 3].start, field[n + 3].end,
1711				    &id);
1712
1713			tag = 0;
1714			s = field[n].start;
1715			st = field[n].start + 1;
1716			len = field[n].end - field[n].start;
1717
1718			if (len == 0) {
1719				ret = ARCHIVE_WARN;
1720				continue;
1721			}
1722
1723			switch (*s) {
1724			case 'u':
1725				if (len == 1 || (len == 4
1726				    && memcmp(st, "ser", 3) == 0))
1727					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1728				break;
1729			case 'g':
1730				if (len == 1 || (len == 5
1731				    && memcmp(st, "roup", 4) == 0))
1732					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1733				break;
1734			case 'o':
1735				if (len == 1 || (len == 5
1736				    && memcmp(st, "ther", 4) == 0))
1737					tag = ARCHIVE_ENTRY_ACL_OTHER;
1738				break;
1739			case 'm':
1740				if (len == 1 || (len == 4
1741				    && memcmp(st, "ask", 3) == 0))
1742					tag = ARCHIVE_ENTRY_ACL_MASK;
1743				break;
1744			default:
1745					break;
1746			}
1747
1748			switch (tag) {
1749			case ARCHIVE_ENTRY_ACL_OTHER:
1750			case ARCHIVE_ENTRY_ACL_MASK:
1751				if (fields == (n + 2)
1752				    && field[n + 1].start < field[n + 1].end
1753				    && ismode(field[n + 1].start,
1754				    field[n + 1].end, &permset)) {
1755					/* This is Solaris-style "other:rwx" */
1756					sol = 1;
1757				} else if (fields == (n + 3) &&
1758				    field[n + 1].start < field[n + 1].end) {
1759					/* Invalid mask or other field */
1760					ret = ARCHIVE_WARN;
1761					continue;
1762				}
1763				break;
1764			case ARCHIVE_ENTRY_ACL_USER_OBJ:
1765			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1766				if (id != -1 ||
1767				    field[n + 1].start < field[n + 1].end) {
1768					name = field[n + 1];
1769					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1770						tag = ARCHIVE_ENTRY_ACL_USER;
1771					else
1772						tag = ARCHIVE_ENTRY_ACL_GROUP;
1773				}
1774				break;
1775			default:
1776				/* Invalid tag, skip entry */
1777				ret = ARCHIVE_WARN;
1778				continue;
1779			}
1780
1781			/*
1782			 * Without "default:" we expect mode in field 3
1783			 * Exception: Solaris other and mask fields
1784			 */
1785			if (permset == 0 && !ismode(field[n + 2 - sol].start,
1786			    field[n + 2 - sol].end, &permset)) {
1787				/* Invalid mode, skip entry */
1788				ret = ARCHIVE_WARN;
1789				continue;
1790			}
1791		} else {
1792			/* NFS4 ACLs */
1793			s = field[0].start;
1794			len = field[0].end - field[0].start;
1795			tag = 0;
1796
1797			switch (len) {
1798			case 4:
1799				if (memcmp(s, "user", 4) == 0)
1800					tag = ARCHIVE_ENTRY_ACL_USER;
1801				break;
1802			case 5:
1803				if (memcmp(s, "group", 5) == 0)
1804					tag = ARCHIVE_ENTRY_ACL_GROUP;
1805				break;
1806			case 6:
1807				if (memcmp(s, "owner@", 6) == 0)
1808					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1809				else if (memcmp(s, "group@", 6) == 0)
1810					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1811				break;
1812			case 9:
1813				if (memcmp(s, "everyone@", 9) == 0)
1814					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1815				break;
1816			default:
1817				break;
1818			}
1819
1820			if (tag == 0) {
1821				/* Invalid tag, skip entry */
1822				ret = ARCHIVE_WARN;
1823				continue;
1824			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1825			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
1826				n = 1;
1827				name = field[1];
1828				isint(name.start, name.end, &id);
1829			} else
1830				n = 0;
1831
1832			if (!is_nfs4_perms(field[1 + n].start,
1833			    field[1 + n].end, &permset)) {
1834				/* Invalid NFSv4 perms, skip entry */
1835				ret = ARCHIVE_WARN;
1836				continue;
1837			}
1838			if (!is_nfs4_flags(field[2 + n].start,
1839			    field[2 + n].end, &permset)) {
1840				/* Invalid NFSv4 flags, skip entry */
1841				ret = ARCHIVE_WARN;
1842				continue;
1843			}
1844			s = field[3 + n].start;
1845			len = field[3 + n].end - field[3 + n].start;
1846			type = 0;
1847			if (len == 4) {
1848				if (memcmp(s, "deny", 4) == 0)
1849					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1850			} else if (len == 5) {
1851				if (memcmp(s, "allow", 5) == 0)
1852					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1853				else if (memcmp(s, "audit", 5) == 0)
1854					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1855				else if (memcmp(s, "alarm", 5) == 0)
1856					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1857			}
1858			if (type == 0) {
1859				/* Invalid entry type, skip entry */
1860				ret = ARCHIVE_WARN;
1861				continue;
1862			}
1863			isint(field[4 + n].start, field[4 + n].end,
1864			    &id);
1865		}
1866
1867		/* Add entry to the internal list. */
1868		r = archive_acl_add_entry_len_l(acl, type, permset,
1869		    tag, id, name.start, name.end - name.start, sc);
1870		if (r < ARCHIVE_WARN)
1871			return (r);
1872		if (r != ARCHIVE_OK)
1873			ret = ARCHIVE_WARN;
1874		types |= type;
1875	}
1876
1877	/* Reset ACL */
1878	archive_acl_reset(acl, types);
1879
1880	return (ret);
1881}
1882
1883/*
1884 * Parse a string to a positive decimal integer.  Returns true if
1885 * the string is non-empty and consists only of decimal digits,
1886 * false otherwise.
1887 */
1888static int
1889isint(const char *start, const char *end, int *result)
1890{
1891	int n = 0;
1892	if (start >= end)
1893		return (0);
1894	while (start < end) {
1895		if (*start < '0' || *start > '9')
1896			return (0);
1897		if (n > (INT_MAX / 10) ||
1898		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1899			n = INT_MAX;
1900		} else {
1901			n *= 10;
1902			n += *start - '0';
1903		}
1904		start++;
1905	}
1906	*result = n;
1907	return (1);
1908}
1909
1910/*
1911 * Parse a string as a mode field.  Returns true if
1912 * the string is non-empty and consists only of mode characters,
1913 * false otherwise.
1914 */
1915static int
1916ismode(const char *start, const char *end, int *permset)
1917{
1918	const char *p;
1919
1920	if (start >= end)
1921		return (0);
1922	p = start;
1923	*permset = 0;
1924	while (p < end) {
1925		switch (*p++) {
1926		case 'r': case 'R':
1927			*permset |= ARCHIVE_ENTRY_ACL_READ;
1928			break;
1929		case 'w': case 'W':
1930			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1931			break;
1932		case 'x': case 'X':
1933			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1934			break;
1935		case '-':
1936			break;
1937		default:
1938			return (0);
1939		}
1940	}
1941	return (1);
1942}
1943
1944/*
1945 * Parse a string as a NFS4 ACL permission field.
1946 * Returns true if the string is non-empty and consists only of NFS4 ACL
1947 * permission characters, false otherwise
1948 */
1949static int
1950is_nfs4_perms(const char *start, const char *end, int *permset)
1951{
1952	const char *p = start;
1953
1954	while (p < end) {
1955		switch (*p++) {
1956		case 'r':
1957			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1958			break;
1959		case 'w':
1960			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1961			break;
1962		case 'x':
1963			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1964			break;
1965		case 'p':
1966			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1967			break;
1968		case 'D':
1969			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1970			break;
1971		case 'd':
1972			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
1973			break;
1974		case 'a':
1975			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1976			break;
1977		case 'A':
1978			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1979			break;
1980		case 'R':
1981			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1982			break;
1983		case 'W':
1984			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1985			break;
1986		case 'c':
1987			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1988			break;
1989		case 'C':
1990			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1991			break;
1992		case 'o':
1993			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1994			break;
1995		case 's':
1996			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1997			break;
1998		case '-':
1999			break;
2000		default:
2001			return(0);
2002		}
2003	}
2004	return (1);
2005}
2006
2007/*
2008 * Parse a string as a NFS4 ACL flags field.
2009 * Returns true if the string is non-empty and consists only of NFS4 ACL
2010 * flag characters, false otherwise
2011 */
2012static int
2013is_nfs4_flags(const char *start, const char *end, int *permset)
2014{
2015	const char *p = start;
2016
2017	while (p < end) {
2018		switch(*p++) {
2019		case 'f':
2020			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2021			break;
2022		case 'd':
2023			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2024			break;
2025		case 'i':
2026			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2027			break;
2028		case 'n':
2029			*permset |=
2030			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2031			break;
2032		case 'S':
2033			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2034			break;
2035		case 'F':
2036			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2037			break;
2038		case 'I':
2039			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2040			break;
2041		case '-':
2042			break;
2043		default:
2044			return (0);
2045		}
2046	}
2047	return (1);
2048}
2049
2050/*
2051 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2052 * to point to just after the separator.  *start points to the first
2053 * character of the matched text and *end just after the last
2054 * character of the matched identifier.  In particular *end - *start
2055 * is the length of the field body, not including leading or trailing
2056 * whitespace.
2057 */
2058static void
2059next_field(const char **p, const char **start,
2060    const char **end, char *sep)
2061{
2062	/* Skip leading whitespace to find start of field. */
2063	while (**p == ' ' || **p == '\t' || **p == '\n') {
2064		(*p)++;
2065	}
2066	*start = *p;
2067
2068	/* Scan for the separator. */
2069	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
2070	    **p != '#') {
2071		(*p)++;
2072	}
2073	*sep = **p;
2074
2075	/* Locate end of field, trim trailing whitespace if necessary */
2076	if (*p == *start) {
2077		*end = *p;
2078	} else {
2079		*end = *p - 1;
2080		while (**end == ' ' || **end == '\t' || **end == '\n') {
2081			(*end)--;
2082		}
2083		(*end)++;
2084	}
2085
2086	/* Handle in-field comments */
2087	if (*sep == '#') {
2088		while (**p != '\0' && **p != ',' && **p != '\n') {
2089			(*p)++;
2090		}
2091		*sep = **p;
2092	}
2093
2094	/* Adjust scanner location. */
2095	if (**p != '\0')
2096		(*p)++;
2097}
2098