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