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