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