archive_acl.c revision 232153
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;
425231200Smm	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0)
426231200Smm		*name = NULL;
427231200Smm	acl->acl_p = acl->acl_p->next;
428231200Smm	return (ARCHIVE_OK);
429231200Smm}
430231200Smm
431231200Smm/*
432231200Smm * Generate a text version of the ACL.  The flags parameter controls
433231200Smm * the style of the generated ACL.
434231200Smm */
435231200Smmconst wchar_t *
436231200Smmarchive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
437231200Smm{
438231200Smm	int count;
439231200Smm	size_t length;
440231200Smm	const wchar_t *wname;
441231200Smm	const wchar_t *prefix;
442231200Smm	wchar_t separator;
443231200Smm	struct archive_acl_entry *ap;
444231200Smm	int id;
445231200Smm	wchar_t *wp;
446231200Smm
447231200Smm	if (acl->acl_text_w != NULL) {
448231200Smm		free (acl->acl_text_w);
449231200Smm		acl->acl_text_w = NULL;
450231200Smm	}
451231200Smm
452231200Smm	separator = L',';
453231200Smm	count = 0;
454231200Smm	length = 0;
455231200Smm	ap = acl->acl_head;
456231200Smm	while (ap != NULL) {
457231200Smm		if ((ap->type & flags) != 0) {
458231200Smm			count++;
459231200Smm			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
460231200Smm			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
461231200Smm				length += 8; /* "default:" */
462231200Smm			length += 5; /* tag name */
463231200Smm			length += 1; /* colon */
464231200Smm			if (archive_mstring_get_wcs(a, &ap->name, &wname) == 0 &&
465231200Smm			    wname != NULL)
466231200Smm				length += wcslen(wname);
467231200Smm			else
468231200Smm				length += sizeof(uid_t) * 3 + 1;
469231200Smm			length ++; /* colon */
470231200Smm			length += 3; /* rwx */
471231200Smm			length += 1; /* colon */
472231200Smm			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
473231200Smm			length ++; /* newline */
474231200Smm		}
475231200Smm		ap = ap->next;
476231200Smm	}
477231200Smm
478231200Smm	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
479231200Smm		length += 10; /* "user::rwx\n" */
480231200Smm		length += 11; /* "group::rwx\n" */
481231200Smm		length += 11; /* "other::rwx\n" */
482231200Smm	}
483231200Smm
484231200Smm	if (count == 0)
485231200Smm		return (NULL);
486231200Smm
487231200Smm	/* Now, allocate the string and actually populate it. */
488231200Smm	wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
489231200Smm	if (wp == NULL)
490231200Smm		__archive_errx(1, "No memory to generate the text version of the ACL");
491231200Smm	count = 0;
492231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
493231200Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
494231200Smm		    acl->mode & 0700, -1);
495231200Smm		*wp++ = ',';
496231200Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
497231200Smm		    acl->mode & 0070, -1);
498231200Smm		*wp++ = ',';
499231200Smm		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
500231200Smm		    acl->mode & 0007, -1);
501231200Smm		count += 3;
502231200Smm
503231200Smm		ap = acl->acl_head;
504231200Smm		while (ap != NULL) {
505231200Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 &&
506231200Smm				archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
507231200Smm				*wp++ = separator;
508231200Smm				if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
509231200Smm					id = ap->id;
510231200Smm				else
511231200Smm					id = -1;
512231200Smm				append_entry_w(&wp, NULL, ap->tag, wname,
513231200Smm				    ap->permset, id);
514231200Smm				count++;
515231200Smm			}
516231200Smm			ap = ap->next;
517231200Smm		}
518231200Smm	}
519231200Smm
520231200Smm
521231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
522231200Smm		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
523231200Smm			prefix = L"default:";
524231200Smm		else
525231200Smm			prefix = NULL;
526231200Smm		ap = acl->acl_head;
527231200Smm		count = 0;
528231200Smm		while (ap != NULL) {
529231200Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 &&
530231200Smm				archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
531231200Smm				if (count > 0)
532231200Smm					*wp++ = separator;
533231200Smm				if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
534231200Smm					id = ap->id;
535231200Smm				else
536231200Smm					id = -1;
537231200Smm				append_entry_w(&wp, prefix, ap->tag,
538231200Smm				    wname, ap->permset, id);
539231200Smm				count ++;
540231200Smm			}
541231200Smm			ap = ap->next;
542231200Smm		}
543231200Smm	}
544231200Smm
545231200Smm	return (acl->acl_text_w);
546231200Smm}
547231200Smm
548231200Smm
549231200Smmstatic void
550231200Smmappend_id_w(wchar_t **wp, int id)
551231200Smm{
552231200Smm	if (id < 0)
553231200Smm		id = 0;
554231200Smm	if (id > 9)
555231200Smm		append_id_w(wp, id / 10);
556231200Smm	*(*wp)++ = L"0123456789"[id % 10];
557231200Smm}
558231200Smm
559231200Smmstatic void
560231200Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
561231200Smm    const wchar_t *wname, int perm, int id)
562231200Smm{
563231200Smm	if (prefix != NULL) {
564231200Smm		wcscpy(*wp, prefix);
565231200Smm		*wp += wcslen(*wp);
566231200Smm	}
567231200Smm	switch (tag) {
568231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
569231200Smm		wname = NULL;
570231200Smm		id = -1;
571231200Smm		/* FALLTHROUGH */
572231200Smm	case ARCHIVE_ENTRY_ACL_USER:
573231200Smm		wcscpy(*wp, L"user");
574231200Smm		break;
575231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
576231200Smm		wname = NULL;
577231200Smm		id = -1;
578231200Smm		/* FALLTHROUGH */
579231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
580231200Smm		wcscpy(*wp, L"group");
581231200Smm		break;
582231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
583231200Smm		wcscpy(*wp, L"mask");
584231200Smm		wname = NULL;
585231200Smm		id = -1;
586231200Smm		break;
587231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
588231200Smm		wcscpy(*wp, L"other");
589231200Smm		wname = NULL;
590231200Smm		id = -1;
591231200Smm		break;
592231200Smm	}
593231200Smm	*wp += wcslen(*wp);
594231200Smm	*(*wp)++ = L':';
595231200Smm	if (wname != NULL) {
596231200Smm		wcscpy(*wp, wname);
597231200Smm		*wp += wcslen(*wp);
598231200Smm	} else if (tag == ARCHIVE_ENTRY_ACL_USER
599231200Smm	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
600231200Smm		append_id_w(wp, id);
601231200Smm		id = -1;
602231200Smm	}
603231200Smm	*(*wp)++ = L':';
604231200Smm	*(*wp)++ = (perm & 0444) ? L'r' : L'-';
605231200Smm	*(*wp)++ = (perm & 0222) ? L'w' : L'-';
606231200Smm	*(*wp)++ = (perm & 0111) ? L'x' : L'-';
607231200Smm	if (id != -1) {
608231200Smm		*(*wp)++ = L':';
609231200Smm		append_id_w(wp, id);
610231200Smm	}
611231200Smm	**wp = L'\0';
612231200Smm}
613231200Smm
614231200Smmint
615231200Smmarchive_acl_text_l(struct archive_acl *acl, int flags,
616231200Smm    const char **acl_text, size_t *acl_text_len,
617231200Smm    struct archive_string_conv *sc)
618231200Smm{
619231200Smm	int count;
620231200Smm	size_t length;
621231200Smm	const char *name;
622231200Smm	const char *prefix;
623231200Smm	char separator;
624231200Smm	struct archive_acl_entry *ap;
625231200Smm	size_t len;
626231200Smm	int id, r;
627231200Smm	char *p;
628231200Smm
629231200Smm	if (acl->acl_text != NULL) {
630231200Smm		free (acl->acl_text);
631231200Smm		acl->acl_text = NULL;
632231200Smm	}
633231200Smm
634231200Smm	*acl_text = NULL;
635231200Smm	if (acl_text_len != NULL)
636231200Smm		*acl_text_len = 0;
637231200Smm	separator = ',';
638231200Smm	count = 0;
639231200Smm	length = 0;
640231200Smm	ap = acl->acl_head;
641231200Smm	while (ap != NULL) {
642231200Smm		if ((ap->type & flags) != 0) {
643231200Smm			count++;
644231200Smm			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
645231200Smm			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
646231200Smm				length += 8; /* "default:" */
647231200Smm			length += 5; /* tag name */
648231200Smm			length += 1; /* colon */
649231200Smm			r = archive_mstring_get_mbs_l(
650231200Smm			    &ap->name, &name, &len, sc);
651231200Smm			if (r != 0)
652231200Smm				return (-1);
653231200Smm			if (len > 0 && name != NULL)
654231200Smm				length += len;
655231200Smm			else
656231200Smm				length += sizeof(uid_t) * 3 + 1;
657231200Smm			length ++; /* colon */
658231200Smm			length += 3; /* rwx */
659231200Smm			length += 1; /* colon */
660231200Smm			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
661231200Smm			length ++; /* newline */
662231200Smm		}
663231200Smm		ap = ap->next;
664231200Smm	}
665231200Smm
666231200Smm	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
667231200Smm		length += 10; /* "user::rwx\n" */
668231200Smm		length += 11; /* "group::rwx\n" */
669231200Smm		length += 11; /* "other::rwx\n" */
670231200Smm	}
671231200Smm
672231200Smm	if (count == 0)
673231200Smm		return (0);
674231200Smm
675231200Smm	/* Now, allocate the string and actually populate it. */
676231200Smm	p = acl->acl_text = (char *)malloc(length);
677231200Smm	if (p == NULL)
678231200Smm		__archive_errx(1, "No memory to generate the text version of the ACL");
679231200Smm	count = 0;
680231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
681231200Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
682231200Smm		    acl->mode & 0700, -1);
683231200Smm		*p++ = ',';
684231200Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
685231200Smm		    acl->mode & 0070, -1);
686231200Smm		*p++ = ',';
687231200Smm		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
688231200Smm		    acl->mode & 0007, -1);
689231200Smm		count += 3;
690231200Smm
691231200Smm		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
692231200Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
693231200Smm				continue;
694231200Smm			r = archive_mstring_get_mbs_l(
695231200Smm			    &ap->name, &name, &len, sc);
696231200Smm			if (r != 0)
697231200Smm				return (-1);
698231200Smm			*p++ = separator;
699231200Smm			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
700231200Smm				id = ap->id;
701231200Smm			else
702231200Smm				id = -1;
703231200Smm			append_entry(&p, NULL, ap->tag, name,
704231200Smm			    ap->permset, id);
705231200Smm			count++;
706231200Smm		}
707231200Smm	}
708231200Smm
709231200Smm
710231200Smm	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
711231200Smm		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
712231200Smm			prefix = "default:";
713231200Smm		else
714231200Smm			prefix = NULL;
715231200Smm		count = 0;
716231200Smm		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
717231200Smm			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
718231200Smm				continue;
719231200Smm			r = archive_mstring_get_mbs_l(
720231200Smm			    &ap->name, &name, &len, sc);
721231200Smm			if (r != 0)
722231200Smm				return (-1);
723231200Smm			if (count > 0)
724231200Smm				*p++ = separator;
725231200Smm			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
726231200Smm				id = ap->id;
727231200Smm			else
728231200Smm				id = -1;
729231200Smm			append_entry(&p, prefix, ap->tag,
730231200Smm			    name, ap->permset, id);
731231200Smm			count ++;
732231200Smm		}
733231200Smm	}
734231200Smm
735231200Smm	*acl_text = acl->acl_text;
736231200Smm	if (acl_text_len != NULL)
737231200Smm		*acl_text_len = strlen(acl->acl_text);
738231200Smm	return (0);
739231200Smm}
740231200Smm
741231200Smmstatic void
742231200Smmappend_id(char **p, int id)
743231200Smm{
744231200Smm	if (id < 0)
745231200Smm		id = 0;
746231200Smm	if (id > 9)
747231200Smm		append_id(p, id / 10);
748231200Smm	*(*p)++ = "0123456789"[id % 10];
749231200Smm}
750231200Smm
751231200Smmstatic void
752231200Smmappend_entry(char **p, const char *prefix, int tag,
753231200Smm    const char *name, int perm, int id)
754231200Smm{
755231200Smm	if (prefix != NULL) {
756231200Smm		strcpy(*p, prefix);
757231200Smm		*p += strlen(*p);
758231200Smm	}
759231200Smm	switch (tag) {
760231200Smm	case ARCHIVE_ENTRY_ACL_USER_OBJ:
761231200Smm		name = NULL;
762231200Smm		id = -1;
763231200Smm		/* FALLTHROUGH */
764231200Smm	case ARCHIVE_ENTRY_ACL_USER:
765231200Smm		strcpy(*p, "user");
766231200Smm		break;
767231200Smm	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
768231200Smm		name = NULL;
769231200Smm		id = -1;
770231200Smm		/* FALLTHROUGH */
771231200Smm	case ARCHIVE_ENTRY_ACL_GROUP:
772231200Smm		strcpy(*p, "group");
773231200Smm		break;
774231200Smm	case ARCHIVE_ENTRY_ACL_MASK:
775231200Smm		strcpy(*p, "mask");
776231200Smm		name = NULL;
777231200Smm		id = -1;
778231200Smm		break;
779231200Smm	case ARCHIVE_ENTRY_ACL_OTHER:
780231200Smm		strcpy(*p, "other");
781231200Smm		name = NULL;
782231200Smm		id = -1;
783231200Smm		break;
784231200Smm	}
785231200Smm	*p += strlen(*p);
786231200Smm	*(*p)++ = ':';
787231200Smm	if (name != NULL) {
788231200Smm		strcpy(*p, name);
789231200Smm		*p += strlen(*p);
790231200Smm	} else if (tag == ARCHIVE_ENTRY_ACL_USER
791231200Smm	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
792231200Smm		append_id(p, id);
793231200Smm		id = -1;
794231200Smm	}
795231200Smm	*(*p)++ = ':';
796231200Smm	*(*p)++ = (perm & 0444) ? 'r' : '-';
797231200Smm	*(*p)++ = (perm & 0222) ? 'w' : '-';
798231200Smm	*(*p)++ = (perm & 0111) ? 'x' : '-';
799231200Smm	if (id != -1) {
800231200Smm		*(*p)++ = ':';
801231200Smm		append_id(p, id);
802231200Smm	}
803231200Smm	**p = '\0';
804231200Smm}
805231200Smm
806231200Smm/*
807231200Smm * Parse a textual ACL.  This automatically recognizes and supports
808231200Smm * extensions described above.  The 'type' argument is used to
809231200Smm * indicate the type that should be used for any entries not
810231200Smm * explicitly marked as "default:".
811231200Smm */
812231200Smmint
813231200Smmarchive_acl_parse_w(struct archive_acl *acl,
814231200Smm    const wchar_t *text, int default_type)
815231200Smm{
816231200Smm	struct {
817231200Smm		const wchar_t *start;
818231200Smm		const wchar_t *end;
819231200Smm	} field[4], name;
820231200Smm
821231200Smm	int fields, n;
822231200Smm	int type, tag, permset, id;
823231200Smm	wchar_t sep;
824231200Smm
825231200Smm	while (text != NULL  &&  *text != L'\0') {
826231200Smm		/*
827231200Smm		 * Parse the fields out of the next entry,
828231200Smm		 * advance 'text' to start of next entry.
829231200Smm		 */
830231200Smm		fields = 0;
831231200Smm		do {
832231200Smm			const wchar_t *start, *end;
833231200Smm			next_field_w(&text, &start, &end, &sep);
834231200Smm			if (fields < 4) {
835231200Smm				field[fields].start = start;
836231200Smm				field[fields].end = end;
837231200Smm			}
838231200Smm			++fields;
839231200Smm		} while (sep == L':');
840231200Smm
841231200Smm		/* Set remaining fields to blank. */
842231200Smm		for (n = fields; n < 4; ++n)
843231200Smm			field[n].start = field[n].end = NULL;
844231200Smm
845231200Smm		/* Check for a numeric ID in field 1 or 3. */
846231200Smm		id = -1;
847231200Smm		isint_w(field[1].start, field[1].end, &id);
848231200Smm		/* Field 3 is optional. */
849231200Smm		if (id == -1 && fields > 3)
850231200Smm			isint_w(field[3].start, field[3].end, &id);
851231200Smm
852231200Smm		/*
853231200Smm		 * Solaris extension:  "defaultuser::rwx" is the
854231200Smm		 * default ACL corresponding to "user::rwx", etc.
855231200Smm		 */
856231200Smm		if (field[0].end - field[0].start > 7
857231200Smm		    && wmemcmp(field[0].start, L"default", 7) == 0) {
858231200Smm			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
859231200Smm			field[0].start += 7;
860231200Smm		} else
861231200Smm			type = default_type;
862231200Smm
863231200Smm		name.start = name.end = NULL;
864231200Smm		if (prefix_w(field[0].start, field[0].end, L"user")) {
865231200Smm			if (!ismode_w(field[2].start, field[2].end, &permset))
866231200Smm				return (ARCHIVE_WARN);
867231200Smm			if (id != -1 || field[1].start < field[1].end) {
868231200Smm				tag = ARCHIVE_ENTRY_ACL_USER;
869231200Smm				name = field[1];
870231200Smm			} else
871231200Smm				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
872231200Smm		} else if (prefix_w(field[0].start, field[0].end, L"group")) {
873231200Smm			if (!ismode_w(field[2].start, field[2].end, &permset))
874231200Smm				return (ARCHIVE_WARN);
875231200Smm			if (id != -1 || field[1].start < field[1].end) {
876231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP;
877231200Smm				name = field[1];
878231200Smm			} else
879231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
880231200Smm		} else if (prefix_w(field[0].start, field[0].end, L"other")) {
881231200Smm			if (fields == 2
882231200Smm			    && field[1].start < field[1].end
883231200Smm			    && ismode_w(field[1].start, field[1].end, &permset)) {
884231200Smm				/* This is Solaris-style "other:rwx" */
885231200Smm			} else if (fields == 3
886231200Smm			    && field[1].start == field[1].end
887231200Smm			    && field[2].start < field[2].end
888231200Smm			    && ismode_w(field[2].start, field[2].end, &permset)) {
889231200Smm				/* This is FreeBSD-style "other::rwx" */
890231200Smm			} else
891231200Smm				return (ARCHIVE_WARN);
892231200Smm			tag = ARCHIVE_ENTRY_ACL_OTHER;
893231200Smm		} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
894231200Smm			if (fields == 2
895231200Smm			    && field[1].start < field[1].end
896231200Smm			    && ismode_w(field[1].start, field[1].end, &permset)) {
897231200Smm				/* This is Solaris-style "mask:rwx" */
898231200Smm			} else if (fields == 3
899231200Smm			    && field[1].start == field[1].end
900231200Smm			    && field[2].start < field[2].end
901231200Smm			    && ismode_w(field[2].start, field[2].end, &permset)) {
902231200Smm				/* This is FreeBSD-style "mask::rwx" */
903231200Smm			} else
904231200Smm				return (ARCHIVE_WARN);
905231200Smm			tag = ARCHIVE_ENTRY_ACL_MASK;
906231200Smm		} else
907231200Smm			return (ARCHIVE_WARN);
908231200Smm
909231200Smm		/* Add entry to the internal list. */
910231200Smm		archive_acl_add_entry_w_len(acl, type, permset,
911231200Smm		    tag, id, name.start, name.end - name.start);
912231200Smm	}
913231200Smm	return (ARCHIVE_OK);
914231200Smm}
915231200Smm
916231200Smm/*
917231200Smm * Parse a string to a positive decimal integer.  Returns true if
918231200Smm * the string is non-empty and consists only of decimal digits,
919231200Smm * false otherwise.
920231200Smm */
921231200Smmstatic int
922231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result)
923231200Smm{
924231200Smm	int n = 0;
925231200Smm	if (start >= end)
926231200Smm		return (0);
927231200Smm	while (start < end) {
928231200Smm		if (*start < '0' || *start > '9')
929231200Smm			return (0);
930231200Smm		if (n > (INT_MAX / 10) ||
931231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
932231200Smm			n = INT_MAX;
933231200Smm		} else {
934231200Smm			n *= 10;
935231200Smm			n += *start - '0';
936231200Smm		}
937231200Smm		start++;
938231200Smm	}
939231200Smm	*result = n;
940231200Smm	return (1);
941231200Smm}
942231200Smm
943231200Smm/*
944231200Smm * Parse a string as a mode field.  Returns true if
945231200Smm * the string is non-empty and consists only of mode characters,
946231200Smm * false otherwise.
947231200Smm */
948231200Smmstatic int
949231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset)
950231200Smm{
951231200Smm	const wchar_t *p;
952231200Smm
953231200Smm	if (start >= end)
954231200Smm		return (0);
955231200Smm	p = start;
956231200Smm	*permset = 0;
957231200Smm	while (p < end) {
958231200Smm		switch (*p++) {
959231200Smm		case 'r': case 'R':
960231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
961231200Smm			break;
962231200Smm		case 'w': case 'W':
963231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
964231200Smm			break;
965231200Smm		case 'x': case 'X':
966231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
967231200Smm			break;
968231200Smm		case '-':
969231200Smm			break;
970231200Smm		default:
971231200Smm			return (0);
972231200Smm		}
973231200Smm	}
974231200Smm	return (1);
975231200Smm}
976231200Smm
977231200Smm/*
978231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
979231200Smm * to point to just after the separator.  *start points to the first
980231200Smm * character of the matched text and *end just after the last
981231200Smm * character of the matched identifier.  In particular *end - *start
982231200Smm * is the length of the field body, not including leading or trailing
983231200Smm * whitespace.
984231200Smm */
985231200Smmstatic void
986231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start,
987231200Smm    const wchar_t **end, wchar_t *sep)
988231200Smm{
989231200Smm	/* Skip leading whitespace to find start of field. */
990231200Smm	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
991231200Smm		(*wp)++;
992231200Smm	}
993231200Smm	*start = *wp;
994231200Smm
995231200Smm	/* Scan for the separator. */
996231200Smm	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
997231200Smm	    **wp != L'\n') {
998231200Smm		(*wp)++;
999231200Smm	}
1000231200Smm	*sep = **wp;
1001231200Smm
1002231200Smm	/* Trim trailing whitespace to locate end of field. */
1003231200Smm	*end = *wp - 1;
1004231200Smm	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1005231200Smm		(*end)--;
1006231200Smm	}
1007231200Smm	(*end)++;
1008231200Smm
1009231200Smm	/* Adjust scanner location. */
1010231200Smm	if (**wp != L'\0')
1011231200Smm		(*wp)++;
1012231200Smm}
1013231200Smm
1014231200Smm/*
1015231200Smm * Return true if the characters [start...end) are a prefix of 'test'.
1016231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1017231200Smm */
1018231200Smmstatic int
1019231200Smmprefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1020231200Smm{
1021231200Smm	if (start == end)
1022231200Smm		return (0);
1023231200Smm
1024231200Smm	if (*start++ != *test++)
1025231200Smm		return (0);
1026231200Smm
1027231200Smm	while (start < end  &&  *start++ == *test++)
1028231200Smm		;
1029231200Smm
1030231200Smm	if (start < end)
1031231200Smm		return (0);
1032231200Smm
1033231200Smm	return (1);
1034231200Smm}
1035231200Smm
1036231200Smm/*
1037231200Smm * Parse a textual ACL.  This automatically recognizes and supports
1038231200Smm * extensions described above.  The 'type' argument is used to
1039231200Smm * indicate the type that should be used for any entries not
1040231200Smm * explicitly marked as "default:".
1041231200Smm */
1042231200Smmint
1043231200Smmarchive_acl_parse_l(struct archive_acl *acl,
1044231200Smm    const char *text, int default_type, struct archive_string_conv *sc)
1045231200Smm{
1046231200Smm	struct {
1047231200Smm		const char *start;
1048231200Smm		const char *end;
1049231200Smm	} field[4], name;
1050231200Smm
1051231200Smm	int fields, n, r, ret = ARCHIVE_OK;
1052231200Smm	int type, tag, permset, id;
1053231200Smm	char sep;
1054231200Smm
1055231200Smm	while (text != NULL  &&  *text != '\0') {
1056231200Smm		/*
1057231200Smm		 * Parse the fields out of the next entry,
1058231200Smm		 * advance 'text' to start of next entry.
1059231200Smm		 */
1060231200Smm		fields = 0;
1061231200Smm		do {
1062231200Smm			const char *start, *end;
1063231200Smm			next_field(&text, &start, &end, &sep);
1064231200Smm			if (fields < 4) {
1065231200Smm				field[fields].start = start;
1066231200Smm				field[fields].end = end;
1067231200Smm			}
1068231200Smm			++fields;
1069231200Smm		} while (sep == ':');
1070231200Smm
1071231200Smm		/* Set remaining fields to blank. */
1072231200Smm		for (n = fields; n < 4; ++n)
1073231200Smm			field[n].start = field[n].end = NULL;
1074231200Smm
1075231200Smm		/* Check for a numeric ID in field 1 or 3. */
1076231200Smm		id = -1;
1077231200Smm		isint(field[1].start, field[1].end, &id);
1078231200Smm		/* Field 3 is optional. */
1079231200Smm		if (id == -1 && fields > 3)
1080231200Smm			isint(field[3].start, field[3].end, &id);
1081231200Smm
1082231200Smm		/*
1083231200Smm		 * Solaris extension:  "defaultuser::rwx" is the
1084231200Smm		 * default ACL corresponding to "user::rwx", etc.
1085231200Smm		 */
1086231200Smm		if (field[0].end - field[0].start > 7
1087231200Smm		    && memcmp(field[0].start, "default", 7) == 0) {
1088231200Smm			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1089231200Smm			field[0].start += 7;
1090231200Smm		} else
1091231200Smm			type = default_type;
1092231200Smm
1093231200Smm		name.start = name.end = NULL;
1094232153Smm		if (prefix_c(field[0].start, field[0].end, "user")) {
1095231200Smm			if (!ismode(field[2].start, field[2].end, &permset))
1096231200Smm				return (ARCHIVE_WARN);
1097231200Smm			if (id != -1 || field[1].start < field[1].end) {
1098231200Smm				tag = ARCHIVE_ENTRY_ACL_USER;
1099231200Smm				name = field[1];
1100231200Smm			} else
1101231200Smm				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1102232153Smm		} else if (prefix_c(field[0].start, field[0].end, "group")) {
1103231200Smm			if (!ismode(field[2].start, field[2].end, &permset))
1104231200Smm				return (ARCHIVE_WARN);
1105231200Smm			if (id != -1 || field[1].start < field[1].end) {
1106231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP;
1107231200Smm				name = field[1];
1108231200Smm			} else
1109231200Smm				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1110232153Smm		} else if (prefix_c(field[0].start, field[0].end, "other")) {
1111231200Smm			if (fields == 2
1112231200Smm			    && field[1].start < field[1].end
1113231200Smm			    && ismode(field[1].start, field[1].end, &permset)) {
1114231200Smm				/* This is Solaris-style "other:rwx" */
1115231200Smm			} else if (fields == 3
1116231200Smm			    && field[1].start == field[1].end
1117231200Smm			    && field[2].start < field[2].end
1118231200Smm			    && ismode(field[2].start, field[2].end, &permset)) {
1119231200Smm				/* This is FreeBSD-style "other::rwx" */
1120231200Smm			} else
1121231200Smm				return (ARCHIVE_WARN);
1122231200Smm			tag = ARCHIVE_ENTRY_ACL_OTHER;
1123232153Smm		} else if (prefix_c(field[0].start, field[0].end, "mask")) {
1124231200Smm			if (fields == 2
1125231200Smm			    && field[1].start < field[1].end
1126231200Smm			    && ismode(field[1].start, field[1].end, &permset)) {
1127231200Smm				/* This is Solaris-style "mask:rwx" */
1128231200Smm			} else if (fields == 3
1129231200Smm			    && field[1].start == field[1].end
1130231200Smm			    && field[2].start < field[2].end
1131231200Smm			    && ismode(field[2].start, field[2].end, &permset)) {
1132231200Smm				/* This is FreeBSD-style "mask::rwx" */
1133231200Smm			} else
1134231200Smm				return (ARCHIVE_WARN);
1135231200Smm			tag = ARCHIVE_ENTRY_ACL_MASK;
1136231200Smm		} else
1137231200Smm			return (ARCHIVE_WARN);
1138231200Smm
1139231200Smm		/* Add entry to the internal list. */
1140231200Smm		r = archive_acl_add_entry_len_l(acl, type, permset,
1141231200Smm		    tag, id, name.start, name.end - name.start, sc);
1142231200Smm		if (r < ARCHIVE_WARN)
1143231200Smm			return (r);
1144231200Smm		if (r != ARCHIVE_OK)
1145231200Smm			ret = ARCHIVE_WARN;
1146231200Smm	}
1147231200Smm	return (ret);
1148231200Smm}
1149231200Smm
1150231200Smm/*
1151231200Smm * Parse a string to a positive decimal integer.  Returns true if
1152231200Smm * the string is non-empty and consists only of decimal digits,
1153231200Smm * false otherwise.
1154231200Smm */
1155231200Smmstatic int
1156231200Smmisint(const char *start, const char *end, int *result)
1157231200Smm{
1158231200Smm	int n = 0;
1159231200Smm	if (start >= end)
1160231200Smm		return (0);
1161231200Smm	while (start < end) {
1162231200Smm		if (*start < '0' || *start > '9')
1163231200Smm			return (0);
1164231200Smm		if (n > (INT_MAX / 10) ||
1165231200Smm		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1166231200Smm			n = INT_MAX;
1167231200Smm		} else {
1168231200Smm			n *= 10;
1169231200Smm			n += *start - '0';
1170231200Smm		}
1171231200Smm		start++;
1172231200Smm	}
1173231200Smm	*result = n;
1174231200Smm	return (1);
1175231200Smm}
1176231200Smm
1177231200Smm/*
1178231200Smm * Parse a string as a mode field.  Returns true if
1179231200Smm * the string is non-empty and consists only of mode characters,
1180231200Smm * false otherwise.
1181231200Smm */
1182231200Smmstatic int
1183231200Smmismode(const char *start, const char *end, int *permset)
1184231200Smm{
1185231200Smm	const char *p;
1186231200Smm
1187231200Smm	if (start >= end)
1188231200Smm		return (0);
1189231200Smm	p = start;
1190231200Smm	*permset = 0;
1191231200Smm	while (p < end) {
1192231200Smm		switch (*p++) {
1193231200Smm		case 'r': case 'R':
1194231200Smm			*permset |= ARCHIVE_ENTRY_ACL_READ;
1195231200Smm			break;
1196231200Smm		case 'w': case 'W':
1197231200Smm			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
1198231200Smm			break;
1199231200Smm		case 'x': case 'X':
1200231200Smm			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1201231200Smm			break;
1202231200Smm		case '-':
1203231200Smm			break;
1204231200Smm		default:
1205231200Smm			return (0);
1206231200Smm		}
1207231200Smm	}
1208231200Smm	return (1);
1209231200Smm}
1210231200Smm
1211231200Smm/*
1212231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1213231200Smm * to point to just after the separator.  *start points to the first
1214231200Smm * character of the matched text and *end just after the last
1215231200Smm * character of the matched identifier.  In particular *end - *start
1216231200Smm * is the length of the field body, not including leading or trailing
1217231200Smm * whitespace.
1218231200Smm */
1219231200Smmstatic void
1220231200Smmnext_field(const char **p, const char **start,
1221231200Smm    const char **end, char *sep)
1222231200Smm{
1223231200Smm	/* Skip leading whitespace to find start of field. */
1224231200Smm	while (**p == ' ' || **p == '\t' || **p == '\n') {
1225231200Smm		(*p)++;
1226231200Smm	}
1227231200Smm	*start = *p;
1228231200Smm
1229231200Smm	/* Scan for the separator. */
1230231200Smm	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1231231200Smm		(*p)++;
1232231200Smm	}
1233231200Smm	*sep = **p;
1234231200Smm
1235231200Smm	/* Trim trailing whitespace to locate end of field. */
1236231200Smm	*end = *p - 1;
1237231200Smm	while (**end == ' ' || **end == '\t' || **end == '\n') {
1238231200Smm		(*end)--;
1239231200Smm	}
1240231200Smm	(*end)++;
1241231200Smm
1242231200Smm	/* Adjust scanner location. */
1243231200Smm	if (**p != '\0')
1244231200Smm		(*p)++;
1245231200Smm}
1246231200Smm
1247231200Smm/*
1248231200Smm * Return true if the characters [start...end) are a prefix of 'test'.
1249231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1250231200Smm */
1251231200Smmstatic int
1252232153Smmprefix_c(const char *start, const char *end, const char *test)
1253231200Smm{
1254231200Smm	if (start == end)
1255231200Smm		return (0);
1256231200Smm
1257231200Smm	if (*start++ != *test++)
1258231200Smm		return (0);
1259231200Smm
1260231200Smm	while (start < end  &&  *start++ == *test++)
1261231200Smm		;
1262231200Smm
1263231200Smm	if (start < end)
1264231200Smm		return (0);
1265231200Smm
1266231200Smm	return (1);
1267231200Smm}
1268