1 /*-
2  * Copyright (c) 2003-2010 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
28 
29 #ifdef HAVE_ERRNO_H
30 #include <errno.h>
31 #endif
32 #ifdef HAVE_LIMITS_H
33 #include <limits.h>
34 #endif
35 #ifdef HAVE_WCHAR_H
36 #include <wchar.h>
37 #endif
38 
39 #include "archive_acl_private.h"
40 #include "archive_entry.h"
41 #include "archive_private.h"
42 
43 #undef max
44 #define	max(a, b)	((a)>(b)?(a):(b))
45 
46 #ifndef HAVE_WMEMCMP
47 /* Good enough for simple equality testing, but not for sorting. */
48 #define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
49 #endif
50 
51 static int	acl_special(struct archive_acl *acl,
52 		    int type, int permset, int tag);
53 static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
54 		    int type, int permset, int tag, int id);
55 static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
56 static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
57 static void	next_field_w(const wchar_t **wp, const wchar_t **start,
58 		    const wchar_t **end, wchar_t *sep);
59 static int	prefix_w(const wchar_t *start, const wchar_t *end,
60 		    const wchar_t *test);
61 static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
62 		    const wchar_t *wname, int perm, int id);
63 static void	append_id_w(wchar_t **wp, int id);
64 static int	isint(const char *start, const char *end, int *result);
65 static int	ismode(const char *start, const char *end, int *result);
66 static void	next_field(const char **p, const char **start,
67 		    const char **end, char *sep);
68 static int	prefix(const char *start, const char *end,
69 		    const char *test);
70 static void	append_entry(char **p, const char *prefix, int tag,
71 		    const char *name, int perm, int id);
72 static void	append_id(char **p, int id);
73 
74 void
75 archive_acl_clear(struct archive_acl *acl)
76 {
77 	struct archive_acl_entry *ap;
78 
79 	while (acl->acl_head != NULL) {
80 		ap = acl->acl_head->next;
81 		archive_mstring_clean(&acl->acl_head->name);
82 		free(acl->acl_head);
83 		acl->acl_head = ap;
84 	}
85 	if (acl->acl_text_w != NULL) {
86 		free(acl->acl_text_w);
87 		acl->acl_text_w = NULL;
88 	}
89 	if (acl->acl_text != NULL) {
90 		free(acl->acl_text);
91 		acl->acl_text = NULL;
92 	}
93 	acl->acl_p = NULL;
94 	acl->acl_state = 0; /* Not counting. */
95 }
96 
97 void
98 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
99 {
100 	struct archive_acl_entry *ap, *ap2;
101 
102 	archive_acl_clear(dest);
103 
104 	dest->mode = src->mode;
105 	ap = src->acl_head;
106 	while (ap != NULL) {
107 		ap2 = acl_new_entry(dest,
108 		    ap->type, ap->permset, ap->tag, ap->id);
109 		if (ap2 != NULL)
110 			archive_mstring_copy(&ap2->name, &ap->name);
111 		ap = ap->next;
112 	}
113 }
114 
115 int
116 archive_acl_add_entry(struct archive_acl *acl,
117     int type, int permset, int tag, int id, const char *name)
118 {
119 	struct archive_acl_entry *ap;
120 
121 	if (acl_special(acl, type, permset, tag) == 0)
122 		return ARCHIVE_OK;
123 	ap = acl_new_entry(acl, type, permset, tag, id);
124 	if (ap == NULL) {
125 		/* XXX Error XXX */
126 		return ARCHIVE_FAILED;
127 	}
128 	if (name != NULL  &&  *name != '\0')
129 		archive_mstring_copy_mbs(&ap->name, name);
130 	else
131 		archive_mstring_clean(&ap->name);
132 	return ARCHIVE_OK;
133 }
134 
135 int
136 archive_acl_add_entry_w_len(struct archive_acl *acl,
137     int type, int permset, int tag, int id, const wchar_t *name, size_t len)
138 {
139 	struct archive_acl_entry *ap;
140 
141 	if (acl_special(acl, type, permset, tag) == 0)
142 		return ARCHIVE_OK;
143 	ap = acl_new_entry(acl, type, permset, tag, id);
144 	if (ap == NULL) {
145 		/* XXX Error XXX */
146 		return ARCHIVE_FAILED;
147 	}
148 	if (name != NULL  &&  *name != L'\0' && len > 0)
149 		archive_mstring_copy_wcs_len(&ap->name, name, len);
150 	else
151 		archive_mstring_clean(&ap->name);
152 	return ARCHIVE_OK;
153 }
154 
155 int
156 archive_acl_add_entry_len_l(struct archive_acl *acl,
157     int type, int permset, int tag, int id, const char *name, size_t len,
158     struct archive_string_conv *sc)
159 {
160 	struct archive_acl_entry *ap;
161 	int r;
162 
163 	if (acl_special(acl, type, permset, tag) == 0)
164 		return ARCHIVE_OK;
165 	ap = acl_new_entry(acl, type, permset, tag, id);
166 	if (ap == NULL) {
167 		/* XXX Error XXX */
168 		return ARCHIVE_FAILED;
169 	}
170 	if (name != NULL  &&  *name != '\0' && len > 0) {
171 		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
172 	} else {
173 		r = 0;
174 		archive_mstring_clean(&ap->name);
175 	}
176 	if (r == 0)
177 		return (ARCHIVE_OK);
178 	else if (errno == ENOMEM)
179 		return (ARCHIVE_FATAL);
180 	else
181 		return (ARCHIVE_WARN);
182 }
183 
184 /*
185  * If this ACL entry is part of the standard POSIX permissions set,
186  * store the permissions in the stat structure and return zero.
187  */
188 static int
189 acl_special(struct archive_acl *acl, int type, int permset, int tag)
190 {
191 	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
192 	    && ((permset & ~007) == 0)) {
193 		switch (tag) {
194 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
195 			acl->mode &= ~0700;
196 			acl->mode |= (permset & 7) << 6;
197 			return (0);
198 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
199 			acl->mode &= ~0070;
200 			acl->mode |= (permset & 7) << 3;
201 			return (0);
202 		case ARCHIVE_ENTRY_ACL_OTHER:
203 			acl->mode &= ~0007;
204 			acl->mode |= permset & 7;
205 			return (0);
206 		}
207 	}
208 	return (1);
209 }
210 
211 /*
212  * Allocate and populate a new ACL entry with everything but the
213  * name.
214  */
215 static struct archive_acl_entry *
216 acl_new_entry(struct archive_acl *acl,
217     int type, int permset, int tag, int id)
218 {
219 	struct archive_acl_entry *ap, *aq;
220 
221 	/* Type argument must be a valid NFS4 or POSIX.1e type.
222 	 * The type must agree with anything already set and
223 	 * the permset must be compatible. */
224 	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
225 		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
226 			return (NULL);
227 		}
228 		if (permset &
229 		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
230 			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
231 			return (NULL);
232 		}
233 	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
234 		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
235 			return (NULL);
236 		}
237 		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
238 			return (NULL);
239 		}
240 	} else {
241 		return (NULL);
242 	}
243 
244 	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
245 	switch (tag) {
246 	case ARCHIVE_ENTRY_ACL_USER:
247 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
248 	case ARCHIVE_ENTRY_ACL_GROUP:
249 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
250 		/* Tags valid in both NFS4 and POSIX.1e */
251 		break;
252 	case ARCHIVE_ENTRY_ACL_MASK:
253 	case ARCHIVE_ENTRY_ACL_OTHER:
254 		/* Tags valid only in POSIX.1e. */
255 		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
256 			return (NULL);
257 		}
258 		break;
259 	case ARCHIVE_ENTRY_ACL_EVERYONE:
260 		/* Tags valid only in NFS4. */
261 		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
262 			return (NULL);
263 		}
264 		break;
265 	default:
266 		/* No other values are valid. */
267 		return (NULL);
268 	}
269 
270 	if (acl->acl_text_w != NULL) {
271 		free(acl->acl_text_w);
272 		acl->acl_text_w = NULL;
273 	}
274 	if (acl->acl_text != NULL) {
275 		free(acl->acl_text);
276 		acl->acl_text = NULL;
277 	}
278 
279 	/* If there's a matching entry already in the list, overwrite it. */
280 	ap = acl->acl_head;
281 	aq = NULL;
282 	while (ap != NULL) {
283 		if (ap->type == type && ap->tag == tag && ap->id == id) {
284 			ap->permset = permset;
285 			return (ap);
286 		}
287 		aq = ap;
288 		ap = ap->next;
289 	}
290 
291 	/* Add a new entry to the end of the list. */
292 	ap = (struct archive_acl_entry *)malloc(sizeof(*ap));
293 	if (ap == NULL)
294 		return (NULL);
295 	memset(ap, 0, sizeof(*ap));
296 	if (aq == NULL)
297 		acl->acl_head = ap;
298 	else
299 		aq->next = ap;
300 	ap->type = type;
301 	ap->tag = tag;
302 	ap->id = id;
303 	ap->permset = permset;
304 	acl->acl_types |= type;
305 	return (ap);
306 }
307 
308 /*
309  * Return a count of entries matching "want_type".
310  */
311 int
312 archive_acl_count(struct archive_acl *acl, int want_type)
313 {
314 	int count;
315 	struct archive_acl_entry *ap;
316 
317 	count = 0;
318 	ap = acl->acl_head;
319 	while (ap != NULL) {
320 		if ((ap->type & want_type) != 0)
321 			count++;
322 		ap = ap->next;
323 	}
324 
325 	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
326 		count += 3;
327 	return (count);
328 }
329 
330 /*
331  * Prepare for reading entries from the ACL data.  Returns a count
332  * of entries matching "want_type", or zero if there are no
333  * non-extended ACL entries of that type.
334  */
335 int
336 archive_acl_reset(struct archive_acl *acl, int want_type)
337 {
338 	int count, cutoff;
339 
340 	count = archive_acl_count(acl, want_type);
341 
342 	/*
343 	 * If the only entries are the three standard ones,
344 	 * then don't return any ACL data.  (In this case,
345 	 * client can just use chmod(2) to set permissions.)
346 	 */
347 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
348 		cutoff = 3;
349 	else
350 		cutoff = 0;
351 
352 	if (count > cutoff)
353 		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
354 	else
355 		acl->acl_state = 0;
356 	acl->acl_p = acl->acl_head;
357 	return (count);
358 }
359 
360 
361 /*
362  * Return the next ACL entry in the list.  Fake entries for the
363  * standard permissions and include them in the returned list.
364  */
365 int
366 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
367     int *permset, int *tag, int *id, const char **name)
368 {
369 	*name = NULL;
370 	*id = -1;
371 
372 	/*
373 	 * The acl_state is either zero (no entries available), -1
374 	 * (reading from list), or an entry type (retrieve that type
375 	 * from ae_stat.aest_mode).
376 	 */
377 	if (acl->acl_state == 0)
378 		return (ARCHIVE_WARN);
379 
380 	/* The first three access entries are special. */
381 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
382 		switch (acl->acl_state) {
383 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
384 			*permset = (acl->mode >> 6) & 7;
385 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
386 			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
387 			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
388 			return (ARCHIVE_OK);
389 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
390 			*permset = (acl->mode >> 3) & 7;
391 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
392 			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
393 			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
394 			return (ARCHIVE_OK);
395 		case ARCHIVE_ENTRY_ACL_OTHER:
396 			*permset = acl->mode & 7;
397 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
398 			*tag = ARCHIVE_ENTRY_ACL_OTHER;
399 			acl->acl_state = -1;
400 			acl->acl_p = acl->acl_head;
401 			return (ARCHIVE_OK);
402 		default:
403 			break;
404 		}
405 	}
406 
407 	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
408 		acl->acl_p = acl->acl_p->next;
409 	if (acl->acl_p == NULL) {
410 		acl->acl_state = 0;
411 		*type = 0;
412 		*permset = 0;
413 		*tag = 0;
414 		*id = -1;
415 		*name = NULL;
416 		return (ARCHIVE_EOF); /* End of ACL entries. */
417 	}
418 	*type = acl->acl_p->type;
419 	*permset = acl->acl_p->permset;
420 	*tag = acl->acl_p->tag;
421 	*id = acl->acl_p->id;
422 	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0)
423 		*name = NULL;
424 	acl->acl_p = acl->acl_p->next;
425 	return (ARCHIVE_OK);
426 }
427 
428 /*
429  * Generate a text version of the ACL.  The flags parameter controls
430  * the style of the generated ACL.
431  */
432 const wchar_t *
433 archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
434 {
435 	int count;
436 	size_t length;
437 	const wchar_t *wname;
438 	const wchar_t *prefix;
439 	wchar_t separator;
440 	struct archive_acl_entry *ap;
441 	int id;
442 	wchar_t *wp;
443 
444 	if (acl->acl_text_w != NULL) {
445 		free (acl->acl_text_w);
446 		acl->acl_text_w = NULL;
447 	}
448 
449 	separator = L',';
450 	count = 0;
451 	length = 0;
452 	ap = acl->acl_head;
453 	while (ap != NULL) {
454 		if ((ap->type & flags) != 0) {
455 			count++;
456 			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
457 			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
458 				length += 8; /* "default:" */
459 			length += 5; /* tag name */
460 			length += 1; /* colon */
461 			if (archive_mstring_get_wcs(a, &ap->name, &wname) == 0 &&
462 			    wname != NULL)
463 				length += wcslen(wname);
464 			else
465 				length += sizeof(uid_t) * 3 + 1;
466 			length ++; /* colon */
467 			length += 3; /* rwx */
468 			length += 1; /* colon */
469 			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
470 			length ++; /* newline */
471 		}
472 		ap = ap->next;
473 	}
474 
475 	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
476 		length += 10; /* "user::rwx\n" */
477 		length += 11; /* "group::rwx\n" */
478 		length += 11; /* "other::rwx\n" */
479 	}
480 
481 	if (count == 0)
482 		return (NULL);
483 
484 	/* Now, allocate the string and actually populate it. */
485 	wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
486 	if (wp == NULL)
487 		__archive_errx(1, "No memory to generate the text version of the ACL");
488 	count = 0;
489 	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
490 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
491 		    acl->mode & 0700, -1);
492 		*wp++ = ',';
493 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
494 		    acl->mode & 0070, -1);
495 		*wp++ = ',';
496 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
497 		    acl->mode & 0007, -1);
498 		count += 3;
499 
500 		ap = acl->acl_head;
501 		while (ap != NULL) {
502 			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 &&
503 				archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
504 				*wp++ = separator;
505 				if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
506 					id = ap->id;
507 				else
508 					id = -1;
509 				append_entry_w(&wp, NULL, ap->tag, wname,
510 				    ap->permset, id);
511 				count++;
512 			}
513 			ap = ap->next;
514 		}
515 	}
516 
517 
518 	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
519 		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
520 			prefix = L"default:";
521 		else
522 			prefix = NULL;
523 		ap = acl->acl_head;
524 		count = 0;
525 		while (ap != NULL) {
526 			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 &&
527 				archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
528 				if (count > 0)
529 					*wp++ = separator;
530 				if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
531 					id = ap->id;
532 				else
533 					id = -1;
534 				append_entry_w(&wp, prefix, ap->tag,
535 				    wname, ap->permset, id);
536 				count ++;
537 			}
538 			ap = ap->next;
539 		}
540 	}
541 
542 	return (acl->acl_text_w);
543 }
544 
545 
546 static void
547 append_id_w(wchar_t **wp, int id)
548 {
549 	if (id < 0)
550 		id = 0;
551 	if (id > 9)
552 		append_id_w(wp, id / 10);
553 	*(*wp)++ = L"0123456789"[id % 10];
554 }
555 
556 static void
557 append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
558     const wchar_t *wname, int perm, int id)
559 {
560 	if (prefix != NULL) {
561 		wcscpy(*wp, prefix);
562 		*wp += wcslen(*wp);
563 	}
564 	switch (tag) {
565 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
566 		wname = NULL;
567 		id = -1;
568 		/* FALLTHROUGH */
569 	case ARCHIVE_ENTRY_ACL_USER:
570 		wcscpy(*wp, L"user");
571 		break;
572 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
573 		wname = NULL;
574 		id = -1;
575 		/* FALLTHROUGH */
576 	case ARCHIVE_ENTRY_ACL_GROUP:
577 		wcscpy(*wp, L"group");
578 		break;
579 	case ARCHIVE_ENTRY_ACL_MASK:
580 		wcscpy(*wp, L"mask");
581 		wname = NULL;
582 		id = -1;
583 		break;
584 	case ARCHIVE_ENTRY_ACL_OTHER:
585 		wcscpy(*wp, L"other");
586 		wname = NULL;
587 		id = -1;
588 		break;
589 	}
590 	*wp += wcslen(*wp);
591 	*(*wp)++ = L':';
592 	if (wname != NULL) {
593 		wcscpy(*wp, wname);
594 		*wp += wcslen(*wp);
595 	} else if (tag == ARCHIVE_ENTRY_ACL_USER
596 	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
597 		append_id_w(wp, id);
598 		id = -1;
599 	}
600 	*(*wp)++ = L':';
601 	*(*wp)++ = (perm & 0444) ? L'r' : L'-';
602 	*(*wp)++ = (perm & 0222) ? L'w' : L'-';
603 	*(*wp)++ = (perm & 0111) ? L'x' : L'-';
604 	if (id != -1) {
605 		*(*wp)++ = L':';
606 		append_id_w(wp, id);
607 	}
608 	**wp = L'\0';
609 }
610 
611 int
612 archive_acl_text_l(struct archive_acl *acl, int flags,
613     const char **acl_text, size_t *acl_text_len,
614     struct archive_string_conv *sc)
615 {
616 	int count;
617 	size_t length;
618 	const char *name;
619 	const char *prefix;
620 	char separator;
621 	struct archive_acl_entry *ap;
622 	size_t len;
623 	int id, r;
624 	char *p;
625 
626 	if (acl->acl_text != NULL) {
627 		free (acl->acl_text);
628 		acl->acl_text = NULL;
629 	}
630 
631 	*acl_text = NULL;
632 	if (acl_text_len != NULL)
633 		*acl_text_len = 0;
634 	separator = ',';
635 	count = 0;
636 	length = 0;
637 	ap = acl->acl_head;
638 	while (ap != NULL) {
639 		if ((ap->type & flags) != 0) {
640 			count++;
641 			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
642 			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
643 				length += 8; /* "default:" */
644 			length += 5; /* tag name */
645 			length += 1; /* colon */
646 			r = archive_mstring_get_mbs_l(
647 			    &ap->name, &name, &len, sc);
648 			if (r != 0)
649 				return (-1);
650 			if (len > 0 && name != NULL)
651 				length += len;
652 			else
653 				length += sizeof(uid_t) * 3 + 1;
654 			length ++; /* colon */
655 			length += 3; /* rwx */
656 			length += 1; /* colon */
657 			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
658 			length ++; /* newline */
659 		}
660 		ap = ap->next;
661 	}
662 
663 	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
664 		length += 10; /* "user::rwx\n" */
665 		length += 11; /* "group::rwx\n" */
666 		length += 11; /* "other::rwx\n" */
667 	}
668 
669 	if (count == 0)
670 		return (0);
671 
672 	/* Now, allocate the string and actually populate it. */
673 	p = acl->acl_text = (char *)malloc(length);
674 	if (p == NULL)
675 		__archive_errx(1, "No memory to generate the text version of the ACL");
676 	count = 0;
677 	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
678 		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
679 		    acl->mode & 0700, -1);
680 		*p++ = ',';
681 		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
682 		    acl->mode & 0070, -1);
683 		*p++ = ',';
684 		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
685 		    acl->mode & 0007, -1);
686 		count += 3;
687 
688 		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
689 			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
690 				continue;
691 			r = archive_mstring_get_mbs_l(
692 			    &ap->name, &name, &len, sc);
693 			if (r != 0)
694 				return (-1);
695 			*p++ = separator;
696 			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
697 				id = ap->id;
698 			else
699 				id = -1;
700 			append_entry(&p, NULL, ap->tag, name,
701 			    ap->permset, id);
702 			count++;
703 		}
704 	}
705 
706 
707 	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
708 		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
709 			prefix = "default:";
710 		else
711 			prefix = NULL;
712 		count = 0;
713 		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
714 			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
715 				continue;
716 			r = archive_mstring_get_mbs_l(
717 			    &ap->name, &name, &len, sc);
718 			if (r != 0)
719 				return (-1);
720 			if (count > 0)
721 				*p++ = separator;
722 			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
723 				id = ap->id;
724 			else
725 				id = -1;
726 			append_entry(&p, prefix, ap->tag,
727 			    name, ap->permset, id);
728 			count ++;
729 		}
730 	}
731 
732 	*acl_text = acl->acl_text;
733 	if (acl_text_len != NULL)
734 		*acl_text_len = strlen(acl->acl_text);
735 	return (0);
736 }
737 
738 static void
739 append_id(char **p, int id)
740 {
741 	if (id < 0)
742 		id = 0;
743 	if (id > 9)
744 		append_id(p, id / 10);
745 	*(*p)++ = "0123456789"[id % 10];
746 }
747 
748 static void
749 append_entry(char **p, const char *prefix, int tag,
750     const char *name, int perm, int id)
751 {
752 	if (prefix != NULL) {
753 		strcpy(*p, prefix);
754 		*p += strlen(*p);
755 	}
756 	switch (tag) {
757 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
758 		name = NULL;
759 		id = -1;
760 		/* FALLTHROUGH */
761 	case ARCHIVE_ENTRY_ACL_USER:
762 		strcpy(*p, "user");
763 		break;
764 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
765 		name = NULL;
766 		id = -1;
767 		/* FALLTHROUGH */
768 	case ARCHIVE_ENTRY_ACL_GROUP:
769 		strcpy(*p, "group");
770 		break;
771 	case ARCHIVE_ENTRY_ACL_MASK:
772 		strcpy(*p, "mask");
773 		name = NULL;
774 		id = -1;
775 		break;
776 	case ARCHIVE_ENTRY_ACL_OTHER:
777 		strcpy(*p, "other");
778 		name = NULL;
779 		id = -1;
780 		break;
781 	}
782 	*p += strlen(*p);
783 	*(*p)++ = ':';
784 	if (name != NULL) {
785 		strcpy(*p, name);
786 		*p += strlen(*p);
787 	} else if (tag == ARCHIVE_ENTRY_ACL_USER
788 	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
789 		append_id(p, id);
790 		id = -1;
791 	}
792 	*(*p)++ = ':';
793 	*(*p)++ = (perm & 0444) ? 'r' : '-';
794 	*(*p)++ = (perm & 0222) ? 'w' : '-';
795 	*(*p)++ = (perm & 0111) ? 'x' : '-';
796 	if (id != -1) {
797 		*(*p)++ = ':';
798 		append_id(p, id);
799 	}
800 	**p = '\0';
801 }
802 
803 /*
804  * Parse a textual ACL.  This automatically recognizes and supports
805  * extensions described above.  The 'type' argument is used to
806  * indicate the type that should be used for any entries not
807  * explicitly marked as "default:".
808  */
809 int
810 archive_acl_parse_w(struct archive_acl *acl,
811     const wchar_t *text, int default_type)
812 {
813 	struct {
814 		const wchar_t *start;
815 		const wchar_t *end;
816 	} field[4], name;
817 
818 	int fields, n;
819 	int type, tag, permset, id;
820 	wchar_t sep;
821 
822 	while (text != NULL  &&  *text != L'\0') {
823 		/*
824 		 * Parse the fields out of the next entry,
825 		 * advance 'text' to start of next entry.
826 		 */
827 		fields = 0;
828 		do {
829 			const wchar_t *start, *end;
830 			next_field_w(&text, &start, &end, &sep);
831 			if (fields < 4) {
832 				field[fields].start = start;
833 				field[fields].end = end;
834 			}
835 			++fields;
836 		} while (sep == L':');
837 
838 		/* Set remaining fields to blank. */
839 		for (n = fields; n < 4; ++n)
840 			field[n].start = field[n].end = NULL;
841 
842 		/* Check for a numeric ID in field 1 or 3. */
843 		id = -1;
844 		isint_w(field[1].start, field[1].end, &id);
845 		/* Field 3 is optional. */
846 		if (id == -1 && fields > 3)
847 			isint_w(field[3].start, field[3].end, &id);
848 
849 		/*
850 		 * Solaris extension:  "defaultuser::rwx" is the
851 		 * default ACL corresponding to "user::rwx", etc.
852 		 */
853 		if (field[0].end - field[0].start > 7
854 		    && wmemcmp(field[0].start, L"default", 7) == 0) {
855 			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
856 			field[0].start += 7;
857 		} else
858 			type = default_type;
859 
860 		name.start = name.end = NULL;
861 		if (prefix_w(field[0].start, field[0].end, L"user")) {
862 			if (!ismode_w(field[2].start, field[2].end, &permset))
863 				return (ARCHIVE_WARN);
864 			if (id != -1 || field[1].start < field[1].end) {
865 				tag = ARCHIVE_ENTRY_ACL_USER;
866 				name = field[1];
867 			} else
868 				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
869 		} else if (prefix_w(field[0].start, field[0].end, L"group")) {
870 			if (!ismode_w(field[2].start, field[2].end, &permset))
871 				return (ARCHIVE_WARN);
872 			if (id != -1 || field[1].start < field[1].end) {
873 				tag = ARCHIVE_ENTRY_ACL_GROUP;
874 				name = field[1];
875 			} else
876 				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
877 		} else if (prefix_w(field[0].start, field[0].end, L"other")) {
878 			if (fields == 2
879 			    && field[1].start < field[1].end
880 			    && ismode_w(field[1].start, field[1].end, &permset)) {
881 				/* This is Solaris-style "other:rwx" */
882 			} else if (fields == 3
883 			    && field[1].start == field[1].end
884 			    && field[2].start < field[2].end
885 			    && ismode_w(field[2].start, field[2].end, &permset)) {
886 				/* This is FreeBSD-style "other::rwx" */
887 			} else
888 				return (ARCHIVE_WARN);
889 			tag = ARCHIVE_ENTRY_ACL_OTHER;
890 		} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
891 			if (fields == 2
892 			    && field[1].start < field[1].end
893 			    && ismode_w(field[1].start, field[1].end, &permset)) {
894 				/* This is Solaris-style "mask:rwx" */
895 			} else if (fields == 3
896 			    && field[1].start == field[1].end
897 			    && field[2].start < field[2].end
898 			    && ismode_w(field[2].start, field[2].end, &permset)) {
899 				/* This is FreeBSD-style "mask::rwx" */
900 			} else
901 				return (ARCHIVE_WARN);
902 			tag = ARCHIVE_ENTRY_ACL_MASK;
903 		} else
904 			return (ARCHIVE_WARN);
905 
906 		/* Add entry to the internal list. */
907 		archive_acl_add_entry_w_len(acl, type, permset,
908 		    tag, id, name.start, name.end - name.start);
909 	}
910 	return (ARCHIVE_OK);
911 }
912 
913 /*
914  * Parse a string to a positive decimal integer.  Returns true if
915  * the string is non-empty and consists only of decimal digits,
916  * false otherwise.
917  */
918 static int
919 isint_w(const wchar_t *start, const wchar_t *end, int *result)
920 {
921 	int n = 0;
922 	if (start >= end)
923 		return (0);
924 	while (start < end) {
925 		if (*start < '0' || *start > '9')
926 			return (0);
927 		if (n > (INT_MAX / 10) ||
928 		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
929 			n = INT_MAX;
930 		} else {
931 			n *= 10;
932 			n += *start - '0';
933 		}
934 		start++;
935 	}
936 	*result = n;
937 	return (1);
938 }
939 
940 /*
941  * Parse a string as a mode field.  Returns true if
942  * the string is non-empty and consists only of mode characters,
943  * false otherwise.
944  */
945 static int
946 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
947 {
948 	const wchar_t *p;
949 
950 	if (start >= end)
951 		return (0);
952 	p = start;
953 	*permset = 0;
954 	while (p < end) {
955 		switch (*p++) {
956 		case 'r': case 'R':
957 			*permset |= ARCHIVE_ENTRY_ACL_READ;
958 			break;
959 		case 'w': case 'W':
960 			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
961 			break;
962 		case 'x': case 'X':
963 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
964 			break;
965 		case '-':
966 			break;
967 		default:
968 			return (0);
969 		}
970 	}
971 	return (1);
972 }
973 
974 /*
975  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
976  * to point to just after the separator.  *start points to the first
977  * character of the matched text and *end just after the last
978  * character of the matched identifier.  In particular *end - *start
979  * is the length of the field body, not including leading or trailing
980  * whitespace.
981  */
982 static void
983 next_field_w(const wchar_t **wp, const wchar_t **start,
984     const wchar_t **end, wchar_t *sep)
985 {
986 	/* Skip leading whitespace to find start of field. */
987 	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
988 		(*wp)++;
989 	}
990 	*start = *wp;
991 
992 	/* Scan for the separator. */
993 	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
994 	    **wp != L'\n') {
995 		(*wp)++;
996 	}
997 	*sep = **wp;
998 
999 	/* Trim trailing whitespace to locate end of field. */
1000 	*end = *wp - 1;
1001 	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1002 		(*end)--;
1003 	}
1004 	(*end)++;
1005 
1006 	/* Adjust scanner location. */
1007 	if (**wp != L'\0')
1008 		(*wp)++;
1009 }
1010 
1011 /*
1012  * Return true if the characters [start...end) are a prefix of 'test'.
1013  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1014  */
1015 static int
1016 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1017 {
1018 	if (start == end)
1019 		return (0);
1020 
1021 	if (*start++ != *test++)
1022 		return (0);
1023 
1024 	while (start < end  &&  *start++ == *test++)
1025 		;
1026 
1027 	if (start < end)
1028 		return (0);
1029 
1030 	return (1);
1031 }
1032 
1033 /*
1034  * Parse a textual ACL.  This automatically recognizes and supports
1035  * extensions described above.  The 'type' argument is used to
1036  * indicate the type that should be used for any entries not
1037  * explicitly marked as "default:".
1038  */
1039 int
1040 archive_acl_parse_l(struct archive_acl *acl,
1041     const char *text, int default_type, struct archive_string_conv *sc)
1042 {
1043 	struct {
1044 		const char *start;
1045 		const char *end;
1046 	} field[4], name;
1047 
1048 	int fields, n, r, ret = ARCHIVE_OK;
1049 	int type, tag, permset, id;
1050 	char sep;
1051 
1052 	while (text != NULL  &&  *text != '\0') {
1053 		/*
1054 		 * Parse the fields out of the next entry,
1055 		 * advance 'text' to start of next entry.
1056 		 */
1057 		fields = 0;
1058 		do {
1059 			const char *start, *end;
1060 			next_field(&text, &start, &end, &sep);
1061 			if (fields < 4) {
1062 				field[fields].start = start;
1063 				field[fields].end = end;
1064 			}
1065 			++fields;
1066 		} while (sep == ':');
1067 
1068 		/* Set remaining fields to blank. */
1069 		for (n = fields; n < 4; ++n)
1070 			field[n].start = field[n].end = NULL;
1071 
1072 		/* Check for a numeric ID in field 1 or 3. */
1073 		id = -1;
1074 		isint(field[1].start, field[1].end, &id);
1075 		/* Field 3 is optional. */
1076 		if (id == -1 && fields > 3)
1077 			isint(field[3].start, field[3].end, &id);
1078 
1079 		/*
1080 		 * Solaris extension:  "defaultuser::rwx" is the
1081 		 * default ACL corresponding to "user::rwx", etc.
1082 		 */
1083 		if (field[0].end - field[0].start > 7
1084 		    && memcmp(field[0].start, "default", 7) == 0) {
1085 			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1086 			field[0].start += 7;
1087 		} else
1088 			type = default_type;
1089 
1090 		name.start = name.end = NULL;
1091 		if (prefix(field[0].start, field[0].end, "user")) {
1092 			if (!ismode(field[2].start, field[2].end, &permset))
1093 				return (ARCHIVE_WARN);
1094 			if (id != -1 || field[1].start < field[1].end) {
1095 				tag = ARCHIVE_ENTRY_ACL_USER;
1096 				name = field[1];
1097 			} else
1098 				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1099 		} else if (prefix(field[0].start, field[0].end, "group")) {
1100 			if (!ismode(field[2].start, field[2].end, &permset))
1101 				return (ARCHIVE_WARN);
1102 			if (id != -1 || field[1].start < field[1].end) {
1103 				tag = ARCHIVE_ENTRY_ACL_GROUP;
1104 				name = field[1];
1105 			} else
1106 				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1107 		} else if (prefix(field[0].start, field[0].end, "other")) {
1108 			if (fields == 2
1109 			    && field[1].start < field[1].end
1110 			    && ismode(field[1].start, field[1].end, &permset)) {
1111 				/* This is Solaris-style "other:rwx" */
1112 			} else if (fields == 3
1113 			    && field[1].start == field[1].end
1114 			    && field[2].start < field[2].end
1115 			    && ismode(field[2].start, field[2].end, &permset)) {
1116 				/* This is FreeBSD-style "other::rwx" */
1117 			} else
1118 				return (ARCHIVE_WARN);
1119 			tag = ARCHIVE_ENTRY_ACL_OTHER;
1120 		} else if (prefix(field[0].start, field[0].end, "mask")) {
1121 			if (fields == 2
1122 			    && field[1].start < field[1].end
1123 			    && ismode(field[1].start, field[1].end, &permset)) {
1124 				/* This is Solaris-style "mask:rwx" */
1125 			} else if (fields == 3
1126 			    && field[1].start == field[1].end
1127 			    && field[2].start < field[2].end
1128 			    && ismode(field[2].start, field[2].end, &permset)) {
1129 				/* This is FreeBSD-style "mask::rwx" */
1130 			} else
1131 				return (ARCHIVE_WARN);
1132 			tag = ARCHIVE_ENTRY_ACL_MASK;
1133 		} else
1134 			return (ARCHIVE_WARN);
1135 
1136 		/* Add entry to the internal list. */
1137 		r = archive_acl_add_entry_len_l(acl, type, permset,
1138 		    tag, id, name.start, name.end - name.start, sc);
1139 		if (r < ARCHIVE_WARN)
1140 			return (r);
1141 		if (r != ARCHIVE_OK)
1142 			ret = ARCHIVE_WARN;
1143 	}
1144 	return (ret);
1145 }
1146 
1147 /*
1148  * Parse a string to a positive decimal integer.  Returns true if
1149  * the string is non-empty and consists only of decimal digits,
1150  * false otherwise.
1151  */
1152 static int
1153 isint(const char *start, const char *end, int *result)
1154 {
1155 	int n = 0;
1156 	if (start >= end)
1157 		return (0);
1158 	while (start < end) {
1159 		if (*start < '0' || *start > '9')
1160 			return (0);
1161 		if (n > (INT_MAX / 10) ||
1162 		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1163 			n = INT_MAX;
1164 		} else {
1165 			n *= 10;
1166 			n += *start - '0';
1167 		}
1168 		start++;
1169 	}
1170 	*result = n;
1171 	return (1);
1172 }
1173 
1174 /*
1175  * Parse a string as a mode field.  Returns true if
1176  * the string is non-empty and consists only of mode characters,
1177  * false otherwise.
1178  */
1179 static int
1180 ismode(const char *start, const char *end, int *permset)
1181 {
1182 	const char *p;
1183 
1184 	if (start >= end)
1185 		return (0);
1186 	p = start;
1187 	*permset = 0;
1188 	while (p < end) {
1189 		switch (*p++) {
1190 		case 'r': case 'R':
1191 			*permset |= ARCHIVE_ENTRY_ACL_READ;
1192 			break;
1193 		case 'w': case 'W':
1194 			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1195 			break;
1196 		case 'x': case 'X':
1197 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1198 			break;
1199 		case '-':
1200 			break;
1201 		default:
1202 			return (0);
1203 		}
1204 	}
1205 	return (1);
1206 }
1207 
1208 /*
1209  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1210  * to point to just after the separator.  *start points to the first
1211  * character of the matched text and *end just after the last
1212  * character of the matched identifier.  In particular *end - *start
1213  * is the length of the field body, not including leading or trailing
1214  * whitespace.
1215  */
1216 static void
1217 next_field(const char **p, const char **start,
1218     const char **end, char *sep)
1219 {
1220 	/* Skip leading whitespace to find start of field. */
1221 	while (**p == ' ' || **p == '\t' || **p == '\n') {
1222 		(*p)++;
1223 	}
1224 	*start = *p;
1225 
1226 	/* Scan for the separator. */
1227 	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1228 		(*p)++;
1229 	}
1230 	*sep = **p;
1231 
1232 	/* Trim trailing whitespace to locate end of field. */
1233 	*end = *p - 1;
1234 	while (**end == ' ' || **end == '\t' || **end == '\n') {
1235 		(*end)--;
1236 	}
1237 	(*end)++;
1238 
1239 	/* Adjust scanner location. */
1240 	if (**p != '\0')
1241 		(*p)++;
1242 }
1243 
1244 /*
1245  * Return true if the characters [start...end) are a prefix of 'test'.
1246  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1247  */
1248 static int
1249 prefix(const char *start, const char *end, const char *test)
1250 {
1251 	if (start == end)
1252 		return (0);
1253 
1254 	if (*start++ != *test++)
1255 		return (0);
1256 
1257 	while (start < end  &&  *start++ == *test++)
1258 		;
1259 
1260 	if (start < end)
1261 		return (0);
1262 
1263 	return (1);
1264 }
1265