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