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