archive_acl.c revision 231200
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
51static int	acl_special(struct archive_acl *acl,
52		    int type, int permset, int tag);
53static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
54		    int type, int permset, int tag, int id);
55static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
56static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
57static void	next_field_w(const wchar_t **wp, const wchar_t **start,
58		    const wchar_t **end, wchar_t *sep);
59static int	prefix_w(const wchar_t *start, const wchar_t *end,
60		    const wchar_t *test);
61static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
62		    const wchar_t *wname, int perm, int id);
63static void	append_id_w(wchar_t **wp, int id);
64static int	isint(const char *start, const char *end, int *result);
65static int	ismode(const char *start, const char *end, int *result);
66static void	next_field(const char **p, const char **start,
67		    const char **end, char *sep);
68static int	prefix(const char *start, const char *end,
69		    const char *test);
70static void	append_entry(char **p, const char *prefix, int tag,
71		    const char *name, int perm, int id);
72static void	append_id(char **p, int id);
73
74void
75archive_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
97void
98archive_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
115int
116archive_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
135int
136archive_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
155int
156archive_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 */
188static int
189acl_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 */
215static struct archive_acl_entry *
216acl_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 */
311int
312archive_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 */
335int
336archive_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 */
365int
366archive_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 */
432const wchar_t *
433archive_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
546static void
547append_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
556static void
557append_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
611int
612archive_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
738static void
739append_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
748static void
749append_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 */
809int
810archive_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 */
918static int
919isint_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 */
945static int
946ismode_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 */
982static void
983next_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 */
1015static int
1016prefix_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 */
1039int
1040archive_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 */
1152static int
1153isint(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 */
1179static int
1180ismode(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 */
1216static void
1217next_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 */
1248static int
1249prefix(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