archive_acl.c revision 238856
1231200Smm/*-
2231200Smm * Copyright (c) 2003-2010 Tim Kientzle
3231200Smm * All rights reserved.
4231200Smm *
5231200Smm * Redistribution and use in source and binary forms, with or without
6231200Smm * modification, are permitted provided that the following conditions
7231200Smm * are met:
8231200Smm * 1. Redistributions of source code must retain the above copyright
9231200Smm *    notice, this list of conditions and the following disclaimer.
10231200Smm * 2. Redistributions in binary form must reproduce the above copyright
11231200Smm *    notice, this list of conditions and the following disclaimer in the
12231200Smm *    documentation and/or other materials provided with the distribution.
13231200Smm *
14231200Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15231200Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16231200Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17231200Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18231200Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19231200Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20231200Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21231200Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22231200Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23231200Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24231200Smm */
25231200Smm
26231200Smm#include "archive_platform.h"
27231200Smm__FBSDID("$FreeBSD$");
28231200Smm
29231200Smm#ifdef HAVE_ERRNO_H
30231200Smm#include <errno.h>
31231200Smm#endif
32231200Smm#ifdef HAVE_LIMITS_H
33231200Smm#include <limits.h>
34231200Smm#endif
35231200Smm#ifdef HAVE_WCHAR_H
36231200Smm#include <wchar.h>
37231200Smm#endif
38231200Smm
39231200Smm#include "archive_acl_private.h"
40231200Smm#include "archive_entry.h"
41231200Smm#include "archive_private.h"
42231200Smm
43231200Smm#undef max
44231200Smm#define	max(a, b)	((a)>(b)?(a):(b))
45231200Smm
46231200Smm#ifndef HAVE_WMEMCMP
47231200Smm/* Good enough for simple equality testing, but not for sorting. */
48231200Smm#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
49231200Smm#endif
50231200Smm
51231200Smmstatic int	acl_special(struct archive_acl *acl,
52231200Smm		    int type, int permset, int tag);
53231200Smmstatic struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
54231200Smm		    int type, int permset, int tag, int id);
55232153Smmstatic int	archive_acl_add_entry_len_l(struct archive_acl *acl,
56232153Smm		    int type, int permset, int tag, int id, const char *name,
57232153Smm		    size_t len, struct archive_string_conv *sc);
58231200Smmstatic int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
59231200Smmstatic int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
60231200Smmstatic void	next_field_w(const wchar_t **wp, const wchar_t **start,
61231200Smm		    const wchar_t **end, wchar_t *sep);
62231200Smmstatic int	prefix_w(const wchar_t *start, const wchar_t *end,
63231200Smm		    const wchar_t *test);
64231200Smmstatic void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
65231200Smm		    const wchar_t *wname, int perm, int id);
66231200Smmstatic void	append_id_w(wchar_t **wp, int id);
67231200Smmstatic int	isint(const char *start, const char *end, int *result);
68231200Smmstatic int	ismode(const char *start, const char *end, int *result);
69231200Smmstatic void	next_field(const char **p, const char **start,
70231200Smm		    const char **end, char *sep);
71232153Smmstatic int	prefix_c(const char *start, const char *end,
72231200Smm		    const char *test);
73231200Smmstatic void	append_entry(char **p, const char *prefix, int tag,
74231200Smm		    const char *name, int perm, int id);
75231200Smmstatic void	append_id(char **p, int id);
76231200Smm
77231200Smmvoid
78231200Smmarchive_acl_clear(struct archive_acl *acl)
79231200Smm{
80231200Smm	struct archive_acl_entry *ap;
81231200Smm
82231200Smm	while (acl->acl_head != NULL) {
83231200Smm		ap = acl->acl_head->next;
84231200Smm		archive_mstring_clean(&acl->acl_head->name);
85231200Smm		free(acl->acl_head);
86231200Smm		acl->acl_head = ap;
87231200Smm	}
88231200Smm	if (acl->acl_text_w != NULL) {
89231200Smm		free(acl->acl_text_w);
90231200Smm		acl->acl_text_w = NULL;
91231200Smm	}
92231200Smm	if (acl->acl_text != NULL) {
93231200Smm		free(acl->acl_text);
94231200Smm		acl->acl_text = NULL;
95231200Smm	}
96231200Smm	acl->acl_p = NULL;
97231200Smm	acl->acl_state = 0; /* Not counting. */
98231200Smm}
99231200Smm
100231200Smmvoid
101231200Smmarchive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
102231200Smm{
103231200Smm	struct archive_acl_entry *ap, *ap2;
104231200Smm
105231200Smm	archive_acl_clear(dest);
106231200Smm
107231200Smm	dest->mode = src->mode;
108231200Smm	ap = src->acl_head;
109231200Smm	while (ap != NULL) {
110231200Smm		ap2 = acl_new_entry(dest,
111231200Smm		    ap->type, ap->permset, ap->tag, ap->id);
112231200Smm		if (ap2 != NULL)
113231200Smm			archive_mstring_copy(&ap2->name, &ap->name);
114231200Smm		ap = ap->next;
115231200Smm	}
116231200Smm}
117231200Smm
118231200Smmint
119231200Smmarchive_acl_add_entry(struct archive_acl *acl,
120231200Smm    int type, int permset, int tag, int id, const char *name)
121231200Smm{
122231200Smm	struct archive_acl_entry *ap;
123231200Smm
124231200Smm	if (acl_special(acl, type, permset, tag) == 0)
125231200Smm		return ARCHIVE_OK;
126231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
127231200Smm	if (ap == NULL) {
128231200Smm		/* XXX Error XXX */
129231200Smm		return ARCHIVE_FAILED;
130231200Smm	}
131231200Smm	if (name != NULL  &&  *name != '\0')
132231200Smm		archive_mstring_copy_mbs(&ap->name, name);
133231200Smm	else
134231200Smm		archive_mstring_clean(&ap->name);
135231200Smm	return ARCHIVE_OK;
136231200Smm}
137231200Smm
138231200Smmint
139231200Smmarchive_acl_add_entry_w_len(struct archive_acl *acl,
140231200Smm    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
141231200Smm{
142231200Smm	struct archive_acl_entry *ap;
143231200Smm
144231200Smm	if (acl_special(acl, type, permset, tag) == 0)
145231200Smm		return ARCHIVE_OK;
146231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
147231200Smm	if (ap == NULL) {
148231200Smm		/* XXX Error XXX */
149231200Smm		return ARCHIVE_FAILED;
150231200Smm	}
151231200Smm	if (name != NULL  &&  *name != L'\0' && len > 0)
152231200Smm		archive_mstring_copy_wcs_len(&ap->name, name, len);
153231200Smm	else
154231200Smm		archive_mstring_clean(&ap->name);
155231200Smm	return ARCHIVE_OK;
156231200Smm}
157231200Smm
158232153Smmstatic int
159231200Smmarchive_acl_add_entry_len_l(struct archive_acl *acl,
160231200Smm    int type, int permset, int tag, int id, const char *name, size_t len,
161231200Smm    struct archive_string_conv *sc)
162231200Smm{
163231200Smm	struct archive_acl_entry *ap;
164231200Smm	int r;
165231200Smm
166231200Smm	if (acl_special(acl, type, permset, tag) == 0)
167231200Smm		return ARCHIVE_OK;
168231200Smm	ap = acl_new_entry(acl, type, permset, tag, id);
169231200Smm	if (ap == NULL) {
170231200Smm		/* XXX Error XXX */
171231200Smm		return ARCHIVE_FAILED;
172231200Smm	}
173231200Smm	if (name != NULL  &&  *name != '\0' && len > 0) {
174231200Smm		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
175231200Smm	} else {
176231200Smm		r = 0;
177231200Smm		archive_mstring_clean(&ap->name);
178231200Smm	}
179231200Smm	if (r == 0)
180231200Smm		return (ARCHIVE_OK);
181231200Smm	else if (errno == ENOMEM)
182231200Smm		return (ARCHIVE_FATAL);
183231200Smm	else
184231200Smm		return (ARCHIVE_WARN);
185231200Smm}
186231200Smm
187231200Smm/*
188231200Smm * If this ACL entry is part of the standard POSIX permissions set,
189231200Smm * store the permissions in the stat structure and return zero.
190231200Smm */
191231200Smmstatic int
192231200Smmacl_special(struct archive_acl *acl, int type, int permset, int tag)
193231200Smm{
194231200Smm	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
195231200Smm	    && ((permset & ~007) == 0)) {
196231200Smm		switch (tag) {
197231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
198231200Smm			acl->mode &= ~0700;
199231200Smm			acl->mode |= (permset & 7) << 6;
200231200Smm			return (0);
201231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
202231200Smm			acl->mode &= ~0070;
203231200Smm			acl->mode |= (permset & 7) << 3;
204231200Smm			return (0);
205231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
206231200Smm			acl->mode &= ~0007;
207231200Smm			acl->mode |= permset & 7;
208231200Smm			return (0);
209231200Smm		}
210231200Smm	}
211231200Smm	return (1);
212231200Smm}
213231200Smm
214231200Smm/*
215231200Smm * Allocate and populate a new ACL entry with everything but the
216231200Smm * name.
217231200Smm */
218231200Smmstatic struct archive_acl_entry *
219231200Smmacl_new_entry(struct archive_acl *acl,
220231200Smm    int type, int permset, int tag, int id)
221231200Smm{
222231200Smm	struct archive_acl_entry *ap, *aq;
223231200Smm
224231200Smm	/* Type argument must be a valid NFS4 or POSIX.1e type.
225231200Smm	 * The type must agree with anything already set and
226231200Smm	 * the permset must be compatible. */
227231200Smm	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
228231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
229231200Smm			return (NULL);
230231200Smm		}
231231200Smm		if (permset &
232231200Smm		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
233231200Smm			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
234231200Smm			return (NULL);
235231200Smm		}
236231200Smm	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
237231200Smm		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
238231200Smm			return (NULL);
239231200Smm		}
240231200Smm		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
241231200Smm			return (NULL);
242231200Smm		}
243231200Smm	} else {
244231200Smm		return (NULL);
245231200Smm	}
246231200Smm
247231200Smm	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
248231200Smm	switch (tag) {
249231200Smm	case ARCHIVE_ENTRY_ACL_USER:
250231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
251231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
252231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
253231200Smm		/* Tags valid in both NFS4 and POSIX.1e */
254231200Smm		break;
255231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
256231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
257231200Smm		/* Tags valid only in POSIX.1e. */
258231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
259231200Smm			return (NULL);
260231200Smm		}
261231200Smm		break;
262231200Smm	case ARCHIVE_ENTRY_ACL_EVERYONE:
263231200Smm		/* Tags valid only in NFS4. */
264231200Smm		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
265231200Smm			return (NULL);
266231200Smm		}
267231200Smm		break;
268231200Smm	default:
269231200Smm		/* No other values are valid. */
270231200Smm		return (NULL);
271231200Smm	}
272231200Smm
273231200Smm	if (acl->acl_text_w != NULL) {
274231200Smm		free(acl->acl_text_w);
275231200Smm		acl->acl_text_w = NULL;
276231200Smm	}
277231200Smm	if (acl->acl_text != NULL) {
278231200Smm		free(acl->acl_text);
279231200Smm		acl->acl_text = NULL;
280231200Smm	}
281231200Smm
282231200Smm	/* If there's a matching entry already in the list, overwrite it. */
283231200Smm	ap = acl->acl_head;
284231200Smm	aq = NULL;
285231200Smm	while (ap != NULL) {
286231200Smm		if (ap->type == type && ap->tag == tag && ap->id == id) {
287231200Smm			ap->permset = permset;
288231200Smm			return (ap);
289231200Smm		}
290231200Smm		aq = ap;
291231200Smm		ap = ap->next;
292231200Smm	}
293231200Smm
294231200Smm	/* Add a new entry to the end of the list. */
295231200Smm	ap = (struct archive_acl_entry *)malloc(sizeof(*ap));
296231200Smm	if (ap == NULL)
297231200Smm		return (NULL);
298231200Smm	memset(ap, 0, sizeof(*ap));
299231200Smm	if (aq == NULL)
300231200Smm		acl->acl_head = ap;
301231200Smm	else
302231200Smm		aq->next = ap;
303231200Smm	ap->type = type;
304231200Smm	ap->tag = tag;
305231200Smm	ap->id = id;
306231200Smm	ap->permset = permset;
307231200Smm	acl->acl_types |= type;
308231200Smm	return (ap);
309231200Smm}
310231200Smm
311231200Smm/*
312231200Smm * Return a count of entries matching "want_type".
313231200Smm */
314231200Smmint
315231200Smmarchive_acl_count(struct archive_acl *acl, int want_type)
316231200Smm{
317231200Smm	int count;
318231200Smm	struct archive_acl_entry *ap;
319231200Smm
320231200Smm	count = 0;
321231200Smm	ap = acl->acl_head;
322231200Smm	while (ap != NULL) {
323231200Smm		if ((ap->type & want_type) != 0)
324231200Smm			count++;
325231200Smm		ap = ap->next;
326231200Smm	}
327231200Smm
328231200Smm	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
329231200Smm		count += 3;
330231200Smm	return (count);
331231200Smm}
332231200Smm
333231200Smm/*
334231200Smm * Prepare for reading entries from the ACL data.  Returns a count
335231200Smm * of entries matching "want_type", or zero if there are no
336231200Smm * non-extended ACL entries of that type.
337231200Smm */
338231200Smmint
339231200Smmarchive_acl_reset(struct archive_acl *acl, int want_type)
340231200Smm{
341231200Smm	int count, cutoff;
342231200Smm
343231200Smm	count = archive_acl_count(acl, want_type);
344231200Smm
345231200Smm	/*
346231200Smm	 * If the only entries are the three standard ones,
347231200Smm	 * then don't return any ACL data.  (In this case,
348231200Smm	 * client can just use chmod(2) to set permissions.)
349231200Smm	 */
350231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
351231200Smm		cutoff = 3;
352231200Smm	else
353231200Smm		cutoff = 0;
354231200Smm
355231200Smm	if (count > cutoff)
356231200Smm		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
357231200Smm	else
358231200Smm		acl->acl_state = 0;
359231200Smm	acl->acl_p = acl->acl_head;
360231200Smm	return (count);
361231200Smm}
362231200Smm
363231200Smm
364231200Smm/*
365231200Smm * Return the next ACL entry in the list.  Fake entries for the
366231200Smm * standard permissions and include them in the returned list.
367231200Smm */
368231200Smmint
369231200Smmarchive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
370231200Smm    int *permset, int *tag, int *id, const char **name)
371231200Smm{
372231200Smm	*name = NULL;
373231200Smm	*id = -1;
374231200Smm
375231200Smm	/*
376231200Smm	 * The acl_state is either zero (no entries available), -1
377231200Smm	 * (reading from list), or an entry type (retrieve that type
378231200Smm	 * from ae_stat.aest_mode).
379231200Smm	 */
380231200Smm	if (acl->acl_state == 0)
381231200Smm		return (ARCHIVE_WARN);
382231200Smm
383231200Smm	/* The first three access entries are special. */
384231200Smm	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
385231200Smm		switch (acl->acl_state) {
386231200Smm		case ARCHIVE_ENTRY_ACL_USER_OBJ:
387231200Smm			*permset = (acl->mode >> 6) & 7;
388231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
389231200Smm			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
390231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
391231200Smm			return (ARCHIVE_OK);
392231200Smm		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
393231200Smm			*permset = (acl->mode >> 3) & 7;
394231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
395231200Smm			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
396231200Smm			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
397231200Smm			return (ARCHIVE_OK);
398231200Smm		case ARCHIVE_ENTRY_ACL_OTHER:
399231200Smm			*permset = acl->mode & 7;
400231200Smm			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
401231200Smm			*tag = ARCHIVE_ENTRY_ACL_OTHER;
402231200Smm			acl->acl_state = -1;
403231200Smm			acl->acl_p = acl->acl_head;
404231200Smm			return (ARCHIVE_OK);
405231200Smm		default:
406231200Smm			break;
407231200Smm		}
408231200Smm	}
409231200Smm
410231200Smm	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
411231200Smm		acl->acl_p = acl->acl_p->next;
412231200Smm	if (acl->acl_p == NULL) {
413231200Smm		acl->acl_state = 0;
414231200Smm		*type = 0;
415231200Smm		*permset = 0;
416231200Smm		*tag = 0;
417231200Smm		*id = -1;
418231200Smm		*name = NULL;
419231200Smm		return (ARCHIVE_EOF); /* End of ACL entries. */
420231200Smm	}
421231200Smm	*type = acl->acl_p->type;
422231200Smm	*permset = acl->acl_p->permset;
423231200Smm	*tag = acl->acl_p->tag;
424231200Smm	*id = acl->acl_p->id;
425238856Smm	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
426238856Smm		if (errno == ENOMEM)
427238856Smm			return (ARCHIVE_FATAL);
428231200Smm		*name = NULL;
429238856Smm	}
430231200Smm	acl->acl_p = acl->acl_p->next;
431231200Smm	return (ARCHIVE_OK);
432231200Smm}
433231200Smm
434231200Smm/*
435231200Smm * Generate a text version of the ACL.  The flags parameter controls
436231200Smm * the style of the generated ACL.
437231200Smm */
438231200Smmconst wchar_t *
439231200Smmarchive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
440231200Smm{
441231200Smm	int count;
442231200Smm	size_t length;
443231200Smm	const wchar_t *wname;
444231200Smm	const wchar_t *prefix;
445231200Smm	wchar_t separator;
446231200Smm	struct archive_acl_entry *ap;
447238856Smm	int id, r;
448231200Smm	wchar_t *wp;
449231200Smm
450231200Smm	if (acl->acl_text_w != NULL) {
451231200Smm		free (acl->acl_text_w);
452231200Smm		acl->acl_text_w = NULL;
453231200Smm	}
454231200Smm
455231200Smm	separator = L',';
456231200Smm	count = 0;
457231200Smm	length = 0;
458231200Smm	ap = acl->acl_head;
459231200Smm	while (ap != NULL) {
460231200Smm		if ((ap->type & flags) != 0) {
461231200Smm			count++;
462231200Smm			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
463231200Smm			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
464231200Smm				length += 8; /* "default:" */
465231200Smm			length += 5; /* tag name */
466231200Smm			length += 1; /* colon */
467238856Smm			r = archive_mstring_get_wcs(a, &ap->name, &wname);
468238856Smm			if (r == 0 && wname != NULL)
469231200Smm				length += wcslen(wname);
470238856Smm			else if (r < 0 && errno == ENOMEM)
471238856Smm				return (NULL);
472231200Smm			else
473231200Smm				length += sizeof(uid_t) * 3 + 1;
474231200Smm			length ++; /* colon */
475231200Smm			length += 3; /* rwx */
476231200Smm			length += 1; /* colon */
477231200Smm			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
478231200Smm			length ++; /* newline */
479231200Smm		}
480231200Smm		ap = ap->next;
481231200Smm	}
482231200Smm
483231200Smm	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
484231200Smm		length += 10; /* "user::rwx\n" */
485231200Smm		length += 11; /* "group::rwx\n" */
486231200Smm		length += 11; /* "other::rwx\n" */
487231200Smm	}
488231200Smm
489231200Smm	if (count == 0)
490231200Smm		return (NULL);
491231200Smm
492231200Smm	/* Now, allocate the string and actually populate it. */
493231200Smm	wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
494231200Smm	if (wp == NULL)
495238856Smm		return (NULL);
496231200Smm	count = 0;
497231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
498231200Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
499231200Smm		    acl->mode & 0700, -1);
500231200Smm		*wp++ = ',';
501231200Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
502231200Smm		    acl->mode & 0070, -1);
503231200Smm		*wp++ = ',';
504231200Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
505231200Smm		    acl->mode & 0007, -1);
506231200Smm		count += 3;
507231200Smm
508231200Smm		ap = acl->acl_head;
509231200Smm		while (ap != NULL) {
510238856Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
511238856Smm				r = archive_mstring_get_wcs(a, &ap->name, &wname);
512238856Smm				if (r == 0) {
513238856Smm					*wp++ = separator;
514238856Smm					if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
515238856Smm						id = ap->id;
516238856Smm					else
517238856Smm						id = -1;
518238856Smm					append_entry_w(&wp, NULL, ap->tag, wname,
519238856Smm					    ap->permset, id);
520238856Smm					count++;
521238856Smm				} else if (r < 0 && errno == ENOMEM)
522238856Smm					return (NULL);
523231200Smm			}
524231200Smm			ap = ap->next;
525231200Smm		}
526231200Smm	}
527231200Smm
528231200Smm
529231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
530231200Smm		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
531231200Smm			prefix = L"default:";
532231200Smm		else
533231200Smm			prefix = NULL;
534231200Smm		ap = acl->acl_head;
535231200Smm		count = 0;
536231200Smm		while (ap != NULL) {
537238856Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
538238856Smm				r = archive_mstring_get_wcs(a, &ap->name, &wname);
539238856Smm				if (r == 0) {
540238856Smm					if (count > 0)
541238856Smm						*wp++ = separator;
542238856Smm					if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
543238856Smm						id = ap->id;
544238856Smm					else
545238856Smm						id = -1;
546238856Smm					append_entry_w(&wp, prefix, ap->tag,
547238856Smm					    wname, ap->permset, id);
548238856Smm					count ++;
549238856Smm				} else if (r < 0 && errno == ENOMEM)
550238856Smm					return (NULL);
551231200Smm			}
552231200Smm			ap = ap->next;
553231200Smm		}
554231200Smm	}
555231200Smm
556231200Smm	return (acl->acl_text_w);
557231200Smm}
558231200Smm
559231200Smm
560231200Smmstatic void
561231200Smmappend_id_w(wchar_t **wp, int id)
562231200Smm{
563231200Smm	if (id < 0)
564231200Smm		id = 0;
565231200Smm	if (id > 9)
566231200Smm		append_id_w(wp, id / 10);
567231200Smm	*(*wp)++ = L"0123456789"[id % 10];
568231200Smm}
569231200Smm
570231200Smmstatic void
571231200Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
572231200Smm    const wchar_t *wname, int perm, int id)
573231200Smm{
574231200Smm	if (prefix != NULL) {
575231200Smm		wcscpy(*wp, prefix);
576231200Smm		*wp += wcslen(*wp);
577231200Smm	}
578231200Smm	switch (tag) {
579231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
580231200Smm		wname = NULL;
581231200Smm		id = -1;
582231200Smm		/* FALLTHROUGH */
583231200Smm	case ARCHIVE_ENTRY_ACL_USER:
584231200Smm		wcscpy(*wp, L"user");
585231200Smm		break;
586231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
587231200Smm		wname = NULL;
588231200Smm		id = -1;
589231200Smm		/* FALLTHROUGH */
590231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
591231200Smm		wcscpy(*wp, L"group");
592231200Smm		break;
593231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
594231200Smm		wcscpy(*wp, L"mask");
595231200Smm		wname = NULL;
596231200Smm		id = -1;
597231200Smm		break;
598231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
599231200Smm		wcscpy(*wp, L"other");
600231200Smm		wname = NULL;
601231200Smm		id = -1;
602231200Smm		break;
603231200Smm	}
604231200Smm	*wp += wcslen(*wp);
605231200Smm	*(*wp)++ = L':';
606231200Smm	if (wname != NULL) {
607231200Smm		wcscpy(*wp, wname);
608231200Smm		*wp += wcslen(*wp);
609231200Smm	} else if (tag == ARCHIVE_ENTRY_ACL_USER
610231200Smm	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
611231200Smm		append_id_w(wp, id);
612231200Smm		id = -1;
613231200Smm	}
614231200Smm	*(*wp)++ = L':';
615231200Smm	*(*wp)++ = (perm & 0444) ? L'r' : L'-';
616231200Smm	*(*wp)++ = (perm & 0222) ? L'w' : L'-';
617231200Smm	*(*wp)++ = (perm & 0111) ? L'x' : L'-';
618231200Smm	if (id != -1) {
619231200Smm		*(*wp)++ = L':';
620231200Smm		append_id_w(wp, id);
621231200Smm	}
622231200Smm	**wp = L'\0';
623231200Smm}
624231200Smm
625231200Smmint
626231200Smmarchive_acl_text_l(struct archive_acl *acl, int flags,
627231200Smm    const char **acl_text, size_t *acl_text_len,
628231200Smm    struct archive_string_conv *sc)
629231200Smm{
630231200Smm	int count;
631231200Smm	size_t length;
632231200Smm	const char *name;
633231200Smm	const char *prefix;
634231200Smm	char separator;
635231200Smm	struct archive_acl_entry *ap;
636231200Smm	size_t len;
637231200Smm	int id, r;
638231200Smm	char *p;
639231200Smm
640231200Smm	if (acl->acl_text != NULL) {
641231200Smm		free (acl->acl_text);
642231200Smm		acl->acl_text = NULL;
643231200Smm	}
644231200Smm
645231200Smm	*acl_text = NULL;
646231200Smm	if (acl_text_len != NULL)
647231200Smm		*acl_text_len = 0;
648231200Smm	separator = ',';
649231200Smm	count = 0;
650231200Smm	length = 0;
651231200Smm	ap = acl->acl_head;
652231200Smm	while (ap != NULL) {
653231200Smm		if ((ap->type & flags) != 0) {
654231200Smm			count++;
655231200Smm			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
656231200Smm			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
657231200Smm				length += 8; /* "default:" */
658231200Smm			length += 5; /* tag name */
659231200Smm			length += 1; /* colon */
660231200Smm			r = archive_mstring_get_mbs_l(
661231200Smm			    &ap->name, &name, &len, sc);
662231200Smm			if (r != 0)
663231200Smm				return (-1);
664231200Smm			if (len > 0 && name != NULL)
665231200Smm				length += len;
666231200Smm			else
667231200Smm				length += sizeof(uid_t) * 3 + 1;
668231200Smm			length ++; /* colon */
669231200Smm			length += 3; /* rwx */
670231200Smm			length += 1; /* colon */
671231200Smm			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
672231200Smm			length ++; /* newline */
673231200Smm		}
674231200Smm		ap = ap->next;
675231200Smm	}
676231200Smm
677231200Smm	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
678231200Smm		length += 10; /* "user::rwx\n" */
679231200Smm		length += 11; /* "group::rwx\n" */
680231200Smm		length += 11; /* "other::rwx\n" */
681231200Smm	}
682231200Smm
683231200Smm	if (count == 0)
684231200Smm		return (0);
685231200Smm
686231200Smm	/* Now, allocate the string and actually populate it. */
687231200Smm	p = acl->acl_text = (char *)malloc(length);
688231200Smm	if (p == NULL)
689238856Smm		return (-1);
690231200Smm	count = 0;
691231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
692231200Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
693231200Smm		    acl->mode & 0700, -1);
694231200Smm		*p++ = ',';
695231200Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
696231200Smm		    acl->mode & 0070, -1);
697231200Smm		*p++ = ',';
698231200Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
699231200Smm		    acl->mode & 0007, -1);
700231200Smm		count += 3;
701231200Smm
702231200Smm		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
703231200Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
704231200Smm				continue;
705231200Smm			r = archive_mstring_get_mbs_l(
706231200Smm			    &ap->name, &name, &len, sc);
707231200Smm			if (r != 0)
708231200Smm				return (-1);
709231200Smm			*p++ = separator;
710231200Smm			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
711231200Smm				id = ap->id;
712231200Smm			else
713231200Smm				id = -1;
714231200Smm			append_entry(&p, NULL, ap->tag, name,
715231200Smm			    ap->permset, id);
716231200Smm			count++;
717231200Smm		}
718231200Smm	}
719231200Smm
720231200Smm
721231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
722231200Smm		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
723231200Smm			prefix = "default:";
724231200Smm		else
725231200Smm			prefix = NULL;
726231200Smm		count = 0;
727231200Smm		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
728231200Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
729231200Smm				continue;
730231200Smm			r = archive_mstring_get_mbs_l(
731231200Smm			    &ap->name, &name, &len, sc);
732231200Smm			if (r != 0)
733231200Smm				return (-1);
734231200Smm			if (count > 0)
735231200Smm				*p++ = separator;
736231200Smm			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
737231200Smm				id = ap->id;
738231200Smm			else
739231200Smm				id = -1;
740231200Smm			append_entry(&p, prefix, ap->tag,
741231200Smm			    name, ap->permset, id);
742231200Smm			count ++;
743231200Smm		}
744231200Smm	}
745231200Smm
746231200Smm	*acl_text = acl->acl_text;
747231200Smm	if (acl_text_len != NULL)
748231200Smm		*acl_text_len = strlen(acl->acl_text);
749231200Smm	return (0);
750231200Smm}
751231200Smm
752231200Smmstatic void
753231200Smmappend_id(char **p, int id)
754231200Smm{
755231200Smm	if (id < 0)
756231200Smm		id = 0;
757231200Smm	if (id > 9)
758231200Smm		append_id(p, id / 10);
759231200Smm	*(*p)++ = "0123456789"[id % 10];
760231200Smm}
761231200Smm
762231200Smmstatic void
763231200Smmappend_entry(char **p, const char *prefix, int tag,
764231200Smm    const char *name, int perm, int id)
765231200Smm{
766231200Smm	if (prefix != NULL) {
767231200Smm		strcpy(*p, prefix);
768231200Smm		*p += strlen(*p);
769231200Smm	}
770231200Smm	switch (tag) {
771231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
772231200Smm		name = NULL;
773231200Smm		id = -1;
774231200Smm		/* FALLTHROUGH */
775231200Smm	case ARCHIVE_ENTRY_ACL_USER:
776231200Smm		strcpy(*p, "user");
777231200Smm		break;
778231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
779231200Smm		name = NULL;
780231200Smm		id = -1;
781231200Smm		/* FALLTHROUGH */
782231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
783231200Smm		strcpy(*p, "group");
784231200Smm		break;
785231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
786231200Smm		strcpy(*p, "mask");
787231200Smm		name = NULL;
788231200Smm		id = -1;
789231200Smm		break;
790231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
791231200Smm		strcpy(*p, "other");
792231200Smm		name = NULL;
793231200Smm		id = -1;
794231200Smm		break;
795231200Smm	}
796231200Smm	*p += strlen(*p);
797231200Smm	*(*p)++ = ':';
798231200Smm	if (name != NULL) {
799231200Smm		strcpy(*p, name);
800231200Smm		*p += strlen(*p);
801231200Smm	} else if (tag == ARCHIVE_ENTRY_ACL_USER
802231200Smm	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
803231200Smm		append_id(p, id);
804231200Smm		id = -1;
805231200Smm	}
806231200Smm	*(*p)++ = ':';
807231200Smm	*(*p)++ = (perm & 0444) ? 'r' : '-';
808231200Smm	*(*p)++ = (perm & 0222) ? 'w' : '-';
809231200Smm	*(*p)++ = (perm & 0111) ? 'x' : '-';
810231200Smm	if (id != -1) {
811231200Smm		*(*p)++ = ':';
812231200Smm		append_id(p, id);
813231200Smm	}
814231200Smm	**p = '\0';
815231200Smm}
816231200Smm
817231200Smm/*
818231200Smm * Parse a textual ACL.  This automatically recognizes and supports
819231200Smm * extensions described above.  The 'type' argument is used to
820231200Smm * indicate the type that should be used for any entries not
821231200Smm * explicitly marked as "default:".
822231200Smm */
823231200Smmint
824231200Smmarchive_acl_parse_w(struct archive_acl *acl,
825231200Smm    const wchar_t *text, int default_type)
826231200Smm{
827231200Smm	struct {
828231200Smm		const wchar_t *start;
829231200Smm		const wchar_t *end;
830231200Smm	} field[4], name;
831231200Smm
832231200Smm	int fields, n;
833231200Smm	int type, tag, permset, id;
834231200Smm	wchar_t sep;
835231200Smm
836231200Smm	while (text != NULL  &&  *text != L'\0') {
837231200Smm		/*
838231200Smm		 * Parse the fields out of the next entry,
839231200Smm		 * advance 'text' to start of next entry.
840231200Smm		 */
841231200Smm		fields = 0;
842231200Smm		do {
843231200Smm			const wchar_t *start, *end;
844231200Smm			next_field_w(&text, &start, &end, &sep);
845231200Smm			if (fields < 4) {
846231200Smm				field[fields].start = start;
847231200Smm				field[fields].end = end;
848231200Smm			}
849231200Smm			++fields;
850231200Smm		} while (sep == L':');
851231200Smm
852231200Smm		/* Set remaining fields to blank. */
853231200Smm		for (n = fields; n < 4; ++n)
854231200Smm			field[n].start = field[n].end = NULL;
855231200Smm
856231200Smm		/* Check for a numeric ID in field 1 or 3. */
857231200Smm		id = -1;
858231200Smm		isint_w(field[1].start, field[1].end, &id);
859231200Smm		/* Field 3 is optional. */
860231200Smm		if (id == -1 && fields > 3)
861231200Smm			isint_w(field[3].start, field[3].end, &id);
862231200Smm
863231200Smm		/*
864231200Smm		 * Solaris extension:  "defaultuser::rwx" is the
865231200Smm		 * default ACL corresponding to "user::rwx", etc.
866231200Smm		 */
867231200Smm		if (field[0].end - field[0].start > 7
868231200Smm		    && wmemcmp(field[0].start, L"default", 7) == 0) {
869231200Smm			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
870231200Smm			field[0].start += 7;
871231200Smm		} else
872231200Smm			type = default_type;
873231200Smm
874231200Smm		name.start = name.end = NULL;
875231200Smm		if (prefix_w(field[0].start, field[0].end, L"user")) {
876231200Smm			if (!ismode_w(field[2].start, field[2].end, &permset))
877231200Smm				return (ARCHIVE_WARN);
878231200Smm			if (id != -1 || field[1].start < field[1].end) {
879231200Smm				tag = ARCHIVE_ENTRY_ACL_USER;
880231200Smm				name = field[1];
881231200Smm			} else
882231200Smm				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
883231200Smm		} else if (prefix_w(field[0].start, field[0].end, L"group")) {
884231200Smm			if (!ismode_w(field[2].start, field[2].end, &permset))
885231200Smm				return (ARCHIVE_WARN);
886231200Smm			if (id != -1 || field[1].start < field[1].end) {
887231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP;
888231200Smm				name = field[1];
889231200Smm			} else
890231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
891231200Smm		} else if (prefix_w(field[0].start, field[0].end, L"other")) {
892231200Smm			if (fields == 2
893231200Smm			    && field[1].start < field[1].end
894231200Smm			    && ismode_w(field[1].start, field[1].end, &permset)) {
895231200Smm				/* This is Solaris-style "other:rwx" */
896231200Smm			} else if (fields == 3
897231200Smm			    && field[1].start == field[1].end
898231200Smm			    && field[2].start < field[2].end
899231200Smm			    && ismode_w(field[2].start, field[2].end, &permset)) {
900231200Smm				/* This is FreeBSD-style "other::rwx" */
901231200Smm			} else
902231200Smm				return (ARCHIVE_WARN);
903231200Smm			tag = ARCHIVE_ENTRY_ACL_OTHER;
904231200Smm		} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
905231200Smm			if (fields == 2
906231200Smm			    && field[1].start < field[1].end
907231200Smm			    && ismode_w(field[1].start, field[1].end, &permset)) {
908231200Smm				/* This is Solaris-style "mask:rwx" */
909231200Smm			} else if (fields == 3
910231200Smm			    && field[1].start == field[1].end
911231200Smm			    && field[2].start < field[2].end
912231200Smm			    && ismode_w(field[2].start, field[2].end, &permset)) {
913231200Smm				/* This is FreeBSD-style "mask::rwx" */
914231200Smm			} else
915231200Smm				return (ARCHIVE_WARN);
916231200Smm			tag = ARCHIVE_ENTRY_ACL_MASK;
917231200Smm		} else
918231200Smm			return (ARCHIVE_WARN);
919231200Smm
920231200Smm		/* Add entry to the internal list. */
921231200Smm		archive_acl_add_entry_w_len(acl, type, permset,
922231200Smm		    tag, id, name.start, name.end - name.start);
923231200Smm	}
924231200Smm	return (ARCHIVE_OK);
925231200Smm}
926231200Smm
927231200Smm/*
928231200Smm * Parse a string to a positive decimal integer.  Returns true if
929231200Smm * the string is non-empty and consists only of decimal digits,
930231200Smm * false otherwise.
931231200Smm */
932231200Smmstatic int
933231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result)
934231200Smm{
935231200Smm	int n = 0;
936231200Smm	if (start >= end)
937231200Smm		return (0);
938231200Smm	while (start < end) {
939231200Smm		if (*start < '0' || *start > '9')
940231200Smm			return (0);
941231200Smm		if (n > (INT_MAX / 10) ||
942231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
943231200Smm			n = INT_MAX;
944231200Smm		} else {
945231200Smm			n *= 10;
946231200Smm			n += *start - '0';
947231200Smm		}
948231200Smm		start++;
949231200Smm	}
950231200Smm	*result = n;
951231200Smm	return (1);
952231200Smm}
953231200Smm
954231200Smm/*
955231200Smm * Parse a string as a mode field.  Returns true if
956231200Smm * the string is non-empty and consists only of mode characters,
957231200Smm * false otherwise.
958231200Smm */
959231200Smmstatic int
960231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset)
961231200Smm{
962231200Smm	const wchar_t *p;
963231200Smm
964231200Smm	if (start >= end)
965231200Smm		return (0);
966231200Smm	p = start;
967231200Smm	*permset = 0;
968231200Smm	while (p < end) {
969231200Smm		switch (*p++) {
970231200Smm		case 'r': case 'R':
971231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
972231200Smm			break;
973231200Smm		case 'w': case 'W':
974231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
975231200Smm			break;
976231200Smm		case 'x': case 'X':
977231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
978231200Smm			break;
979231200Smm		case '-':
980231200Smm			break;
981231200Smm		default:
982231200Smm			return (0);
983231200Smm		}
984231200Smm	}
985231200Smm	return (1);
986231200Smm}
987231200Smm
988231200Smm/*
989231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
990231200Smm * to point to just after the separator.  *start points to the first
991231200Smm * character of the matched text and *end just after the last
992231200Smm * character of the matched identifier.  In particular *end - *start
993231200Smm * is the length of the field body, not including leading or trailing
994231200Smm * whitespace.
995231200Smm */
996231200Smmstatic void
997231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start,
998231200Smm    const wchar_t **end, wchar_t *sep)
999231200Smm{
1000231200Smm	/* Skip leading whitespace to find start of field. */
1001231200Smm	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1002231200Smm		(*wp)++;
1003231200Smm	}
1004231200Smm	*start = *wp;
1005231200Smm
1006231200Smm	/* Scan for the separator. */
1007231200Smm	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1008231200Smm	    **wp != L'\n') {
1009231200Smm		(*wp)++;
1010231200Smm	}
1011231200Smm	*sep = **wp;
1012231200Smm
1013231200Smm	/* Trim trailing whitespace to locate end of field. */
1014231200Smm	*end = *wp - 1;
1015231200Smm	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1016231200Smm		(*end)--;
1017231200Smm	}
1018231200Smm	(*end)++;
1019231200Smm
1020231200Smm	/* Adjust scanner location. */
1021231200Smm	if (**wp != L'\0')
1022231200Smm		(*wp)++;
1023231200Smm}
1024231200Smm
1025231200Smm/*
1026231200Smm * Return true if the characters [start...end) are a prefix of 'test'.
1027231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1028231200Smm */
1029231200Smmstatic int
1030231200Smmprefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1031231200Smm{
1032231200Smm	if (start == end)
1033231200Smm		return (0);
1034231200Smm
1035231200Smm	if (*start++ != *test++)
1036231200Smm		return (0);
1037231200Smm
1038231200Smm	while (start < end  &&  *start++ == *test++)
1039231200Smm		;
1040231200Smm
1041231200Smm	if (start < end)
1042231200Smm		return (0);
1043231200Smm
1044231200Smm	return (1);
1045231200Smm}
1046231200Smm
1047231200Smm/*
1048231200Smm * Parse a textual ACL.  This automatically recognizes and supports
1049231200Smm * extensions described above.  The 'type' argument is used to
1050231200Smm * indicate the type that should be used for any entries not
1051231200Smm * explicitly marked as "default:".
1052231200Smm */
1053231200Smmint
1054231200Smmarchive_acl_parse_l(struct archive_acl *acl,
1055231200Smm    const char *text, int default_type, struct archive_string_conv *sc)
1056231200Smm{
1057231200Smm	struct {
1058231200Smm		const char *start;
1059231200Smm		const char *end;
1060231200Smm	} field[4], name;
1061231200Smm
1062231200Smm	int fields, n, r, ret = ARCHIVE_OK;
1063231200Smm	int type, tag, permset, id;
1064231200Smm	char sep;
1065231200Smm
1066231200Smm	while (text != NULL  &&  *text != '\0') {
1067231200Smm		/*
1068231200Smm		 * Parse the fields out of the next entry,
1069231200Smm		 * advance 'text' to start of next entry.
1070231200Smm		 */
1071231200Smm		fields = 0;
1072231200Smm		do {
1073231200Smm			const char *start, *end;
1074231200Smm			next_field(&text, &start, &end, &sep);
1075231200Smm			if (fields < 4) {
1076231200Smm				field[fields].start = start;
1077231200Smm				field[fields].end = end;
1078231200Smm			}
1079231200Smm			++fields;
1080231200Smm		} while (sep == ':');
1081231200Smm
1082231200Smm		/* Set remaining fields to blank. */
1083231200Smm		for (n = fields; n < 4; ++n)
1084231200Smm			field[n].start = field[n].end = NULL;
1085231200Smm
1086231200Smm		/* Check for a numeric ID in field 1 or 3. */
1087231200Smm		id = -1;
1088231200Smm		isint(field[1].start, field[1].end, &id);
1089231200Smm		/* Field 3 is optional. */
1090231200Smm		if (id == -1 && fields > 3)
1091231200Smm			isint(field[3].start, field[3].end, &id);
1092231200Smm
1093231200Smm		/*
1094231200Smm		 * Solaris extension:  "defaultuser::rwx" is the
1095231200Smm		 * default ACL corresponding to "user::rwx", etc.
1096231200Smm		 */
1097231200Smm		if (field[0].end - field[0].start > 7
1098231200Smm		    && memcmp(field[0].start, "default", 7) == 0) {
1099231200Smm			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1100231200Smm			field[0].start += 7;
1101231200Smm		} else
1102231200Smm			type = default_type;
1103231200Smm
1104231200Smm		name.start = name.end = NULL;
1105232153Smm		if (prefix_c(field[0].start, field[0].end, "user")) {
1106231200Smm			if (!ismode(field[2].start, field[2].end, &permset))
1107231200Smm				return (ARCHIVE_WARN);
1108231200Smm			if (id != -1 || field[1].start < field[1].end) {
1109231200Smm				tag = ARCHIVE_ENTRY_ACL_USER;
1110231200Smm				name = field[1];
1111231200Smm			} else
1112231200Smm				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1113232153Smm		} else if (prefix_c(field[0].start, field[0].end, "group")) {
1114231200Smm			if (!ismode(field[2].start, field[2].end, &permset))
1115231200Smm				return (ARCHIVE_WARN);
1116231200Smm			if (id != -1 || field[1].start < field[1].end) {
1117231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP;
1118231200Smm				name = field[1];
1119231200Smm			} else
1120231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1121232153Smm		} else if (prefix_c(field[0].start, field[0].end, "other")) {
1122231200Smm			if (fields == 2
1123231200Smm			    && field[1].start < field[1].end
1124231200Smm			    && ismode(field[1].start, field[1].end, &permset)) {
1125231200Smm				/* This is Solaris-style "other:rwx" */
1126231200Smm			} else if (fields == 3
1127231200Smm			    && field[1].start == field[1].end
1128231200Smm			    && field[2].start < field[2].end
1129231200Smm			    && ismode(field[2].start, field[2].end, &permset)) {
1130231200Smm				/* This is FreeBSD-style "other::rwx" */
1131231200Smm			} else
1132231200Smm				return (ARCHIVE_WARN);
1133231200Smm			tag = ARCHIVE_ENTRY_ACL_OTHER;
1134232153Smm		} else if (prefix_c(field[0].start, field[0].end, "mask")) {
1135231200Smm			if (fields == 2
1136231200Smm			    && field[1].start < field[1].end
1137231200Smm			    && ismode(field[1].start, field[1].end, &permset)) {
1138231200Smm				/* This is Solaris-style "mask:rwx" */
1139231200Smm			} else if (fields == 3
1140231200Smm			    && field[1].start == field[1].end
1141231200Smm			    && field[2].start < field[2].end
1142231200Smm			    && ismode(field[2].start, field[2].end, &permset)) {
1143231200Smm				/* This is FreeBSD-style "mask::rwx" */
1144231200Smm			} else
1145231200Smm				return (ARCHIVE_WARN);
1146231200Smm			tag = ARCHIVE_ENTRY_ACL_MASK;
1147231200Smm		} else
1148231200Smm			return (ARCHIVE_WARN);
1149231200Smm
1150231200Smm		/* Add entry to the internal list. */
1151231200Smm		r = archive_acl_add_entry_len_l(acl, type, permset,
1152231200Smm		    tag, id, name.start, name.end - name.start, sc);
1153231200Smm		if (r < ARCHIVE_WARN)
1154231200Smm			return (r);
1155231200Smm		if (r != ARCHIVE_OK)
1156231200Smm			ret = ARCHIVE_WARN;
1157231200Smm	}
1158231200Smm	return (ret);
1159231200Smm}
1160231200Smm
1161231200Smm/*
1162231200Smm * Parse a string to a positive decimal integer.  Returns true if
1163231200Smm * the string is non-empty and consists only of decimal digits,
1164231200Smm * false otherwise.
1165231200Smm */
1166231200Smmstatic int
1167231200Smmisint(const char *start, const char *end, int *result)
1168231200Smm{
1169231200Smm	int n = 0;
1170231200Smm	if (start >= end)
1171231200Smm		return (0);
1172231200Smm	while (start < end) {
1173231200Smm		if (*start < '0' || *start > '9')
1174231200Smm			return (0);
1175231200Smm		if (n > (INT_MAX / 10) ||
1176231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1177231200Smm			n = INT_MAX;
1178231200Smm		} else {
1179231200Smm			n *= 10;
1180231200Smm			n += *start - '0';
1181231200Smm		}
1182231200Smm		start++;
1183231200Smm	}
1184231200Smm	*result = n;
1185231200Smm	return (1);
1186231200Smm}
1187231200Smm
1188231200Smm/*
1189231200Smm * Parse a string as a mode field.  Returns true if
1190231200Smm * the string is non-empty and consists only of mode characters,
1191231200Smm * false otherwise.
1192231200Smm */
1193231200Smmstatic int
1194231200Smmismode(const char *start, const char *end, int *permset)
1195231200Smm{
1196231200Smm	const char *p;
1197231200Smm
1198231200Smm	if (start >= end)
1199231200Smm		return (0);
1200231200Smm	p = start;
1201231200Smm	*permset = 0;
1202231200Smm	while (p < end) {
1203231200Smm		switch (*p++) {
1204231200Smm		case 'r': case 'R':
1205231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1206231200Smm			break;
1207231200Smm		case 'w': case 'W':
1208231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1209231200Smm			break;
1210231200Smm		case 'x': case 'X':
1211231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1212231200Smm			break;
1213231200Smm		case '-':
1214231200Smm			break;
1215231200Smm		default:
1216231200Smm			return (0);
1217231200Smm		}
1218231200Smm	}
1219231200Smm	return (1);
1220231200Smm}
1221231200Smm
1222231200Smm/*
1223231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1224231200Smm * to point to just after the separator.  *start points to the first
1225231200Smm * character of the matched text and *end just after the last
1226231200Smm * character of the matched identifier.  In particular *end - *start
1227231200Smm * is the length of the field body, not including leading or trailing
1228231200Smm * whitespace.
1229231200Smm */
1230231200Smmstatic void
1231231200Smmnext_field(const char **p, const char **start,
1232231200Smm    const char **end, char *sep)
1233231200Smm{
1234231200Smm	/* Skip leading whitespace to find start of field. */
1235231200Smm	while (**p == ' ' || **p == '\t' || **p == '\n') {
1236231200Smm		(*p)++;
1237231200Smm	}
1238231200Smm	*start = *p;
1239231200Smm
1240231200Smm	/* Scan for the separator. */
1241231200Smm	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1242231200Smm		(*p)++;
1243231200Smm	}
1244231200Smm	*sep = **p;
1245231200Smm
1246231200Smm	/* Trim trailing whitespace to locate end of field. */
1247231200Smm	*end = *p - 1;
1248231200Smm	while (**end == ' ' || **end == '\t' || **end == '\n') {
1249231200Smm		(*end)--;
1250231200Smm	}
1251231200Smm	(*end)++;
1252231200Smm
1253231200Smm	/* Adjust scanner location. */
1254231200Smm	if (**p != '\0')
1255231200Smm		(*p)++;
1256231200Smm}
1257231200Smm
1258231200Smm/*
1259231200Smm * Return true if the characters [start...end) are a prefix of 'test'.
1260231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1261231200Smm */
1262231200Smmstatic int
1263232153Smmprefix_c(const char *start, const char *end, const char *test)
1264231200Smm{
1265231200Smm	if (start == end)
1266231200Smm		return (0);
1267231200Smm
1268231200Smm	if (*start++ != *test++)
1269231200Smm		return (0);
1270231200Smm
1271231200Smm	while (start < end  &&  *start++ == *test++)
1272231200Smm		;
1273231200Smm
1274231200Smm	if (start < end)
1275231200Smm		return (0);
1276231200Smm
1277231200Smm	return (1);
1278231200Smm}
1279