archive_acl.c revision 305754
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_state = 0; /* Not counting. */
98}
99
100void
101archive_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
118int
119archive_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
138int
139archive_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
158static int
159archive_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 */
191static int
192acl_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 */
218static struct archive_acl_entry *
219acl_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 */
314int
315archive_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 */
338int
339archive_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 */
368int
369archive_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 */
438const wchar_t *
439archive_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
560static void
561append_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
570static void
571append_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
625int
626archive_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 (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
711				id = ap->id;
712			} else {
713				id = -1;
714			}
715			append_entry(&p, NULL, ap->tag, name,
716			    ap->permset, id);
717			count++;
718		}
719	}
720
721
722	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
723		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
724			prefix = "default:";
725		else
726			prefix = NULL;
727		count = 0;
728		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
729			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
730				continue;
731			r = archive_mstring_get_mbs_l(
732			    &ap->name, &name, &len, sc);
733			if (r != 0)
734				return (-1);
735			if (count > 0)
736				*p++ = separator;
737			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
738				id = ap->id;
739			else
740				id = -1;
741			append_entry(&p, prefix, ap->tag,
742			    name, ap->permset, id);
743			count ++;
744		}
745	}
746
747	*acl_text = acl->acl_text;
748	if (acl_text_len != NULL)
749		*acl_text_len = strlen(acl->acl_text);
750	return (0);
751}
752
753static void
754append_id(char **p, int id)
755{
756	if (id < 0)
757		id = 0;
758	if (id > 9)
759		append_id(p, id / 10);
760	*(*p)++ = "0123456789"[id % 10];
761}
762
763static void
764append_entry(char **p, const char *prefix, int tag,
765    const char *name, int perm, int id)
766{
767	if (prefix != NULL) {
768		strcpy(*p, prefix);
769		*p += strlen(*p);
770	}
771	switch (tag) {
772	case ARCHIVE_ENTRY_ACL_USER_OBJ:
773		name = NULL;
774		id = -1;
775		/* FALLTHROUGH */
776	case ARCHIVE_ENTRY_ACL_USER:
777		strcpy(*p, "user");
778		break;
779	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
780		name = NULL;
781		id = -1;
782		/* FALLTHROUGH */
783	case ARCHIVE_ENTRY_ACL_GROUP:
784		strcpy(*p, "group");
785		break;
786	case ARCHIVE_ENTRY_ACL_MASK:
787		strcpy(*p, "mask");
788		name = NULL;
789		id = -1;
790		break;
791	case ARCHIVE_ENTRY_ACL_OTHER:
792		strcpy(*p, "other");
793		name = NULL;
794		id = -1;
795		break;
796	}
797	*p += strlen(*p);
798	*(*p)++ = ':';
799	if (name != NULL) {
800		strcpy(*p, name);
801		*p += strlen(*p);
802	} else if (tag == ARCHIVE_ENTRY_ACL_USER
803	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
804		append_id(p, id);
805		id = -1;
806	}
807	*(*p)++ = ':';
808	*(*p)++ = (perm & 0444) ? 'r' : '-';
809	*(*p)++ = (perm & 0222) ? 'w' : '-';
810	*(*p)++ = (perm & 0111) ? 'x' : '-';
811	if (id != -1) {
812		*(*p)++ = ':';
813		append_id(p, id);
814	}
815	**p = '\0';
816}
817
818/*
819 * Parse a textual ACL.  This automatically recognizes and supports
820 * extensions described above.  The 'type' argument is used to
821 * indicate the type that should be used for any entries not
822 * explicitly marked as "default:".
823 */
824int
825archive_acl_parse_w(struct archive_acl *acl,
826    const wchar_t *text, int default_type)
827{
828	struct {
829		const wchar_t *start;
830		const wchar_t *end;
831	} field[4], name;
832
833	int fields, n;
834	int type, tag, permset, id;
835	wchar_t sep;
836
837	while (text != NULL  &&  *text != L'\0') {
838		/*
839		 * Parse the fields out of the next entry,
840		 * advance 'text' to start of next entry.
841		 */
842		fields = 0;
843		do {
844			const wchar_t *start, *end;
845			next_field_w(&text, &start, &end, &sep);
846			if (fields < 4) {
847				field[fields].start = start;
848				field[fields].end = end;
849			}
850			++fields;
851		} while (sep == L':');
852
853		/* Set remaining fields to blank. */
854		for (n = fields; n < 4; ++n)
855			field[n].start = field[n].end = NULL;
856
857		/* Check for a numeric ID in field 1 or 3. */
858		id = -1;
859		isint_w(field[1].start, field[1].end, &id);
860		/* Field 3 is optional. */
861		if (id == -1 && fields > 3)
862			isint_w(field[3].start, field[3].end, &id);
863
864		/*
865		 * Solaris extension:  "defaultuser::rwx" is the
866		 * default ACL corresponding to "user::rwx", etc.
867		 */
868		if (field[0].end - field[0].start > 7
869		    && wmemcmp(field[0].start, L"default", 7) == 0) {
870			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
871			field[0].start += 7;
872		} else
873			type = default_type;
874
875		name.start = name.end = NULL;
876		if (prefix_w(field[0].start, field[0].end, L"user")) {
877			if (!ismode_w(field[2].start, field[2].end, &permset))
878				return (ARCHIVE_WARN);
879			if (id != -1 || field[1].start < field[1].end) {
880				tag = ARCHIVE_ENTRY_ACL_USER;
881				name = field[1];
882			} else
883				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
884		} else if (prefix_w(field[0].start, field[0].end, L"group")) {
885			if (!ismode_w(field[2].start, field[2].end, &permset))
886				return (ARCHIVE_WARN);
887			if (id != -1 || field[1].start < field[1].end) {
888				tag = ARCHIVE_ENTRY_ACL_GROUP;
889				name = field[1];
890			} else
891				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
892		} else if (prefix_w(field[0].start, field[0].end, L"other")) {
893			if (fields == 2
894			    && field[1].start < field[1].end
895			    && ismode_w(field[1].start, field[1].end, &permset)) {
896				/* This is Solaris-style "other:rwx" */
897			} else if (fields == 3
898			    && field[1].start == field[1].end
899			    && field[2].start < field[2].end
900			    && ismode_w(field[2].start, field[2].end, &permset)) {
901				/* This is FreeBSD-style "other::rwx" */
902			} else
903				return (ARCHIVE_WARN);
904			tag = ARCHIVE_ENTRY_ACL_OTHER;
905		} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
906			if (fields == 2
907			    && field[1].start < field[1].end
908			    && ismode_w(field[1].start, field[1].end, &permset)) {
909				/* This is Solaris-style "mask:rwx" */
910			} else if (fields == 3
911			    && field[1].start == field[1].end
912			    && field[2].start < field[2].end
913			    && ismode_w(field[2].start, field[2].end, &permset)) {
914				/* This is FreeBSD-style "mask::rwx" */
915			} else
916				return (ARCHIVE_WARN);
917			tag = ARCHIVE_ENTRY_ACL_MASK;
918		} else
919			return (ARCHIVE_WARN);
920
921		/* Add entry to the internal list. */
922		archive_acl_add_entry_w_len(acl, type, permset,
923		    tag, id, name.start, name.end - name.start);
924	}
925	return (ARCHIVE_OK);
926}
927
928/*
929 * Parse a string to a positive decimal integer.  Returns true if
930 * the string is non-empty and consists only of decimal digits,
931 * false otherwise.
932 */
933static int
934isint_w(const wchar_t *start, const wchar_t *end, int *result)
935{
936	int n = 0;
937	if (start >= end)
938		return (0);
939	while (start < end) {
940		if (*start < '0' || *start > '9')
941			return (0);
942		if (n > (INT_MAX / 10) ||
943		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
944			n = INT_MAX;
945		} else {
946			n *= 10;
947			n += *start - '0';
948		}
949		start++;
950	}
951	*result = n;
952	return (1);
953}
954
955/*
956 * Parse a string as a mode field.  Returns true if
957 * the string is non-empty and consists only of mode characters,
958 * false otherwise.
959 */
960static int
961ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
962{
963	const wchar_t *p;
964
965	if (start >= end)
966		return (0);
967	p = start;
968	*permset = 0;
969	while (p < end) {
970		switch (*p++) {
971		case 'r': case 'R':
972			*permset |= ARCHIVE_ENTRY_ACL_READ;
973			break;
974		case 'w': case 'W':
975			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
976			break;
977		case 'x': case 'X':
978			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
979			break;
980		case '-':
981			break;
982		default:
983			return (0);
984		}
985	}
986	return (1);
987}
988
989/*
990 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
991 * to point to just after the separator.  *start points to the first
992 * character of the matched text and *end just after the last
993 * character of the matched identifier.  In particular *end - *start
994 * is the length of the field body, not including leading or trailing
995 * whitespace.
996 */
997static void
998next_field_w(const wchar_t **wp, const wchar_t **start,
999    const wchar_t **end, wchar_t *sep)
1000{
1001	/* Skip leading whitespace to find start of field. */
1002	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1003		(*wp)++;
1004	}
1005	*start = *wp;
1006
1007	/* Scan for the separator. */
1008	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1009	    **wp != L'\n') {
1010		(*wp)++;
1011	}
1012	*sep = **wp;
1013
1014	/* Trim trailing whitespace to locate end of field. */
1015	*end = *wp - 1;
1016	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1017		(*end)--;
1018	}
1019	(*end)++;
1020
1021	/* Adjust scanner location. */
1022	if (**wp != L'\0')
1023		(*wp)++;
1024}
1025
1026/*
1027 * Return true if the characters [start...end) are a prefix of 'test'.
1028 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1029 */
1030static int
1031prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1032{
1033	if (start == end)
1034		return (0);
1035
1036	if (*start++ != *test++)
1037		return (0);
1038
1039	while (start < end  &&  *start++ == *test++)
1040		;
1041
1042	if (start < end)
1043		return (0);
1044
1045	return (1);
1046}
1047
1048/*
1049 * Parse a textual ACL.  This automatically recognizes and supports
1050 * extensions described above.  The 'type' argument is used to
1051 * indicate the type that should be used for any entries not
1052 * explicitly marked as "default:".
1053 */
1054int
1055archive_acl_parse_l(struct archive_acl *acl,
1056    const char *text, int default_type, struct archive_string_conv *sc)
1057{
1058	struct {
1059		const char *start;
1060		const char *end;
1061	} field[4], name;
1062
1063	int fields, n, r, ret = ARCHIVE_OK;
1064	int type, tag, permset, id;
1065	char sep;
1066
1067	while (text != NULL  &&  *text != '\0') {
1068		/*
1069		 * Parse the fields out of the next entry,
1070		 * advance 'text' to start of next entry.
1071		 */
1072		fields = 0;
1073		do {
1074			const char *start, *end;
1075			next_field(&text, &start, &end, &sep);
1076			if (fields < 4) {
1077				field[fields].start = start;
1078				field[fields].end = end;
1079			}
1080			++fields;
1081		} while (sep == ':');
1082
1083		/* Set remaining fields to blank. */
1084		for (n = fields; n < 4; ++n)
1085			field[n].start = field[n].end = NULL;
1086
1087		/* Check for a numeric ID in field 1 or 3. */
1088		id = -1;
1089		isint(field[1].start, field[1].end, &id);
1090		/* Field 3 is optional. */
1091		if (id == -1 && fields > 3)
1092			isint(field[3].start, field[3].end, &id);
1093
1094		/*
1095		 * Solaris extension:  "defaultuser::rwx" is the
1096		 * default ACL corresponding to "user::rwx", etc.
1097		 */
1098		if (field[0].end - field[0].start > 7
1099		    && memcmp(field[0].start, "default", 7) == 0) {
1100			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1101			field[0].start += 7;
1102		} else
1103			type = default_type;
1104
1105		name.start = name.end = NULL;
1106		if (prefix_c(field[0].start, field[0].end, "user")) {
1107			if (!ismode(field[2].start, field[2].end, &permset))
1108				return (ARCHIVE_WARN);
1109			if (id != -1 || field[1].start < field[1].end) {
1110				tag = ARCHIVE_ENTRY_ACL_USER;
1111				name = field[1];
1112			} else
1113				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1114		} else if (prefix_c(field[0].start, field[0].end, "group")) {
1115			if (!ismode(field[2].start, field[2].end, &permset))
1116				return (ARCHIVE_WARN);
1117			if (id != -1 || field[1].start < field[1].end) {
1118				tag = ARCHIVE_ENTRY_ACL_GROUP;
1119				name = field[1];
1120			} else
1121				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1122		} else if (prefix_c(field[0].start, field[0].end, "other")) {
1123			if (fields == 2
1124			    && field[1].start < field[1].end
1125			    && ismode(field[1].start, field[1].end, &permset)) {
1126				/* This is Solaris-style "other:rwx" */
1127			} else if (fields == 3
1128			    && field[1].start == field[1].end
1129			    && field[2].start < field[2].end
1130			    && ismode(field[2].start, field[2].end, &permset)) {
1131				/* This is FreeBSD-style "other::rwx" */
1132			} else
1133				return (ARCHIVE_WARN);
1134			tag = ARCHIVE_ENTRY_ACL_OTHER;
1135		} else if (prefix_c(field[0].start, field[0].end, "mask")) {
1136			if (fields == 2
1137			    && field[1].start < field[1].end
1138			    && ismode(field[1].start, field[1].end, &permset)) {
1139				/* This is Solaris-style "mask:rwx" */
1140			} else if (fields == 3
1141			    && field[1].start == field[1].end
1142			    && field[2].start < field[2].end
1143			    && ismode(field[2].start, field[2].end, &permset)) {
1144				/* This is FreeBSD-style "mask::rwx" */
1145			} else
1146				return (ARCHIVE_WARN);
1147			tag = ARCHIVE_ENTRY_ACL_MASK;
1148		} else
1149			return (ARCHIVE_WARN);
1150
1151		/* Add entry to the internal list. */
1152		r = archive_acl_add_entry_len_l(acl, type, permset,
1153		    tag, id, name.start, name.end - name.start, sc);
1154		if (r < ARCHIVE_WARN)
1155			return (r);
1156		if (r != ARCHIVE_OK)
1157			ret = ARCHIVE_WARN;
1158	}
1159	return (ret);
1160}
1161
1162/*
1163 * Parse a string to a positive decimal integer.  Returns true if
1164 * the string is non-empty and consists only of decimal digits,
1165 * false otherwise.
1166 */
1167static int
1168isint(const char *start, const char *end, int *result)
1169{
1170	int n = 0;
1171	if (start >= end)
1172		return (0);
1173	while (start < end) {
1174		if (*start < '0' || *start > '9')
1175			return (0);
1176		if (n > (INT_MAX / 10) ||
1177		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1178			n = INT_MAX;
1179		} else {
1180			n *= 10;
1181			n += *start - '0';
1182		}
1183		start++;
1184	}
1185	*result = n;
1186	return (1);
1187}
1188
1189/*
1190 * Parse a string as a mode field.  Returns true if
1191 * the string is non-empty and consists only of mode characters,
1192 * false otherwise.
1193 */
1194static int
1195ismode(const char *start, const char *end, int *permset)
1196{
1197	const char *p;
1198
1199	if (start >= end)
1200		return (0);
1201	p = start;
1202	*permset = 0;
1203	while (p < end) {
1204		switch (*p++) {
1205		case 'r': case 'R':
1206			*permset |= ARCHIVE_ENTRY_ACL_READ;
1207			break;
1208		case 'w': case 'W':
1209			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1210			break;
1211		case 'x': case 'X':
1212			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1213			break;
1214		case '-':
1215			break;
1216		default:
1217			return (0);
1218		}
1219	}
1220	return (1);
1221}
1222
1223/*
1224 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1225 * to point to just after the separator.  *start points to the first
1226 * character of the matched text and *end just after the last
1227 * character of the matched identifier.  In particular *end - *start
1228 * is the length of the field body, not including leading or trailing
1229 * whitespace.
1230 */
1231static void
1232next_field(const char **p, const char **start,
1233    const char **end, char *sep)
1234{
1235	/* Skip leading whitespace to find start of field. */
1236	while (**p == ' ' || **p == '\t' || **p == '\n') {
1237		(*p)++;
1238	}
1239	*start = *p;
1240
1241	/* Scan for the separator. */
1242	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1243		(*p)++;
1244	}
1245	*sep = **p;
1246
1247	/* Trim trailing whitespace to locate end of field. */
1248	*end = *p - 1;
1249	while (**end == ' ' || **end == '\t' || **end == '\n') {
1250		(*end)--;
1251	}
1252	(*end)++;
1253
1254	/* Adjust scanner location. */
1255	if (**p != '\0')
1256		(*p)++;
1257}
1258
1259/*
1260 * Return true if the characters [start...end) are a prefix of 'test'.
1261 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1262 */
1263static int
1264prefix_c(const char *start, const char *end, const char *test)
1265{
1266	if (start == end)
1267		return (0);
1268
1269	if (*start++ != *test++)
1270		return (0);
1271
1272	while (start < end  &&  *start++ == *test++)
1273		;
1274
1275	if (start < end)
1276		return (0);
1277
1278	return (1);
1279}
1280