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