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