]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_acl.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_acl.c
1 /*-
2  * Copyright (c) 2003-2010 Tim Kientzle
3  * Copyright (c) 2016 Martin Matuska
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "archive_platform.h"
28 __FBSDID("$FreeBSD$");
29
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_LIMITS_H
34 #include <limits.h>
35 #endif
36 #ifdef HAVE_WCHAR_H
37 #include <wchar.h>
38 #endif
39
40 #include "archive_acl_private.h"
41 #include "archive_entry.h"
42 #include "archive_private.h"
43
44 #undef max
45 #define max(a, b)       ((a)>(b)?(a):(b))
46
47 #ifndef HAVE_WMEMCMP
48 /* Good enough for simple equality testing, but not for sorting. */
49 #define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
50 #endif
51
52 static int      acl_special(struct archive_acl *acl,
53                     int type, int permset, int tag);
54 static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
55                     int type, int permset, int tag, int id);
56 static int      archive_acl_add_entry_len_l(struct archive_acl *acl,
57                     int type, int permset, int tag, int id, const char *name,
58                     size_t len, struct archive_string_conv *sc);
59 static int      archive_acl_text_want_type(struct archive_acl *acl, int flags);
60 static ssize_t  archive_acl_text_len(struct archive_acl *acl, int want_type,
61                     int flags, int wide, struct archive *a,
62                     struct archive_string_conv *sc);
63 static int      isint_w(const wchar_t *start, const wchar_t *end, int *result);
64 static int      ismode_w(const wchar_t *start, const wchar_t *end, int *result);
65 static int      is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
66                     int *result);
67 static int      is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
68                     int *result);
69 static void     next_field_w(const wchar_t **wp, const wchar_t **start,
70                     const wchar_t **end, wchar_t *sep);
71 static void     append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
72                     int tag, int flags, const wchar_t *wname, int perm, int id);
73 static void     append_id_w(wchar_t **wp, int id);
74 static int      isint(const char *start, const char *end, int *result);
75 static int      ismode(const char *start, const char *end, int *result);
76 static int      is_nfs4_flags(const char *start, const char *end,
77                     int *result);
78 static int      is_nfs4_perms(const char *start, const char *end,
79                     int *result);
80 static void     next_field(const char **p, const char **start,
81                     const char **end, char *sep);
82 static void     append_entry(char **p, const char *prefix, int type,
83                     int tag, int flags, const char *name, int perm, int id);
84 static void     append_id(char **p, int id);
85
86 static const struct {
87         const int perm;
88         const char c;
89         const wchar_t wc;
90 } nfsv4_acl_perm_map[] = {
91         { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
92             L'r' },
93         { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
94             L'w' },
95         { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
96         { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
97             'p', L'p' },
98         { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
99         { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
100         { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
101         { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
102         { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
103         { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
104         { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
105         { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
106         { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
107         { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
108 };
109
110 static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
111     sizeof(nfsv4_acl_perm_map[0]));
112
113 static const struct {
114         const int perm;
115         const char c;
116         const wchar_t wc;
117 } nfsv4_acl_flag_map[] = {
118         { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
119         { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
120         { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
121         { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
122         { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
123         { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
124         { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
125 };
126
127 static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
128     sizeof(nfsv4_acl_flag_map[0]));
129
130 void
131 archive_acl_clear(struct archive_acl *acl)
132 {
133         struct archive_acl_entry *ap;
134
135         while (acl->acl_head != NULL) {
136                 ap = acl->acl_head->next;
137                 archive_mstring_clean(&acl->acl_head->name);
138                 free(acl->acl_head);
139                 acl->acl_head = ap;
140         }
141         free(acl->acl_text_w);
142         acl->acl_text_w = NULL;
143         free(acl->acl_text);
144         acl->acl_text = NULL;
145         acl->acl_p = NULL;
146         acl->acl_types = 0;
147         acl->acl_state = 0; /* Not counting. */
148 }
149
150 void
151 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
152 {
153         struct archive_acl_entry *ap, *ap2;
154
155         archive_acl_clear(dest);
156
157         dest->mode = src->mode;
158         ap = src->acl_head;
159         while (ap != NULL) {
160                 ap2 = acl_new_entry(dest,
161                     ap->type, ap->permset, ap->tag, ap->id);
162                 if (ap2 != NULL)
163                         archive_mstring_copy(&ap2->name, &ap->name);
164                 ap = ap->next;
165         }
166 }
167
168 int
169 archive_acl_add_entry(struct archive_acl *acl,
170     int type, int permset, int tag, int id, const char *name)
171 {
172         struct archive_acl_entry *ap;
173
174         if (acl_special(acl, type, permset, tag) == 0)
175                 return ARCHIVE_OK;
176         ap = acl_new_entry(acl, type, permset, tag, id);
177         if (ap == NULL) {
178                 /* XXX Error XXX */
179                 return ARCHIVE_FAILED;
180         }
181         if (name != NULL  &&  *name != '\0')
182                 archive_mstring_copy_mbs(&ap->name, name);
183         else
184                 archive_mstring_clean(&ap->name);
185         return ARCHIVE_OK;
186 }
187
188 int
189 archive_acl_add_entry_w_len(struct archive_acl *acl,
190     int type, int permset, int tag, int id, const wchar_t *name, size_t len)
191 {
192         struct archive_acl_entry *ap;
193
194         if (acl_special(acl, type, permset, tag) == 0)
195                 return ARCHIVE_OK;
196         ap = acl_new_entry(acl, type, permset, tag, id);
197         if (ap == NULL) {
198                 /* XXX Error XXX */
199                 return ARCHIVE_FAILED;
200         }
201         if (name != NULL  &&  *name != L'\0' && len > 0)
202                 archive_mstring_copy_wcs_len(&ap->name, name, len);
203         else
204                 archive_mstring_clean(&ap->name);
205         return ARCHIVE_OK;
206 }
207
208 static int
209 archive_acl_add_entry_len_l(struct archive_acl *acl,
210     int type, int permset, int tag, int id, const char *name, size_t len,
211     struct archive_string_conv *sc)
212 {
213         struct archive_acl_entry *ap;
214         int r;
215
216         if (acl_special(acl, type, permset, tag) == 0)
217                 return ARCHIVE_OK;
218         ap = acl_new_entry(acl, type, permset, tag, id);
219         if (ap == NULL) {
220                 /* XXX Error XXX */
221                 return ARCHIVE_FAILED;
222         }
223         if (name != NULL  &&  *name != '\0' && len > 0) {
224                 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
225         } else {
226                 r = 0;
227                 archive_mstring_clean(&ap->name);
228         }
229         if (r == 0)
230                 return (ARCHIVE_OK);
231         else if (errno == ENOMEM)
232                 return (ARCHIVE_FATAL);
233         else
234                 return (ARCHIVE_WARN);
235 }
236
237 /*
238  * If this ACL entry is part of the standard POSIX permissions set,
239  * store the permissions in the stat structure and return zero.
240  */
241 static int
242 acl_special(struct archive_acl *acl, int type, int permset, int tag)
243 {
244         if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
245             && ((permset & ~007) == 0)) {
246                 switch (tag) {
247                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
248                         acl->mode &= ~0700;
249                         acl->mode |= (permset & 7) << 6;
250                         return (0);
251                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
252                         acl->mode &= ~0070;
253                         acl->mode |= (permset & 7) << 3;
254                         return (0);
255                 case ARCHIVE_ENTRY_ACL_OTHER:
256                         acl->mode &= ~0007;
257                         acl->mode |= permset & 7;
258                         return (0);
259                 }
260         }
261         return (1);
262 }
263
264 /*
265  * Allocate and populate a new ACL entry with everything but the
266  * name.
267  */
268 static struct archive_acl_entry *
269 acl_new_entry(struct archive_acl *acl,
270     int type, int permset, int tag, int id)
271 {
272         struct archive_acl_entry *ap, *aq;
273
274         /* Type argument must be a valid NFS4 or POSIX.1e type.
275          * The type must agree with anything already set and
276          * the permset must be compatible. */
277         if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
278                 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
279                         return (NULL);
280                 }
281                 if (permset &
282                     ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
283                         | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
284                         return (NULL);
285                 }
286         } else  if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
287                 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
288                         return (NULL);
289                 }
290                 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
291                         return (NULL);
292                 }
293         } else {
294                 return (NULL);
295         }
296
297         /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
298         switch (tag) {
299         case ARCHIVE_ENTRY_ACL_USER:
300         case ARCHIVE_ENTRY_ACL_USER_OBJ:
301         case ARCHIVE_ENTRY_ACL_GROUP:
302         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
303                 /* Tags valid in both NFS4 and POSIX.1e */
304                 break;
305         case ARCHIVE_ENTRY_ACL_MASK:
306         case ARCHIVE_ENTRY_ACL_OTHER:
307                 /* Tags valid only in POSIX.1e. */
308                 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
309                         return (NULL);
310                 }
311                 break;
312         case ARCHIVE_ENTRY_ACL_EVERYONE:
313                 /* Tags valid only in NFS4. */
314                 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
315                         return (NULL);
316                 }
317                 break;
318         default:
319                 /* No other values are valid. */
320                 return (NULL);
321         }
322
323         free(acl->acl_text_w);
324         acl->acl_text_w = NULL;
325         free(acl->acl_text);
326         acl->acl_text = NULL;
327
328         /*
329          * If there's a matching entry already in the list, overwrite it.
330          * NFSv4 entries may be repeated and are not overwritten.
331          *
332          * TODO: compare names of no id is provided (needs more rework)
333          */
334         ap = acl->acl_head;
335         aq = NULL;
336         while (ap != NULL) {
337                 if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
338                     ap->type == type && ap->tag == tag && ap->id == id) {
339                         if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
340                             tag != ARCHIVE_ENTRY_ACL_GROUP)) {
341                                 ap->permset = permset;
342                                 return (ap);
343                         }
344                 }
345                 aq = ap;
346                 ap = ap->next;
347         }
348
349         /* Add a new entry to the end of the list. */
350         ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
351         if (ap == NULL)
352                 return (NULL);
353         if (aq == NULL)
354                 acl->acl_head = ap;
355         else
356                 aq->next = ap;
357         ap->type = type;
358         ap->tag = tag;
359         ap->id = id;
360         ap->permset = permset;
361         acl->acl_types |= type;
362         return (ap);
363 }
364
365 /*
366  * Return a count of entries matching "want_type".
367  */
368 int
369 archive_acl_count(struct archive_acl *acl, int want_type)
370 {
371         int count;
372         struct archive_acl_entry *ap;
373
374         count = 0;
375         ap = acl->acl_head;
376         while (ap != NULL) {
377                 if ((ap->type & want_type) != 0)
378                         count++;
379                 ap = ap->next;
380         }
381
382         if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
383                 count += 3;
384         return (count);
385 }
386
387 /*
388  * Return a bitmask of stored ACL types in an ACL list
389  */
390 int
391 archive_acl_types(struct archive_acl *acl)
392 {
393         return (acl->acl_types);
394 }
395
396 /*
397  * Prepare for reading entries from the ACL data.  Returns a count
398  * of entries matching "want_type", or zero if there are no
399  * non-extended ACL entries of that type.
400  */
401 int
402 archive_acl_reset(struct archive_acl *acl, int want_type)
403 {
404         int count, cutoff;
405
406         count = archive_acl_count(acl, want_type);
407
408         /*
409          * If the only entries are the three standard ones,
410          * then don't return any ACL data.  (In this case,
411          * client can just use chmod(2) to set permissions.)
412          */
413         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
414                 cutoff = 3;
415         else
416                 cutoff = 0;
417
418         if (count > cutoff)
419                 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
420         else
421                 acl->acl_state = 0;
422         acl->acl_p = acl->acl_head;
423         return (count);
424 }
425
426
427 /*
428  * Return the next ACL entry in the list.  Fake entries for the
429  * standard permissions and include them in the returned list.
430  */
431 int
432 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
433     int *type, int *permset, int *tag, int *id, const char **name)
434 {
435         *name = NULL;
436         *id = -1;
437
438         /*
439          * The acl_state is either zero (no entries available), -1
440          * (reading from list), or an entry type (retrieve that type
441          * from ae_stat.aest_mode).
442          */
443         if (acl->acl_state == 0)
444                 return (ARCHIVE_WARN);
445
446         /* The first three access entries are special. */
447         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
448                 switch (acl->acl_state) {
449                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
450                         *permset = (acl->mode >> 6) & 7;
451                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
452                         *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
453                         acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
454                         return (ARCHIVE_OK);
455                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
456                         *permset = (acl->mode >> 3) & 7;
457                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
458                         *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
459                         acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
460                         return (ARCHIVE_OK);
461                 case ARCHIVE_ENTRY_ACL_OTHER:
462                         *permset = acl->mode & 7;
463                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
464                         *tag = ARCHIVE_ENTRY_ACL_OTHER;
465                         acl->acl_state = -1;
466                         acl->acl_p = acl->acl_head;
467                         return (ARCHIVE_OK);
468                 default:
469                         break;
470                 }
471         }
472
473         while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
474                 acl->acl_p = acl->acl_p->next;
475         if (acl->acl_p == NULL) {
476                 acl->acl_state = 0;
477                 *type = 0;
478                 *permset = 0;
479                 *tag = 0;
480                 *id = -1;
481                 *name = NULL;
482                 return (ARCHIVE_EOF); /* End of ACL entries. */
483         }
484         *type = acl->acl_p->type;
485         *permset = acl->acl_p->permset;
486         *tag = acl->acl_p->tag;
487         *id = acl->acl_p->id;
488         if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
489                 if (errno == ENOMEM)
490                         return (ARCHIVE_FATAL);
491                 *name = NULL;
492         }
493         acl->acl_p = acl->acl_p->next;
494         return (ARCHIVE_OK);
495 }
496
497 /*
498  * Determine what type of ACL do we want
499  */
500 static int
501 archive_acl_text_want_type(struct archive_acl *acl, int flags)
502 {
503         int want_type;
504
505         /* Check if ACL is NFSv4 */
506         if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
507                 /* NFSv4 should never mix with POSIX.1e */
508                 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
509                         return (0);
510                 else
511                         return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
512         }
513
514         /* Now deal with POSIX.1e ACLs */
515
516         want_type = 0;
517         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
518                 want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
519         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
520                 want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
521
522         /* By default we want both access and default ACLs */
523         if (want_type == 0)
524                 return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
525
526         return (want_type);
527 }
528
529 /*
530  * Calculate ACL text string length
531  */
532 static ssize_t
533 archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
534     int wide, struct archive *a, struct archive_string_conv *sc) {
535         struct archive_acl_entry *ap;
536         const char *name;
537         const wchar_t *wname;
538         int count, idlen, tmp, r;
539         ssize_t length;
540         size_t len;
541
542         count = 0;
543         length = 0;
544         for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
545                 if ((ap->type & want_type) == 0)
546                         continue;
547                 /*
548                  * Filemode-mapping ACL entries are stored exclusively in
549                  * ap->mode so they should not be in the list
550                  */
551                 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
552                     && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
553                     || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
554                     || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
555                         continue;
556                 count++;
557                 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
558                     && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
559                         length += 8; /* "default:" */
560                 switch (ap->tag) {
561                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
562                         if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
563                                 length += 6; /* "owner@" */
564                                 break;
565                         }
566                         /* FALLTHROUGH */
567                 case ARCHIVE_ENTRY_ACL_USER:
568                 case ARCHIVE_ENTRY_ACL_MASK:
569                         length += 4; /* "user", "mask" */
570                         break;
571                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
572                         if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
573                                 length += 6; /* "group@" */
574                                 break;
575                         }
576                         /* FALLTHROUGH */
577                 case ARCHIVE_ENTRY_ACL_GROUP:
578                 case ARCHIVE_ENTRY_ACL_OTHER:
579                         length += 5; /* "group", "other" */
580                         break;
581                 case ARCHIVE_ENTRY_ACL_EVERYONE:
582                         length += 9; /* "everyone@" */
583                         break;
584                 }
585                 length += 1; /* colon after tag */
586                 if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
587                     ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
588                         if (wide) {
589                                 r = archive_mstring_get_wcs(a, &ap->name,
590                                     &wname);
591                                 if (r == 0 && wname != NULL)
592                                         length += wcslen(wname);
593                                 else if (r < 0 && errno == ENOMEM)
594                                         return (0);
595                                 else
596                                         length += sizeof(uid_t) * 3 + 1;
597                         } else {
598                                 r = archive_mstring_get_mbs_l(a, &ap->name, &name,
599                                     &len, sc);
600                                 if (r != 0)
601                                         return (0);
602                                 if (len > 0 && name != NULL)
603                                         length += len;
604                                 else
605                                         length += sizeof(uid_t) * 3 + 1;
606                         }
607                         length += 1; /* colon after user or group name */
608                 } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
609                         length += 1; /* 2nd colon empty user,group or other */
610
611                 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
612                     && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
613                     && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
614                     || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
615                         /* Solaris has no colon after other: and mask: */
616                         length = length - 1;
617                 }
618
619                 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
620                         /* rwxpdDaARWcCos:fdinSFI:deny */
621                         length += 27;
622                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
623                                 length += 1; /* allow, alarm, audit */
624                 } else
625                         length += 3; /* rwx */
626
627                 if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
628                     ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
629                     (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
630                         length += 1; /* colon */
631                         /* ID digit count */
632                         idlen = 1;
633                         tmp = ap->id;
634                         while (tmp > 9) {
635                                 tmp = tmp / 10;
636                                 idlen++;
637                         }
638                         length += idlen;
639                 }
640                 length ++; /* entry separator */
641         }
642
643         /* Add filemode-mapping access entries to the length */
644         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
645                 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
646                         /* "user::rwx\ngroup::rwx\nother:rwx\n" */
647                         length += 31;
648                 } else {
649                         /* "user::rwx\ngroup::rwx\nother::rwx\n" */
650                         length += 32;
651                 }
652         } else if (count == 0)
653                 return (0);
654
655         /* The terminating character is included in count */
656         return (length);
657 }
658
659 /*
660  * Generate a wide text version of the ACL. The flags parameter controls
661  * the type and style of the generated ACL.
662  */
663 wchar_t *
664 archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
665     struct archive *a)
666 {
667         int count;
668         ssize_t length;
669         size_t len;
670         const wchar_t *wname;
671         const wchar_t *prefix;
672         wchar_t separator;
673         struct archive_acl_entry *ap;
674         int id, r, want_type;
675         wchar_t *wp, *ws;
676
677         want_type = archive_acl_text_want_type(acl, flags);
678
679         /* Both NFSv4 and POSIX.1 types found */
680         if (want_type == 0)
681                 return (NULL);
682
683         if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
684                 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
685
686         length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
687
688         if (length == 0)
689                 return (NULL);
690
691         if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
692                 separator = L',';
693         else
694                 separator = L'\n';
695
696         /* Now, allocate the string and actually populate it. */
697         wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
698         if (wp == NULL) {
699                 if (errno == ENOMEM)
700                         __archive_errx(1, "No memory");
701                 return (NULL);
702         }
703         count = 0;
704
705         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
706                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
707                     ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
708                     acl->mode & 0700, -1);
709                 *wp++ = separator;
710                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
711                     ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
712                     acl->mode & 0070, -1);
713                 *wp++ = separator;
714                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
715                     ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
716                     acl->mode & 0007, -1);
717                 count += 3;
718         }
719
720         for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
721                 if ((ap->type & want_type) == 0)
722                         continue;
723                 /*
724                  * Filemode-mapping ACL entries are stored exclusively in
725                  * ap->mode so they should not be in the list
726                  */
727                 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
728                     && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
729                     || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
730                     || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
731                         continue;
732                 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
733                     (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
734                         prefix = L"default:";
735                 else
736                         prefix = NULL;
737                 r = archive_mstring_get_wcs(a, &ap->name, &wname);
738                 if (r == 0) {
739                         if (count > 0)
740                                 *wp++ = separator;
741                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
742                                 id = ap->id;
743                         else
744                                 id = -1;
745                         append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
746                             wname, ap->permset, id);
747                         count++;
748                 } else if (r < 0 && errno == ENOMEM)
749                         return (NULL);
750         }
751
752         /* Add terminating character */
753         *wp++ = L'\0';
754
755         len = wcslen(ws);
756
757         if ((ssize_t)len > (length - 1))
758                 __archive_errx(1, "Buffer overrun");
759
760         if (text_len != NULL)
761                 *text_len = len;
762
763         return (ws);
764 }
765
766 static void
767 append_id_w(wchar_t **wp, int id)
768 {
769         if (id < 0)
770                 id = 0;
771         if (id > 9)
772                 append_id_w(wp, id / 10);
773         *(*wp)++ = L"0123456789"[id % 10];
774 }
775
776 static void
777 append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
778     int tag, int flags, const wchar_t *wname, int perm, int id)
779 {
780         int i;
781
782         if (prefix != NULL) {
783                 wcscpy(*wp, prefix);
784                 *wp += wcslen(*wp);
785         }
786         switch (tag) {
787         case ARCHIVE_ENTRY_ACL_USER_OBJ:
788                 wname = NULL;
789                 id = -1;
790                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
791                         wcscpy(*wp, L"owner@");
792                         break;
793                 }
794                 /* FALLTHROUGH */
795         case ARCHIVE_ENTRY_ACL_USER:
796                 wcscpy(*wp, L"user");
797                 break;
798         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
799                 wname = NULL;
800                 id = -1;
801                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
802                         wcscpy(*wp, L"group@");
803                         break;
804                 }
805                 /* FALLTHROUGH */
806         case ARCHIVE_ENTRY_ACL_GROUP:
807                 wcscpy(*wp, L"group");
808                 break;
809         case ARCHIVE_ENTRY_ACL_MASK:
810                 wcscpy(*wp, L"mask");
811                 wname = NULL;
812                 id = -1;
813                 break;
814         case ARCHIVE_ENTRY_ACL_OTHER:
815                 wcscpy(*wp, L"other");
816                 wname = NULL;
817                 id = -1;
818                 break;
819         case ARCHIVE_ENTRY_ACL_EVERYONE:
820                 wcscpy(*wp, L"everyone@");
821                 wname = NULL;
822                 id = -1;
823                 break;
824         }
825         *wp += wcslen(*wp);
826         *(*wp)++ = L':';
827         if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
828             tag == ARCHIVE_ENTRY_ACL_USER ||
829             tag == ARCHIVE_ENTRY_ACL_GROUP) {
830                 if (wname != NULL) {
831                         wcscpy(*wp, wname);
832                         *wp += wcslen(*wp);
833                 } else if (tag == ARCHIVE_ENTRY_ACL_USER
834                     || tag == ARCHIVE_ENTRY_ACL_GROUP) {
835                         append_id_w(wp, id);
836                         if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
837                                 id = -1;
838                 }
839                 /* Solaris style has no second colon after other and mask */
840                 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
841                     || (tag != ARCHIVE_ENTRY_ACL_OTHER
842                     && tag != ARCHIVE_ENTRY_ACL_MASK))
843                         *(*wp)++ = L':';
844         }
845         if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
846                 /* POSIX.1e ACL perms */
847                 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
848                 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
849                 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
850         } else {
851                 /* NFSv4 ACL perms */
852                 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
853                         if (perm & nfsv4_acl_perm_map[i].perm)
854                                 *(*wp)++ = nfsv4_acl_perm_map[i].wc;
855                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
856                                 *(*wp)++ = L'-';
857                 }
858                 *(*wp)++ = L':';
859                 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
860                         if (perm & nfsv4_acl_flag_map[i].perm)
861                                 *(*wp)++ = nfsv4_acl_flag_map[i].wc;
862                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
863                                 *(*wp)++ = L'-';
864                 }
865                 *(*wp)++ = L':';
866                 switch (type) {
867                 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
868                         wcscpy(*wp, L"allow");
869                         break;
870                 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
871                         wcscpy(*wp, L"deny");
872                         break;
873                 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
874                         wcscpy(*wp, L"audit");
875                         break;
876                 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
877                         wcscpy(*wp, L"alarm");
878                         break;
879                 default:
880                         break;
881                 }
882                 *wp += wcslen(*wp);
883         }
884         if (id != -1) {
885                 *(*wp)++ = L':';
886                 append_id_w(wp, id);
887         }
888 }
889
890 /*
891  * Generate a text version of the ACL. The flags parameter controls
892  * the type and style of the generated ACL.
893  */
894 char *
895 archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
896     struct archive_string_conv *sc)
897 {
898         int count;
899         ssize_t length;
900         size_t len;
901         const char *name;
902         const char *prefix;
903         char separator;
904         struct archive_acl_entry *ap;
905         int id, r, want_type;
906         char *p, *s;
907
908         want_type = archive_acl_text_want_type(acl, flags);
909
910         /* Both NFSv4 and POSIX.1 types found */
911         if (want_type == 0)
912                 return (NULL);
913
914         if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
915                 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
916
917         length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
918
919         if (length == 0)
920                 return (NULL);
921
922         if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
923                 separator = ',';
924         else
925                 separator = '\n';
926
927         /* Now, allocate the string and actually populate it. */
928         p = s = (char *)malloc(length * sizeof(char));
929         if (p == NULL) {
930                 if (errno == ENOMEM)
931                         __archive_errx(1, "No memory");
932                 return (NULL);
933         }
934         count = 0;
935
936         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
937                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
938                     ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
939                     acl->mode & 0700, -1);
940                 *p++ = separator;
941                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
942                     ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
943                     acl->mode & 0070, -1);
944                 *p++ = separator;
945                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
946                     ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
947                     acl->mode & 0007, -1);
948                 count += 3;
949         }
950
951         for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
952                 if ((ap->type & want_type) == 0)
953                         continue;
954                 /*
955                  * Filemode-mapping ACL entries are stored exclusively in
956                  * ap->mode so they should not be in the list
957                  */
958                 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
959                     && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
960                     || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
961                     || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
962                         continue;
963                 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
964                     (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
965                         prefix = "default:";
966                 else
967                         prefix = NULL;
968                 r = archive_mstring_get_mbs_l(
969                     NULL, &ap->name, &name, &len, sc);
970                 if (r != 0)
971                         return (NULL);
972                 if (count > 0)
973                         *p++ = separator;
974                 if (name == NULL ||
975                     (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
976                         id = ap->id;
977                 } else {
978                         id = -1;
979                 }
980                 append_entry(&p, prefix, ap->type, ap->tag, flags, name,
981                     ap->permset, id);
982                 count++;
983         }
984
985         /* Add terminating character */
986         *p++ = '\0';
987
988         len = strlen(s);
989
990         if ((ssize_t)len > (length - 1))
991                 __archive_errx(1, "Buffer overrun");
992
993         if (text_len != NULL)
994                 *text_len = len;
995
996         return (s);
997 }
998
999 static void
1000 append_id(char **p, int id)
1001 {
1002         if (id < 0)
1003                 id = 0;
1004         if (id > 9)
1005                 append_id(p, id / 10);
1006         *(*p)++ = "0123456789"[id % 10];
1007 }
1008
1009 static void
1010 append_entry(char **p, const char *prefix, int type,
1011     int tag, int flags, const char *name, int perm, int id)
1012 {
1013         int i;
1014
1015         if (prefix != NULL) {
1016                 strcpy(*p, prefix);
1017                 *p += strlen(*p);
1018         }
1019         switch (tag) {
1020         case ARCHIVE_ENTRY_ACL_USER_OBJ:
1021                 name = NULL;
1022                 id = -1;
1023                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1024                         strcpy(*p, "owner@");
1025                         break;
1026                 }
1027                 /* FALLTHROUGH */
1028         case ARCHIVE_ENTRY_ACL_USER:
1029                 strcpy(*p, "user");
1030                 break;
1031         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1032                 name = NULL;
1033                 id = -1;
1034                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1035                         strcpy(*p, "group@");
1036                         break;
1037                 }
1038                 /* FALLTHROUGH */
1039         case ARCHIVE_ENTRY_ACL_GROUP:
1040                 strcpy(*p, "group");
1041                 break;
1042         case ARCHIVE_ENTRY_ACL_MASK:
1043                 strcpy(*p, "mask");
1044                 name = NULL;
1045                 id = -1;
1046                 break;
1047         case ARCHIVE_ENTRY_ACL_OTHER:
1048                 strcpy(*p, "other");
1049                 name = NULL;
1050                 id = -1;
1051                 break;
1052         case ARCHIVE_ENTRY_ACL_EVERYONE:
1053                 strcpy(*p, "everyone@");
1054                 name = NULL;
1055                 id = -1;
1056                 break;
1057         }
1058         *p += strlen(*p);
1059         *(*p)++ = ':';
1060         if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1061             tag == ARCHIVE_ENTRY_ACL_USER ||
1062             tag == ARCHIVE_ENTRY_ACL_GROUP) {
1063                 if (name != NULL) {
1064                         strcpy(*p, name);
1065                         *p += strlen(*p);
1066                 } else if (tag == ARCHIVE_ENTRY_ACL_USER
1067                     || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1068                         append_id(p, id);
1069                         if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1070                                 id = -1;
1071                 }
1072                 /* Solaris style has no second colon after other and mask */
1073                 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1074                     || (tag != ARCHIVE_ENTRY_ACL_OTHER
1075                     && tag != ARCHIVE_ENTRY_ACL_MASK))
1076                         *(*p)++ = ':';
1077         }
1078         if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1079                 /* POSIX.1e ACL perms */
1080                 *(*p)++ = (perm & 0444) ? 'r' : '-';
1081                 *(*p)++ = (perm & 0222) ? 'w' : '-';
1082                 *(*p)++ = (perm & 0111) ? 'x' : '-';
1083         } else {
1084                 /* NFSv4 ACL perms */
1085                 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1086                         if (perm & nfsv4_acl_perm_map[i].perm)
1087                                 *(*p)++ = nfsv4_acl_perm_map[i].c;
1088                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1089                                 *(*p)++ = '-';
1090                 }
1091                 *(*p)++ = ':';
1092                 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1093                         if (perm & nfsv4_acl_flag_map[i].perm)
1094                                 *(*p)++ = nfsv4_acl_flag_map[i].c;
1095                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1096                                 *(*p)++ = '-';
1097                 }
1098                 *(*p)++ = ':';
1099                 switch (type) {
1100                 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1101                         strcpy(*p, "allow");
1102                         break;
1103                 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1104                         strcpy(*p, "deny");
1105                         break;
1106                 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1107                         strcpy(*p, "audit");
1108                         break;
1109                 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1110                         strcpy(*p, "alarm");
1111                         break;
1112                 }
1113                 *p += strlen(*p);
1114         }
1115         if (id != -1) {
1116                 *(*p)++ = ':';
1117                 append_id(p, id);
1118         }
1119 }
1120
1121 /*
1122  * Parse a wide ACL text string.
1123  *
1124  * The want_type argument may be one of the following:
1125  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1126  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1127  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1128  *
1129  * POSIX.1e ACL entries prefixed with "default:" are treated as
1130  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1131  */
1132 int
1133 archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1134     int want_type)
1135 {
1136         struct {
1137                 const wchar_t *start;
1138                 const wchar_t *end;
1139         } field[6], name;
1140
1141         const wchar_t *s, *st;
1142
1143         int numfields, fields, n, r, sol, ret;
1144         int type, types, tag, permset, id;
1145         size_t len;
1146         wchar_t sep;
1147
1148         ret = ARCHIVE_OK;
1149         types = 0;
1150
1151         switch (want_type) {
1152         case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1153                 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1154                 __LA_FALLTHROUGH;
1155         case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1156         case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1157                 numfields = 5;
1158                 break;
1159         case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1160                 numfields = 6;
1161                 break;
1162         default:
1163                 return (ARCHIVE_FATAL);
1164         }
1165
1166         while (text != NULL && *text != L'\0') {
1167                 /*
1168                  * Parse the fields out of the next entry,
1169                  * advance 'text' to start of next entry.
1170                  */
1171                 fields = 0;
1172                 do {
1173                         const wchar_t *start, *end;
1174                         next_field_w(&text, &start, &end, &sep);
1175                         if (fields < numfields) {
1176                                 field[fields].start = start;
1177                                 field[fields].end = end;
1178                         }
1179                         ++fields;
1180                 } while (sep == L':');
1181
1182                 /* Set remaining fields to blank. */
1183                 for (n = fields; n < numfields; ++n)
1184                         field[n].start = field[n].end = NULL;
1185
1186                 if (field[0].start != NULL && *(field[0].start) == L'#') {
1187                         /* Comment, skip entry */
1188                         continue;
1189                 }
1190
1191                 n = 0;
1192                 sol = 0;
1193                 id = -1;
1194                 permset = 0;
1195                 name.start = name.end = NULL;
1196
1197                 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1198                         /* POSIX.1e ACLs */
1199                         /*
1200                          * Default keyword "default:user::rwx"
1201                          * if found, we have one more field
1202                          *
1203                          * We also support old Solaris extension:
1204                          * "defaultuser::rwx" is the default ACL corresponding
1205                          * to "user::rwx", etc. valid only for first field
1206                          */
1207                         s = field[0].start;
1208                         len = field[0].end - field[0].start;
1209                         if (*s == L'd' && (len == 1 || (len >= 7
1210                             && wmemcmp((s + 1), L"efault", 6) == 0))) {
1211                                 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1212                                 if (len > 7)
1213                                         field[0].start += 7;
1214                                 else
1215                                         n = 1;
1216                         } else
1217                                 type = want_type;
1218
1219                         /* Check for a numeric ID in field n+1 or n+3. */
1220                         isint_w(field[n + 1].start, field[n + 1].end, &id);
1221                         /* Field n+3 is optional. */
1222                         if (id == -1 && fields > n+3)
1223                                 isint_w(field[n + 3].start, field[n + 3].end,
1224                                     &id);
1225
1226                         tag = 0;
1227                         s = field[n].start;
1228                         st = field[n].start + 1;
1229                         len = field[n].end - field[n].start;
1230
1231                         switch (*s) {
1232                         case L'u':
1233                                 if (len == 1 || (len == 4
1234                                     && wmemcmp(st, L"ser", 3) == 0))
1235                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1236                                 break;
1237                         case L'g':
1238                                 if (len == 1 || (len == 5
1239                                     && wmemcmp(st, L"roup", 4) == 0))
1240                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1241                                 break;
1242                         case L'o':
1243                                 if (len == 1 || (len == 5
1244                                     && wmemcmp(st, L"ther", 4) == 0))
1245                                         tag = ARCHIVE_ENTRY_ACL_OTHER;
1246                                 break;
1247                         case L'm':
1248                                 if (len == 1 || (len == 4
1249                                     && wmemcmp(st, L"ask", 3) == 0))
1250                                         tag = ARCHIVE_ENTRY_ACL_MASK;
1251                                 break;
1252                         default:
1253                                         break;
1254                         }
1255
1256                         switch (tag) {
1257                         case ARCHIVE_ENTRY_ACL_OTHER:
1258                         case ARCHIVE_ENTRY_ACL_MASK:
1259                                 if (fields == (n + 2)
1260                                     && field[n + 1].start < field[n + 1].end
1261                                     && ismode_w(field[n + 1].start,
1262                                     field[n + 1].end, &permset)) {
1263                                         /* This is Solaris-style "other:rwx" */
1264                                         sol = 1;
1265                                 } else if (fields == (n + 3) &&
1266                                     field[n + 1].start < field[n + 1].end) {
1267                                         /* Invalid mask or other field */
1268                                         ret = ARCHIVE_WARN;
1269                                         continue;
1270                                 }
1271                                 break;
1272                         case ARCHIVE_ENTRY_ACL_USER_OBJ:
1273                         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1274                                 if (id != -1 ||
1275                                     field[n + 1].start < field[n + 1].end) {
1276                                         name = field[n + 1];
1277                                         if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1278                                                 tag = ARCHIVE_ENTRY_ACL_USER;
1279                                         else
1280                                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
1281                                 }
1282                                 break;
1283                         default:
1284                                 /* Invalid tag, skip entry */
1285                                 ret = ARCHIVE_WARN;
1286                                 continue;
1287                         }
1288
1289                         /*
1290                          * Without "default:" we expect mode in field 2
1291                          * Exception: Solaris other and mask fields
1292                          */
1293                         if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1294                             field[n + 2 - sol].end, &permset)) {
1295                                 /* Invalid mode, skip entry */
1296                                 ret = ARCHIVE_WARN;
1297                                 continue;
1298                         }
1299                 } else {
1300                         /* NFS4 ACLs */
1301                         s = field[0].start;
1302                         len = field[0].end - field[0].start;
1303                         tag = 0;
1304
1305                         switch (len) {
1306                         case 4:
1307                                 if (wmemcmp(s, L"user", 4) == 0)
1308                                         tag = ARCHIVE_ENTRY_ACL_USER;
1309                                 break;
1310                         case 5:
1311                                 if (wmemcmp(s, L"group", 5) == 0)
1312                                         tag = ARCHIVE_ENTRY_ACL_GROUP;
1313                                 break;
1314                         case 6:
1315                                 if (wmemcmp(s, L"owner@", 6) == 0)
1316                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1317                                 else if (wmemcmp(s, L"group@", len) == 0)
1318                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1319                                 break;
1320                         case 9:
1321                                 if (wmemcmp(s, L"everyone@", 9) == 0)
1322                                         tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1323                         default:
1324                                 break;
1325                         }
1326
1327                         if (tag == 0) {
1328                                 /* Invalid tag, skip entry */
1329                                 ret = ARCHIVE_WARN;
1330                                 continue;
1331                         } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1332                             tag == ARCHIVE_ENTRY_ACL_GROUP) {
1333                                 n = 1;
1334                                 name = field[1];
1335                                 isint_w(name.start, name.end, &id);
1336                         } else
1337                                 n = 0;
1338
1339                         if (!is_nfs4_perms_w(field[1 + n].start,
1340                             field[1 + n].end, &permset)) {
1341                                 /* Invalid NFSv4 perms, skip entry */
1342                                 ret = ARCHIVE_WARN;
1343                                 continue;
1344                         }
1345                         if (!is_nfs4_flags_w(field[2 + n].start,
1346                             field[2 + n].end, &permset)) {
1347                                 /* Invalid NFSv4 flags, skip entry */
1348                                 ret = ARCHIVE_WARN;
1349                                 continue;
1350                         }
1351                         s = field[3 + n].start;
1352                         len = field[3 + n].end - field[3 + n].start;
1353                         type = 0;
1354                         if (len == 4) {
1355                                 if (wmemcmp(s, L"deny", 4) == 0)
1356                                         type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1357                         } else if (len == 5) {
1358                                 if (wmemcmp(s, L"allow", 5) == 0)
1359                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1360                                 else if (wmemcmp(s, L"audit", 5) == 0)
1361                                         type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1362                                 else if (wmemcmp(s, L"alarm", 5) == 0)
1363                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1364                         }
1365                         if (type == 0) {
1366                                 /* Invalid entry type, skip entry */
1367                                 ret = ARCHIVE_WARN;
1368                                 continue;
1369                         }
1370                         isint_w(field[4 + n].start, field[4 + n].end, &id);
1371                 }
1372
1373                 /* Add entry to the internal list. */
1374                 r = archive_acl_add_entry_w_len(acl, type, permset,
1375                     tag, id, name.start, name.end - name.start);
1376                 if (r < ARCHIVE_WARN)
1377                         return (r);
1378                 if (r != ARCHIVE_OK)
1379                         ret = ARCHIVE_WARN;
1380                 types |= type;
1381         }
1382
1383         /* Reset ACL */
1384         archive_acl_reset(acl, types);
1385
1386         return (ret);
1387 }
1388
1389 /*
1390  * Parse a string to a positive decimal integer.  Returns true if
1391  * the string is non-empty and consists only of decimal digits,
1392  * false otherwise.
1393  */
1394 static int
1395 isint_w(const wchar_t *start, const wchar_t *end, int *result)
1396 {
1397         int n = 0;
1398         if (start >= end)
1399                 return (0);
1400         while (start < end) {
1401                 if (*start < L'0' || *start > L'9')
1402                         return (0);
1403                 if (n > (INT_MAX / 10) ||
1404                     (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
1405                         n = INT_MAX;
1406                 } else {
1407                         n *= 10;
1408                         n += *start - L'0';
1409                 }
1410                 start++;
1411         }
1412         *result = n;
1413         return (1);
1414 }
1415
1416 /*
1417  * Parse a string as a mode field.  Returns true if
1418  * the string is non-empty and consists only of mode characters,
1419  * false otherwise.
1420  */
1421 static int
1422 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1423 {
1424         const wchar_t *p;
1425
1426         if (start >= end)
1427                 return (0);
1428         p = start;
1429         *permset = 0;
1430         while (p < end) {
1431                 switch (*p++) {
1432                 case L'r': case L'R':
1433                         *permset |= ARCHIVE_ENTRY_ACL_READ;
1434                         break;
1435                 case L'w': case L'W':
1436                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1437                         break;
1438                 case L'x': case L'X':
1439                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1440                         break;
1441                 case L'-':
1442                         break;
1443                 default:
1444                         return (0);
1445                 }
1446         }
1447         return (1);
1448 }
1449
1450 /*
1451  * Parse a string as a NFS4 ACL permission field.
1452  * Returns true if the string is non-empty and consists only of NFS4 ACL
1453  * permission characters, false otherwise
1454  */
1455 static int
1456 is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1457 {
1458         const wchar_t *p = start;
1459
1460         while (p < end) {
1461                 switch (*p++) {
1462                 case L'r':
1463                         *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1464                         break;
1465                 case L'w':
1466                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1467                         break;
1468                 case L'x':
1469                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1470                         break;
1471                 case L'p':
1472                         *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1473                         break;
1474                 case L'D':
1475                         *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1476                         break;
1477                 case L'd':
1478                         *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1479                         break;
1480                 case L'a':
1481                         *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1482                         break;
1483                 case L'A':
1484                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1485                         break;
1486                 case L'R':
1487                         *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1488                         break;
1489                 case L'W':
1490                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1491                         break;
1492                 case L'c':
1493                         *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1494                         break;
1495                 case L'C':
1496                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1497                         break;
1498                 case L'o':
1499                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1500                         break;
1501                 case L's':
1502                         *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1503                         break;
1504                 case L'-':
1505                         break;
1506                 default:
1507                         return(0);
1508                 }
1509         }
1510         return (1);
1511 }
1512
1513 /*
1514  * Parse a string as a NFS4 ACL flags field.
1515  * Returns true if the string is non-empty and consists only of NFS4 ACL
1516  * flag characters, false otherwise
1517  */
1518 static int
1519 is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1520 {
1521         const wchar_t *p = start;
1522
1523         while (p < end) {
1524                 switch(*p++) {
1525                 case L'f':
1526                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1527                         break;
1528                 case L'd':
1529                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1530                         break;
1531                 case L'i':
1532                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1533                         break;
1534                 case L'n':
1535                         *permset |=
1536                             ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1537                         break;
1538                 case L'S':
1539                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1540                         break;
1541                 case L'F':
1542                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1543                         break;
1544                 case L'I':
1545                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1546                         break;
1547                 case L'-':
1548                         break;
1549                 default:
1550                         return (0);
1551                 }
1552         }
1553         return (1);
1554 }
1555
1556 /*
1557  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1558  * to point to just after the separator.  *start points to the first
1559  * character of the matched text and *end just after the last
1560  * character of the matched identifier.  In particular *end - *start
1561  * is the length of the field body, not including leading or trailing
1562  * whitespace.
1563  */
1564 static void
1565 next_field_w(const wchar_t **wp, const wchar_t **start,
1566     const wchar_t **end, wchar_t *sep)
1567 {
1568         /* Skip leading whitespace to find start of field. */
1569         while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1570                 (*wp)++;
1571         }
1572         *start = *wp;
1573
1574         /* Scan for the separator. */
1575         while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1576             **wp != L'\n') {
1577                 (*wp)++;
1578         }
1579         *sep = **wp;
1580
1581         /* Trim trailing whitespace to locate end of field. */
1582         *end = *wp - 1;
1583         while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1584                 (*end)--;
1585         }
1586         (*end)++;
1587
1588         /* Adjust scanner location. */
1589         if (**wp != L'\0')
1590                 (*wp)++;
1591 }
1592
1593 /*
1594  * Parse an ACL text string.
1595  *
1596  * The want_type argument may be one of the following:
1597  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1598  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1599  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1600  *
1601  * POSIX.1e ACL entries prefixed with "default:" are treated as
1602  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1603  */
1604 int
1605 archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1606     int want_type, struct archive_string_conv *sc)
1607 {
1608         struct {
1609                 const char *start;
1610                 const char *end;
1611         } field[6], name;
1612
1613         const char *s, *st;
1614         int numfields, fields, n, r, sol, ret;
1615         int type, types, tag, permset, id;
1616         size_t len;
1617         char sep;
1618
1619         switch (want_type) {
1620         case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1621                 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1622                 __LA_FALLTHROUGH;
1623         case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1624         case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1625                 numfields = 5;
1626                 break;
1627         case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1628                 numfields = 6;
1629                 break;
1630         default:
1631                 return (ARCHIVE_FATAL);
1632         }
1633
1634         ret = ARCHIVE_OK;
1635         types = 0;
1636
1637         while (text != NULL  &&  *text != '\0') {
1638                 /*
1639                  * Parse the fields out of the next entry,
1640                  * advance 'text' to start of next entry.
1641                  */
1642                 fields = 0;
1643                 do {
1644                         const char *start, *end;
1645                         next_field(&text, &start, &end, &sep);
1646                         if (fields < numfields) {
1647                                 field[fields].start = start;
1648                                 field[fields].end = end;
1649                         }
1650                         ++fields;
1651                 } while (sep == ':');
1652
1653                 /* Set remaining fields to blank. */
1654                 for (n = fields; n < numfields; ++n)
1655                         field[n].start = field[n].end = NULL;
1656
1657                 if (field[0].start != NULL && *(field[0].start) == '#') {
1658                         /* Comment, skip entry */
1659                         continue;
1660                 }
1661
1662                 n = 0;
1663                 sol = 0;
1664                 id = -1;
1665                 permset = 0;
1666                 name.start = name.end = NULL;
1667
1668                 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1669                         /* POSIX.1e ACLs */
1670                         /*
1671                          * Default keyword "default:user::rwx"
1672                          * if found, we have one more field
1673                          *
1674                          * We also support old Solaris extension:
1675                          * "defaultuser::rwx" is the default ACL corresponding
1676                          * to "user::rwx", etc. valid only for first field
1677                          */
1678                         s = field[0].start;
1679                         len = field[0].end - field[0].start;
1680                         if (*s == 'd' && (len == 1 || (len >= 7
1681                             && memcmp((s + 1), "efault", 6) == 0))) {
1682                                 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1683                                 if (len > 7)
1684                                         field[0].start += 7;
1685                                 else
1686                                         n = 1;
1687                         } else
1688                                 type = want_type;
1689
1690                         /* Check for a numeric ID in field n+1 or n+3. */
1691                         isint(field[n + 1].start, field[n + 1].end, &id);
1692                         /* Field n+3 is optional. */
1693                         if (id == -1 && fields > (n + 3))
1694                                 isint(field[n + 3].start, field[n + 3].end,
1695                                     &id);
1696
1697                         tag = 0;
1698                         s = field[n].start;
1699                         st = field[n].start + 1;
1700                         len = field[n].end - field[n].start;
1701
1702                         switch (*s) {
1703                         case 'u':
1704                                 if (len == 1 || (len == 4
1705                                     && memcmp(st, "ser", 3) == 0))
1706                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1707                                 break;
1708                         case 'g':
1709                                 if (len == 1 || (len == 5
1710                                     && memcmp(st, "roup", 4) == 0))
1711                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1712                                 break;
1713                         case 'o':
1714                                 if (len == 1 || (len == 5
1715                                     && memcmp(st, "ther", 4) == 0))
1716                                         tag = ARCHIVE_ENTRY_ACL_OTHER;
1717                                 break;
1718                         case 'm':
1719                                 if (len == 1 || (len == 4
1720                                     && memcmp(st, "ask", 3) == 0))
1721                                         tag = ARCHIVE_ENTRY_ACL_MASK;
1722                                 break;
1723                         default:
1724                                         break;
1725                         }
1726
1727                         switch (tag) {
1728                         case ARCHIVE_ENTRY_ACL_OTHER:
1729                         case ARCHIVE_ENTRY_ACL_MASK:
1730                                 if (fields == (n + 2)
1731                                     && field[n + 1].start < field[n + 1].end
1732                                     && ismode(field[n + 1].start,
1733                                     field[n + 1].end, &permset)) {
1734                                         /* This is Solaris-style "other:rwx" */
1735                                         sol = 1;
1736                                 } else if (fields == (n + 3) &&
1737                                     field[n + 1].start < field[n + 1].end) {
1738                                         /* Invalid mask or other field */
1739                                         ret = ARCHIVE_WARN;
1740                                         continue;
1741                                 }
1742                                 break;
1743                         case ARCHIVE_ENTRY_ACL_USER_OBJ:
1744                         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1745                                 if (id != -1 ||
1746                                     field[n + 1].start < field[n + 1].end) {
1747                                         name = field[n + 1];
1748                                         if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1749                                                 tag = ARCHIVE_ENTRY_ACL_USER;
1750                                         else
1751                                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
1752                                 }
1753                                 break;
1754                         default:
1755                                 /* Invalid tag, skip entry */
1756                                 ret = ARCHIVE_WARN;
1757                                 continue;
1758                         }
1759
1760                         /*
1761                          * Without "default:" we expect mode in field 3
1762                          * Exception: Solaris other and mask fields
1763                          */
1764                         if (permset == 0 && !ismode(field[n + 2 - sol].start,
1765                             field[n + 2 - sol].end, &permset)) {
1766                                 /* Invalid mode, skip entry */
1767                                 ret = ARCHIVE_WARN;
1768                                 continue;
1769                         }
1770                 } else {
1771                         /* NFS4 ACLs */
1772                         s = field[0].start;
1773                         len = field[0].end - field[0].start;
1774                         tag = 0;
1775
1776                         switch (len) {
1777                         case 4:
1778                                 if (memcmp(s, "user", 4) == 0)
1779                                         tag = ARCHIVE_ENTRY_ACL_USER;
1780                                 break;
1781                         case 5:
1782                                 if (memcmp(s, "group", 5) == 0)
1783                                         tag = ARCHIVE_ENTRY_ACL_GROUP;
1784                                 break;
1785                         case 6:
1786                                 if (memcmp(s, "owner@", 6) == 0)
1787                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1788                                 else if (memcmp(s, "group@", 6) == 0)
1789                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1790                                 break;
1791                         case 9:
1792                                 if (memcmp(s, "everyone@", 9) == 0)
1793                                         tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1794                                 break;
1795                         default:
1796                                 break;
1797                         }
1798
1799                         if (tag == 0) {
1800                                 /* Invalid tag, skip entry */
1801                                 ret = ARCHIVE_WARN;
1802                                 continue;
1803                         } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1804                             tag == ARCHIVE_ENTRY_ACL_GROUP) {
1805                                 n = 1;
1806                                 name = field[1];
1807                                 isint(name.start, name.end, &id);
1808                         } else
1809                                 n = 0;
1810
1811                         if (!is_nfs4_perms(field[1 + n].start,
1812                             field[1 + n].end, &permset)) {
1813                                 /* Invalid NFSv4 perms, skip entry */
1814                                 ret = ARCHIVE_WARN;
1815                                 continue;
1816                         }
1817                         if (!is_nfs4_flags(field[2 + n].start,
1818                             field[2 + n].end, &permset)) {
1819                                 /* Invalid NFSv4 flags, skip entry */
1820                                 ret = ARCHIVE_WARN;
1821                                 continue;
1822                         }
1823                         s = field[3 + n].start;
1824                         len = field[3 + n].end - field[3 + n].start;
1825                         type = 0;
1826                         if (len == 4) {
1827                                 if (memcmp(s, "deny", 4) == 0)
1828                                         type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1829                         } else if (len == 5) {
1830                                 if (memcmp(s, "allow", 5) == 0)
1831                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1832                                 else if (memcmp(s, "audit", 5) == 0)
1833                                         type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1834                                 else if (memcmp(s, "alarm", 5) == 0)
1835                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1836                         }
1837                         if (type == 0) {
1838                                 /* Invalid entry type, skip entry */
1839                                 ret = ARCHIVE_WARN;
1840                                 continue;
1841                         }
1842                         isint(field[4 + n].start, field[4 + n].end,
1843                             &id);
1844                 }
1845
1846                 /* Add entry to the internal list. */
1847                 r = archive_acl_add_entry_len_l(acl, type, permset,
1848                     tag, id, name.start, name.end - name.start, sc);
1849                 if (r < ARCHIVE_WARN)
1850                         return (r);
1851                 if (r != ARCHIVE_OK)
1852                         ret = ARCHIVE_WARN;
1853                 types |= type;
1854         }
1855
1856         /* Reset ACL */
1857         archive_acl_reset(acl, types);
1858
1859         return (ret);
1860 }
1861
1862 /*
1863  * Parse a string to a positive decimal integer.  Returns true if
1864  * the string is non-empty and consists only of decimal digits,
1865  * false otherwise.
1866  */
1867 static int
1868 isint(const char *start, const char *end, int *result)
1869 {
1870         int n = 0;
1871         if (start >= end)
1872                 return (0);
1873         while (start < end) {
1874                 if (*start < '0' || *start > '9')
1875                         return (0);
1876                 if (n > (INT_MAX / 10) ||
1877                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1878                         n = INT_MAX;
1879                 } else {
1880                         n *= 10;
1881                         n += *start - '0';
1882                 }
1883                 start++;
1884         }
1885         *result = n;
1886         return (1);
1887 }
1888
1889 /*
1890  * Parse a string as a mode field.  Returns true if
1891  * the string is non-empty and consists only of mode characters,
1892  * false otherwise.
1893  */
1894 static int
1895 ismode(const char *start, const char *end, int *permset)
1896 {
1897         const char *p;
1898
1899         if (start >= end)
1900                 return (0);
1901         p = start;
1902         *permset = 0;
1903         while (p < end) {
1904                 switch (*p++) {
1905                 case 'r': case 'R':
1906                         *permset |= ARCHIVE_ENTRY_ACL_READ;
1907                         break;
1908                 case 'w': case 'W':
1909                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1910                         break;
1911                 case 'x': case 'X':
1912                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1913                         break;
1914                 case '-':
1915                         break;
1916                 default:
1917                         return (0);
1918                 }
1919         }
1920         return (1);
1921 }
1922
1923 /*
1924  * Parse a string as a NFS4 ACL permission field.
1925  * Returns true if the string is non-empty and consists only of NFS4 ACL
1926  * permission characters, false otherwise
1927  */
1928 static int
1929 is_nfs4_perms(const char *start, const char *end, int *permset)
1930 {
1931         const char *p = start;
1932
1933         while (p < end) {
1934                 switch (*p++) {
1935                 case 'r':
1936                         *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1937                         break;
1938                 case 'w':
1939                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1940                         break;
1941                 case 'x':
1942                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1943                         break;
1944                 case 'p':
1945                         *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1946                         break;
1947                 case 'D':
1948                         *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1949                         break;
1950                 case 'd':
1951                         *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1952                         break;
1953                 case 'a':
1954                         *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1955                         break;
1956                 case 'A':
1957                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1958                         break;
1959                 case 'R':
1960                         *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1961                         break;
1962                 case 'W':
1963                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1964                         break;
1965                 case 'c':
1966                         *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1967                         break;
1968                 case 'C':
1969                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1970                         break;
1971                 case 'o':
1972                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1973                         break;
1974                 case 's':
1975                         *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1976                         break;
1977                 case '-':
1978                         break;
1979                 default:
1980                         return(0);
1981                 }
1982         }
1983         return (1);
1984 }
1985
1986 /*
1987  * Parse a string as a NFS4 ACL flags field.
1988  * Returns true if the string is non-empty and consists only of NFS4 ACL
1989  * flag characters, false otherwise
1990  */
1991 static int
1992 is_nfs4_flags(const char *start, const char *end, int *permset)
1993 {
1994         const char *p = start;
1995
1996         while (p < end) {
1997                 switch(*p++) {
1998                 case 'f':
1999                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2000                         break;
2001                 case 'd':
2002                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2003                         break;
2004                 case 'i':
2005                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2006                         break;
2007                 case 'n':
2008                         *permset |=
2009                             ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2010                         break;
2011                 case 'S':
2012                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2013                         break;
2014                 case 'F':
2015                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2016                         break;
2017                 case 'I':
2018                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2019                         break;
2020                 case '-':
2021                         break;
2022                 default:
2023                         return (0);
2024                 }
2025         }
2026         return (1);
2027 }
2028
2029 /*
2030  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2031  * to point to just after the separator.  *start points to the first
2032  * character of the matched text and *end just after the last
2033  * character of the matched identifier.  In particular *end - *start
2034  * is the length of the field body, not including leading or trailing
2035  * whitespace.
2036  */
2037 static void
2038 next_field(const char **p, const char **start,
2039     const char **end, char *sep)
2040 {
2041         /* Skip leading whitespace to find start of field. */
2042         while (**p == ' ' || **p == '\t' || **p == '\n') {
2043                 (*p)++;
2044         }
2045         *start = *p;
2046
2047         /* Scan for the separator. */
2048         while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
2049                 (*p)++;
2050         }
2051         *sep = **p;
2052
2053         /* If the field is only whitespace, bail out now. */
2054         if (**p == '\0') {
2055                 *end = *p;
2056                 return;
2057         }
2058
2059         /* Trim trailing whitespace to locate end of field. */
2060         *end = *p - 1;
2061         while (**end == ' ' || **end == '\t' || **end == '\n') {
2062                 (*end)--;
2063         }
2064         (*end)++;
2065
2066         /* Adjust scanner location. */
2067         if (**p != '\0')
2068                 (*p)++;
2069 }