Merge branch 'vendor/DHCPCD'
[dragonfly.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                         free(ws);
750                         return (NULL);
751                 }
752         }
753
754         /* Add terminating character */
755         *wp++ = L'\0';
756
757         len = wcslen(ws);
758
759         if ((ssize_t)len > (length - 1))
760                 __archive_errx(1, "Buffer overrun");
761
762         if (text_len != NULL)
763                 *text_len = len;
764
765         return (ws);
766 }
767
768 static void
769 append_id_w(wchar_t **wp, int id)
770 {
771         if (id < 0)
772                 id = 0;
773         if (id > 9)
774                 append_id_w(wp, id / 10);
775         *(*wp)++ = L"0123456789"[id % 10];
776 }
777
778 static void
779 append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
780     int tag, int flags, const wchar_t *wname, int perm, int id)
781 {
782         int i;
783
784         if (prefix != NULL) {
785                 wcscpy(*wp, prefix);
786                 *wp += wcslen(*wp);
787         }
788         switch (tag) {
789         case ARCHIVE_ENTRY_ACL_USER_OBJ:
790                 wname = NULL;
791                 id = -1;
792                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
793                         wcscpy(*wp, L"owner@");
794                         break;
795                 }
796                 /* FALLTHROUGH */
797         case ARCHIVE_ENTRY_ACL_USER:
798                 wcscpy(*wp, L"user");
799                 break;
800         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
801                 wname = NULL;
802                 id = -1;
803                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
804                         wcscpy(*wp, L"group@");
805                         break;
806                 }
807                 /* FALLTHROUGH */
808         case ARCHIVE_ENTRY_ACL_GROUP:
809                 wcscpy(*wp, L"group");
810                 break;
811         case ARCHIVE_ENTRY_ACL_MASK:
812                 wcscpy(*wp, L"mask");
813                 wname = NULL;
814                 id = -1;
815                 break;
816         case ARCHIVE_ENTRY_ACL_OTHER:
817                 wcscpy(*wp, L"other");
818                 wname = NULL;
819                 id = -1;
820                 break;
821         case ARCHIVE_ENTRY_ACL_EVERYONE:
822                 wcscpy(*wp, L"everyone@");
823                 wname = NULL;
824                 id = -1;
825                 break;
826         }
827         *wp += wcslen(*wp);
828         *(*wp)++ = L':';
829         if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
830             tag == ARCHIVE_ENTRY_ACL_USER ||
831             tag == ARCHIVE_ENTRY_ACL_GROUP) {
832                 if (wname != NULL) {
833                         wcscpy(*wp, wname);
834                         *wp += wcslen(*wp);
835                 } else if (tag == ARCHIVE_ENTRY_ACL_USER
836                     || tag == ARCHIVE_ENTRY_ACL_GROUP) {
837                         append_id_w(wp, id);
838                         if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
839                                 id = -1;
840                 }
841                 /* Solaris style has no second colon after other and mask */
842                 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
843                     || (tag != ARCHIVE_ENTRY_ACL_OTHER
844                     && tag != ARCHIVE_ENTRY_ACL_MASK))
845                         *(*wp)++ = L':';
846         }
847         if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
848                 /* POSIX.1e ACL perms */
849                 *(*wp)++ = (perm & 0444) ? L'r' : L'-';
850                 *(*wp)++ = (perm & 0222) ? L'w' : L'-';
851                 *(*wp)++ = (perm & 0111) ? L'x' : L'-';
852         } else {
853                 /* NFSv4 ACL perms */
854                 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
855                         if (perm & nfsv4_acl_perm_map[i].perm)
856                                 *(*wp)++ = nfsv4_acl_perm_map[i].wc;
857                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
858                                 *(*wp)++ = L'-';
859                 }
860                 *(*wp)++ = L':';
861                 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
862                         if (perm & nfsv4_acl_flag_map[i].perm)
863                                 *(*wp)++ = nfsv4_acl_flag_map[i].wc;
864                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
865                                 *(*wp)++ = L'-';
866                 }
867                 *(*wp)++ = L':';
868                 switch (type) {
869                 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
870                         wcscpy(*wp, L"allow");
871                         break;
872                 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
873                         wcscpy(*wp, L"deny");
874                         break;
875                 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
876                         wcscpy(*wp, L"audit");
877                         break;
878                 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
879                         wcscpy(*wp, L"alarm");
880                         break;
881                 default:
882                         break;
883                 }
884                 *wp += wcslen(*wp);
885         }
886         if (id != -1) {
887                 *(*wp)++ = L':';
888                 append_id_w(wp, id);
889         }
890 }
891
892 /*
893  * Generate a text version of the ACL. The flags parameter controls
894  * the type and style of the generated ACL.
895  */
896 char *
897 archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
898     struct archive_string_conv *sc)
899 {
900         int count;
901         ssize_t length;
902         size_t len;
903         const char *name;
904         const char *prefix;
905         char separator;
906         struct archive_acl_entry *ap;
907         int id, r, want_type;
908         char *p, *s;
909
910         want_type = archive_acl_text_want_type(acl, flags);
911
912         /* Both NFSv4 and POSIX.1 types found */
913         if (want_type == 0)
914                 return (NULL);
915
916         if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
917                 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
918
919         length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
920
921         if (length == 0)
922                 return (NULL);
923
924         if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
925                 separator = ',';
926         else
927                 separator = '\n';
928
929         /* Now, allocate the string and actually populate it. */
930         p = s = (char *)malloc(length * sizeof(char));
931         if (p == NULL) {
932                 if (errno == ENOMEM)
933                         __archive_errx(1, "No memory");
934                 return (NULL);
935         }
936         count = 0;
937
938         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
939                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
940                     ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
941                     acl->mode & 0700, -1);
942                 *p++ = separator;
943                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
944                     ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
945                     acl->mode & 0070, -1);
946                 *p++ = separator;
947                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
948                     ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
949                     acl->mode & 0007, -1);
950                 count += 3;
951         }
952
953         for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
954                 if ((ap->type & want_type) == 0)
955                         continue;
956                 /*
957                  * Filemode-mapping ACL entries are stored exclusively in
958                  * ap->mode so they should not be in the list
959                  */
960                 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
961                     && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
962                     || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
963                     || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
964                         continue;
965                 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
966                     (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
967                         prefix = "default:";
968                 else
969                         prefix = NULL;
970                 r = archive_mstring_get_mbs_l(
971                     NULL, &ap->name, &name, &len, sc);
972                 if (r != 0) {
973                         free(s);
974                         return (NULL);
975                 }
976                 if (count > 0)
977                         *p++ = separator;
978                 if (name == NULL ||
979                     (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
980                         id = ap->id;
981                 } else {
982                         id = -1;
983                 }
984                 append_entry(&p, prefix, ap->type, ap->tag, flags, name,
985                     ap->permset, id);
986                 count++;
987         }
988
989         /* Add terminating character */
990         *p++ = '\0';
991
992         len = strlen(s);
993
994         if ((ssize_t)len > (length - 1))
995                 __archive_errx(1, "Buffer overrun");
996
997         if (text_len != NULL)
998                 *text_len = len;
999
1000         return (s);
1001 }
1002
1003 static void
1004 append_id(char **p, int id)
1005 {
1006         if (id < 0)
1007                 id = 0;
1008         if (id > 9)
1009                 append_id(p, id / 10);
1010         *(*p)++ = "0123456789"[id % 10];
1011 }
1012
1013 static void
1014 append_entry(char **p, const char *prefix, int type,
1015     int tag, int flags, const char *name, int perm, int id)
1016 {
1017         int i;
1018
1019         if (prefix != NULL) {
1020                 strcpy(*p, prefix);
1021                 *p += strlen(*p);
1022         }
1023         switch (tag) {
1024         case ARCHIVE_ENTRY_ACL_USER_OBJ:
1025                 name = NULL;
1026                 id = -1;
1027                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1028                         strcpy(*p, "owner@");
1029                         break;
1030                 }
1031                 /* FALLTHROUGH */
1032         case ARCHIVE_ENTRY_ACL_USER:
1033                 strcpy(*p, "user");
1034                 break;
1035         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1036                 name = NULL;
1037                 id = -1;
1038                 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
1039                         strcpy(*p, "group@");
1040                         break;
1041                 }
1042                 /* FALLTHROUGH */
1043         case ARCHIVE_ENTRY_ACL_GROUP:
1044                 strcpy(*p, "group");
1045                 break;
1046         case ARCHIVE_ENTRY_ACL_MASK:
1047                 strcpy(*p, "mask");
1048                 name = NULL;
1049                 id = -1;
1050                 break;
1051         case ARCHIVE_ENTRY_ACL_OTHER:
1052                 strcpy(*p, "other");
1053                 name = NULL;
1054                 id = -1;
1055                 break;
1056         case ARCHIVE_ENTRY_ACL_EVERYONE:
1057                 strcpy(*p, "everyone@");
1058                 name = NULL;
1059                 id = -1;
1060                 break;
1061         }
1062         *p += strlen(*p);
1063         *(*p)++ = ':';
1064         if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
1065             tag == ARCHIVE_ENTRY_ACL_USER ||
1066             tag == ARCHIVE_ENTRY_ACL_GROUP) {
1067                 if (name != NULL) {
1068                         strcpy(*p, name);
1069                         *p += strlen(*p);
1070                 } else if (tag == ARCHIVE_ENTRY_ACL_USER
1071                     || tag == ARCHIVE_ENTRY_ACL_GROUP) {
1072                         append_id(p, id);
1073                         if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
1074                                 id = -1;
1075                 }
1076                 /* Solaris style has no second colon after other and mask */
1077                 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
1078                     || (tag != ARCHIVE_ENTRY_ACL_OTHER
1079                     && tag != ARCHIVE_ENTRY_ACL_MASK))
1080                         *(*p)++ = ':';
1081         }
1082         if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
1083                 /* POSIX.1e ACL perms */
1084                 *(*p)++ = (perm & 0444) ? 'r' : '-';
1085                 *(*p)++ = (perm & 0222) ? 'w' : '-';
1086                 *(*p)++ = (perm & 0111) ? 'x' : '-';
1087         } else {
1088                 /* NFSv4 ACL perms */
1089                 for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
1090                         if (perm & nfsv4_acl_perm_map[i].perm)
1091                                 *(*p)++ = nfsv4_acl_perm_map[i].c;
1092                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1093                                 *(*p)++ = '-';
1094                 }
1095                 *(*p)++ = ':';
1096                 for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
1097                         if (perm & nfsv4_acl_flag_map[i].perm)
1098                                 *(*p)++ = nfsv4_acl_flag_map[i].c;
1099                         else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
1100                                 *(*p)++ = '-';
1101                 }
1102                 *(*p)++ = ':';
1103                 switch (type) {
1104                 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
1105                         strcpy(*p, "allow");
1106                         break;
1107                 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
1108                         strcpy(*p, "deny");
1109                         break;
1110                 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
1111                         strcpy(*p, "audit");
1112                         break;
1113                 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
1114                         strcpy(*p, "alarm");
1115                         break;
1116                 }
1117                 *p += strlen(*p);
1118         }
1119         if (id != -1) {
1120                 *(*p)++ = ':';
1121                 append_id(p, id);
1122         }
1123 }
1124
1125 /*
1126  * Parse a wide ACL text string.
1127  *
1128  * The want_type argument may be one of the following:
1129  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1130  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1131  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1132  *
1133  * POSIX.1e ACL entries prefixed with "default:" are treated as
1134  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1135  */
1136 int
1137 archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1138     int want_type)
1139 {
1140         struct {
1141                 const wchar_t *start;
1142                 const wchar_t *end;
1143         } field[6], name;
1144
1145         const wchar_t *s, *st;
1146
1147         int numfields, fields, n, r, sol, ret;
1148         int type, types, tag, permset, id;
1149         size_t len;
1150         wchar_t sep;
1151
1152         ret = ARCHIVE_OK;
1153         types = 0;
1154
1155         switch (want_type) {
1156         case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1157                 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1158                 __LA_FALLTHROUGH;
1159         case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1160         case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1161                 numfields = 5;
1162                 break;
1163         case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1164                 numfields = 6;
1165                 break;
1166         default:
1167                 return (ARCHIVE_FATAL);
1168         }
1169
1170         while (text != NULL && *text != L'\0') {
1171                 /*
1172                  * Parse the fields out of the next entry,
1173                  * advance 'text' to start of next entry.
1174                  */
1175                 fields = 0;
1176                 do {
1177                         const wchar_t *start, *end;
1178                         next_field_w(&text, &start, &end, &sep);
1179                         if (fields < numfields) {
1180                                 field[fields].start = start;
1181                                 field[fields].end = end;
1182                         }
1183                         ++fields;
1184                 } while (sep == L':');
1185
1186                 /* Set remaining fields to blank. */
1187                 for (n = fields; n < numfields; ++n)
1188                         field[n].start = field[n].end = NULL;
1189
1190                 if (field[0].start != NULL && *(field[0].start) == L'#') {
1191                         /* Comment, skip entry */
1192                         continue;
1193                 }
1194
1195                 n = 0;
1196                 sol = 0;
1197                 id = -1;
1198                 permset = 0;
1199                 name.start = name.end = NULL;
1200
1201                 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1202                         /* POSIX.1e ACLs */
1203                         /*
1204                          * Default keyword "default:user::rwx"
1205                          * if found, we have one more field
1206                          *
1207                          * We also support old Solaris extension:
1208                          * "defaultuser::rwx" is the default ACL corresponding
1209                          * to "user::rwx", etc. valid only for first field
1210                          */
1211                         s = field[0].start;
1212                         len = field[0].end - field[0].start;
1213                         if (*s == L'd' && (len == 1 || (len >= 7
1214                             && wmemcmp((s + 1), L"efault", 6) == 0))) {
1215                                 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1216                                 if (len > 7)
1217                                         field[0].start += 7;
1218                                 else
1219                                         n = 1;
1220                         } else
1221                                 type = want_type;
1222
1223                         /* Check for a numeric ID in field n+1 or n+3. */
1224                         isint_w(field[n + 1].start, field[n + 1].end, &id);
1225                         /* Field n+3 is optional. */
1226                         if (id == -1 && fields > n+3)
1227                                 isint_w(field[n + 3].start, field[n + 3].end,
1228                                     &id);
1229
1230                         tag = 0;
1231                         s = field[n].start;
1232                         st = field[n].start + 1;
1233                         len = field[n].end - field[n].start;
1234
1235                         switch (*s) {
1236                         case L'u':
1237                                 if (len == 1 || (len == 4
1238                                     && wmemcmp(st, L"ser", 3) == 0))
1239                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1240                                 break;
1241                         case L'g':
1242                                 if (len == 1 || (len == 5
1243                                     && wmemcmp(st, L"roup", 4) == 0))
1244                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1245                                 break;
1246                         case L'o':
1247                                 if (len == 1 || (len == 5
1248                                     && wmemcmp(st, L"ther", 4) == 0))
1249                                         tag = ARCHIVE_ENTRY_ACL_OTHER;
1250                                 break;
1251                         case L'm':
1252                                 if (len == 1 || (len == 4
1253                                     && wmemcmp(st, L"ask", 3) == 0))
1254                                         tag = ARCHIVE_ENTRY_ACL_MASK;
1255                                 break;
1256                         default:
1257                                         break;
1258                         }
1259
1260                         switch (tag) {
1261                         case ARCHIVE_ENTRY_ACL_OTHER:
1262                         case ARCHIVE_ENTRY_ACL_MASK:
1263                                 if (fields == (n + 2)
1264                                     && field[n + 1].start < field[n + 1].end
1265                                     && ismode_w(field[n + 1].start,
1266                                     field[n + 1].end, &permset)) {
1267                                         /* This is Solaris-style "other:rwx" */
1268                                         sol = 1;
1269                                 } else if (fields == (n + 3) &&
1270                                     field[n + 1].start < field[n + 1].end) {
1271                                         /* Invalid mask or other field */
1272                                         ret = ARCHIVE_WARN;
1273                                         continue;
1274                                 }
1275                                 break;
1276                         case ARCHIVE_ENTRY_ACL_USER_OBJ:
1277                         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1278                                 if (id != -1 ||
1279                                     field[n + 1].start < field[n + 1].end) {
1280                                         name = field[n + 1];
1281                                         if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1282                                                 tag = ARCHIVE_ENTRY_ACL_USER;
1283                                         else
1284                                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
1285                                 }
1286                                 break;
1287                         default:
1288                                 /* Invalid tag, skip entry */
1289                                 ret = ARCHIVE_WARN;
1290                                 continue;
1291                         }
1292
1293                         /*
1294                          * Without "default:" we expect mode in field 2
1295                          * Exception: Solaris other and mask fields
1296                          */
1297                         if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1298                             field[n + 2 - sol].end, &permset)) {
1299                                 /* Invalid mode, skip entry */
1300                                 ret = ARCHIVE_WARN;
1301                                 continue;
1302                         }
1303                 } else {
1304                         /* NFS4 ACLs */
1305                         s = field[0].start;
1306                         len = field[0].end - field[0].start;
1307                         tag = 0;
1308
1309                         switch (len) {
1310                         case 4:
1311                                 if (wmemcmp(s, L"user", 4) == 0)
1312                                         tag = ARCHIVE_ENTRY_ACL_USER;
1313                                 break;
1314                         case 5:
1315                                 if (wmemcmp(s, L"group", 5) == 0)
1316                                         tag = ARCHIVE_ENTRY_ACL_GROUP;
1317                                 break;
1318                         case 6:
1319                                 if (wmemcmp(s, L"owner@", 6) == 0)
1320                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1321                                 else if (wmemcmp(s, L"group@", len) == 0)
1322                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1323                                 break;
1324                         case 9:
1325                                 if (wmemcmp(s, L"everyone@", 9) == 0)
1326                                         tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1327                         default:
1328                                 break;
1329                         }
1330
1331                         if (tag == 0) {
1332                                 /* Invalid tag, skip entry */
1333                                 ret = ARCHIVE_WARN;
1334                                 continue;
1335                         } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1336                             tag == ARCHIVE_ENTRY_ACL_GROUP) {
1337                                 n = 1;
1338                                 name = field[1];
1339                                 isint_w(name.start, name.end, &id);
1340                         } else
1341                                 n = 0;
1342
1343                         if (!is_nfs4_perms_w(field[1 + n].start,
1344                             field[1 + n].end, &permset)) {
1345                                 /* Invalid NFSv4 perms, skip entry */
1346                                 ret = ARCHIVE_WARN;
1347                                 continue;
1348                         }
1349                         if (!is_nfs4_flags_w(field[2 + n].start,
1350                             field[2 + n].end, &permset)) {
1351                                 /* Invalid NFSv4 flags, skip entry */
1352                                 ret = ARCHIVE_WARN;
1353                                 continue;
1354                         }
1355                         s = field[3 + n].start;
1356                         len = field[3 + n].end - field[3 + n].start;
1357                         type = 0;
1358                         if (len == 4) {
1359                                 if (wmemcmp(s, L"deny", 4) == 0)
1360                                         type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1361                         } else if (len == 5) {
1362                                 if (wmemcmp(s, L"allow", 5) == 0)
1363                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1364                                 else if (wmemcmp(s, L"audit", 5) == 0)
1365                                         type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1366                                 else if (wmemcmp(s, L"alarm", 5) == 0)
1367                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1368                         }
1369                         if (type == 0) {
1370                                 /* Invalid entry type, skip entry */
1371                                 ret = ARCHIVE_WARN;
1372                                 continue;
1373                         }
1374                         isint_w(field[4 + n].start, field[4 + n].end, &id);
1375                 }
1376
1377                 /* Add entry to the internal list. */
1378                 r = archive_acl_add_entry_w_len(acl, type, permset,
1379                     tag, id, name.start, name.end - name.start);
1380                 if (r < ARCHIVE_WARN)
1381                         return (r);
1382                 if (r != ARCHIVE_OK)
1383                         ret = ARCHIVE_WARN;
1384                 types |= type;
1385         }
1386
1387         /* Reset ACL */
1388         archive_acl_reset(acl, types);
1389
1390         return (ret);
1391 }
1392
1393 /*
1394  * Parse a string to a positive decimal integer.  Returns true if
1395  * the string is non-empty and consists only of decimal digits,
1396  * false otherwise.
1397  */
1398 static int
1399 isint_w(const wchar_t *start, const wchar_t *end, int *result)
1400 {
1401         int n = 0;
1402         if (start >= end)
1403                 return (0);
1404         while (start < end) {
1405                 if (*start < L'0' || *start > L'9')
1406                         return (0);
1407                 if (n > (INT_MAX / 10) ||
1408                     (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
1409                         n = INT_MAX;
1410                 } else {
1411                         n *= 10;
1412                         n += *start - L'0';
1413                 }
1414                 start++;
1415         }
1416         *result = n;
1417         return (1);
1418 }
1419
1420 /*
1421  * Parse a string as a mode field.  Returns true if
1422  * the string is non-empty and consists only of mode characters,
1423  * false otherwise.
1424  */
1425 static int
1426 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1427 {
1428         const wchar_t *p;
1429
1430         if (start >= end)
1431                 return (0);
1432         p = start;
1433         *permset = 0;
1434         while (p < end) {
1435                 switch (*p++) {
1436                 case L'r': case L'R':
1437                         *permset |= ARCHIVE_ENTRY_ACL_READ;
1438                         break;
1439                 case L'w': case L'W':
1440                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1441                         break;
1442                 case L'x': case L'X':
1443                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1444                         break;
1445                 case L'-':
1446                         break;
1447                 default:
1448                         return (0);
1449                 }
1450         }
1451         return (1);
1452 }
1453
1454 /*
1455  * Parse a string as a NFS4 ACL permission field.
1456  * Returns true if the string is non-empty and consists only of NFS4 ACL
1457  * permission characters, false otherwise
1458  */
1459 static int
1460 is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1461 {
1462         const wchar_t *p = start;
1463
1464         while (p < end) {
1465                 switch (*p++) {
1466                 case L'r':
1467                         *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1468                         break;
1469                 case L'w':
1470                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1471                         break;
1472                 case L'x':
1473                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1474                         break;
1475                 case L'p':
1476                         *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1477                         break;
1478                 case L'D':
1479                         *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1480                         break;
1481                 case L'd':
1482                         *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1483                         break;
1484                 case L'a':
1485                         *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1486                         break;
1487                 case L'A':
1488                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1489                         break;
1490                 case L'R':
1491                         *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1492                         break;
1493                 case L'W':
1494                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1495                         break;
1496                 case L'c':
1497                         *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1498                         break;
1499                 case L'C':
1500                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1501                         break;
1502                 case L'o':
1503                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1504                         break;
1505                 case L's':
1506                         *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1507                         break;
1508                 case L'-':
1509                         break;
1510                 default:
1511                         return(0);
1512                 }
1513         }
1514         return (1);
1515 }
1516
1517 /*
1518  * Parse a string as a NFS4 ACL flags field.
1519  * Returns true if the string is non-empty and consists only of NFS4 ACL
1520  * flag characters, false otherwise
1521  */
1522 static int
1523 is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1524 {
1525         const wchar_t *p = start;
1526
1527         while (p < end) {
1528                 switch(*p++) {
1529                 case L'f':
1530                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1531                         break;
1532                 case L'd':
1533                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1534                         break;
1535                 case L'i':
1536                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1537                         break;
1538                 case L'n':
1539                         *permset |=
1540                             ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1541                         break;
1542                 case L'S':
1543                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1544                         break;
1545                 case L'F':
1546                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1547                         break;
1548                 case L'I':
1549                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1550                         break;
1551                 case L'-':
1552                         break;
1553                 default:
1554                         return (0);
1555                 }
1556         }
1557         return (1);
1558 }
1559
1560 /*
1561  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1562  * to point to just after the separator.  *start points to the first
1563  * character of the matched text and *end just after the last
1564  * character of the matched identifier.  In particular *end - *start
1565  * is the length of the field body, not including leading or trailing
1566  * whitespace.
1567  */
1568 static void
1569 next_field_w(const wchar_t **wp, const wchar_t **start,
1570     const wchar_t **end, wchar_t *sep)
1571 {
1572         /* Skip leading whitespace to find start of field. */
1573         while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1574                 (*wp)++;
1575         }
1576         *start = *wp;
1577
1578         /* Scan for the separator. */
1579         while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1580             **wp != L'\n' && **wp != L'#') {
1581                 (*wp)++;
1582         }
1583         *sep = **wp;
1584
1585         /* Locate end of field, trim trailing whitespace if necessary */
1586         if (*wp == *start) {
1587                 *end = *wp;
1588         } else {
1589                 *end = *wp - 1;
1590                 while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1591                         (*end)--;
1592                 }
1593                 (*end)++;
1594         }
1595
1596         /* Handle in-field comments */
1597         if (*sep == L'#') {
1598                 while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
1599                         (*wp)++;
1600                 }
1601                 *sep = **wp;
1602         }
1603
1604         /* Adjust scanner location. */
1605         if (**wp != L'\0')
1606                 (*wp)++;
1607 }
1608
1609 /*
1610  * Parse an ACL text string.
1611  *
1612  * The want_type argument may be one of the following:
1613  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1614  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1615  * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1616  *
1617  * POSIX.1e ACL entries prefixed with "default:" are treated as
1618  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1619  */
1620 int
1621 archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1622     int want_type, struct archive_string_conv *sc)
1623 {
1624         struct {
1625                 const char *start;
1626                 const char *end;
1627         } field[6], name;
1628
1629         const char *s, *st;
1630         int numfields, fields, n, r, sol, ret;
1631         int type, types, tag, permset, id;
1632         size_t len;
1633         char sep;
1634
1635         switch (want_type) {
1636         case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1637                 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1638                 __LA_FALLTHROUGH;
1639         case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1640         case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1641                 numfields = 5;
1642                 break;
1643         case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1644                 numfields = 6;
1645                 break;
1646         default:
1647                 return (ARCHIVE_FATAL);
1648         }
1649
1650         ret = ARCHIVE_OK;
1651         types = 0;
1652
1653         while (text != NULL &&  *text != '\0') {
1654                 /*
1655                  * Parse the fields out of the next entry,
1656                  * advance 'text' to start of next entry.
1657                  */
1658                 fields = 0;
1659                 do {
1660                         const char *start, *end;
1661                         next_field(&text, &start, &end, &sep);
1662                         if (fields < numfields) {
1663                                 field[fields].start = start;
1664                                 field[fields].end = end;
1665                         }
1666                         ++fields;
1667                 } while (sep == ':');
1668
1669                 /* Set remaining fields to blank. */
1670                 for (n = fields; n < numfields; ++n)
1671                         field[n].start = field[n].end = NULL;
1672
1673                 if (field[0].start != NULL && *(field[0].start) == '#') {
1674                         /* Comment, skip entry */
1675                         continue;
1676                 }
1677
1678                 n = 0;
1679                 sol = 0;
1680                 id = -1;
1681                 permset = 0;
1682                 name.start = name.end = NULL;
1683
1684                 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1685                         /* POSIX.1e ACLs */
1686                         /*
1687                          * Default keyword "default:user::rwx"
1688                          * if found, we have one more field
1689                          *
1690                          * We also support old Solaris extension:
1691                          * "defaultuser::rwx" is the default ACL corresponding
1692                          * to "user::rwx", etc. valid only for first field
1693                          */
1694                         s = field[0].start;
1695                         len = field[0].end - field[0].start;
1696                         if (*s == 'd' && (len == 1 || (len >= 7
1697                             && memcmp((s + 1), "efault", 6) == 0))) {
1698                                 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1699                                 if (len > 7)
1700                                         field[0].start += 7;
1701                                 else
1702                                         n = 1;
1703                         } else
1704                                 type = want_type;
1705
1706                         /* Check for a numeric ID in field n+1 or n+3. */
1707                         isint(field[n + 1].start, field[n + 1].end, &id);
1708                         /* Field n+3 is optional. */
1709                         if (id == -1 && fields > (n + 3))
1710                                 isint(field[n + 3].start, field[n + 3].end,
1711                                     &id);
1712
1713                         tag = 0;
1714                         s = field[n].start;
1715                         st = field[n].start + 1;
1716                         len = field[n].end - field[n].start;
1717
1718                         if (len == 0) {
1719                                 ret = ARCHIVE_WARN;
1720                                 continue;
1721                         }
1722
1723                         switch (*s) {
1724                         case 'u':
1725                                 if (len == 1 || (len == 4
1726                                     && memcmp(st, "ser", 3) == 0))
1727                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1728                                 break;
1729                         case 'g':
1730                                 if (len == 1 || (len == 5
1731                                     && memcmp(st, "roup", 4) == 0))
1732                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1733                                 break;
1734                         case 'o':
1735                                 if (len == 1 || (len == 5
1736                                     && memcmp(st, "ther", 4) == 0))
1737                                         tag = ARCHIVE_ENTRY_ACL_OTHER;
1738                                 break;
1739                         case 'm':
1740                                 if (len == 1 || (len == 4
1741                                     && memcmp(st, "ask", 3) == 0))
1742                                         tag = ARCHIVE_ENTRY_ACL_MASK;
1743                                 break;
1744                         default:
1745                                         break;
1746                         }
1747
1748                         switch (tag) {
1749                         case ARCHIVE_ENTRY_ACL_OTHER:
1750                         case ARCHIVE_ENTRY_ACL_MASK:
1751                                 if (fields == (n + 2)
1752                                     && field[n + 1].start < field[n + 1].end
1753                                     && ismode(field[n + 1].start,
1754                                     field[n + 1].end, &permset)) {
1755                                         /* This is Solaris-style "other:rwx" */
1756                                         sol = 1;
1757                                 } else if (fields == (n + 3) &&
1758                                     field[n + 1].start < field[n + 1].end) {
1759                                         /* Invalid mask or other field */
1760                                         ret = ARCHIVE_WARN;
1761                                         continue;
1762                                 }
1763                                 break;
1764                         case ARCHIVE_ENTRY_ACL_USER_OBJ:
1765                         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1766                                 if (id != -1 ||
1767                                     field[n + 1].start < field[n + 1].end) {
1768                                         name = field[n + 1];
1769                                         if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1770                                                 tag = ARCHIVE_ENTRY_ACL_USER;
1771                                         else
1772                                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
1773                                 }
1774                                 break;
1775                         default:
1776                                 /* Invalid tag, skip entry */
1777                                 ret = ARCHIVE_WARN;
1778                                 continue;
1779                         }
1780
1781                         /*
1782                          * Without "default:" we expect mode in field 3
1783                          * Exception: Solaris other and mask fields
1784                          */
1785                         if (permset == 0 && !ismode(field[n + 2 - sol].start,
1786                             field[n + 2 - sol].end, &permset)) {
1787                                 /* Invalid mode, skip entry */
1788                                 ret = ARCHIVE_WARN;
1789                                 continue;
1790                         }
1791                 } else {
1792                         /* NFS4 ACLs */
1793                         s = field[0].start;
1794                         len = field[0].end - field[0].start;
1795                         tag = 0;
1796
1797                         switch (len) {
1798                         case 4:
1799                                 if (memcmp(s, "user", 4) == 0)
1800                                         tag = ARCHIVE_ENTRY_ACL_USER;
1801                                 break;
1802                         case 5:
1803                                 if (memcmp(s, "group", 5) == 0)
1804                                         tag = ARCHIVE_ENTRY_ACL_GROUP;
1805                                 break;
1806                         case 6:
1807                                 if (memcmp(s, "owner@", 6) == 0)
1808                                         tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1809                                 else if (memcmp(s, "group@", 6) == 0)
1810                                         tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1811                                 break;
1812                         case 9:
1813                                 if (memcmp(s, "everyone@", 9) == 0)
1814                                         tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1815                                 break;
1816                         default:
1817                                 break;
1818                         }
1819
1820                         if (tag == 0) {
1821                                 /* Invalid tag, skip entry */
1822                                 ret = ARCHIVE_WARN;
1823                                 continue;
1824                         } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1825                             tag == ARCHIVE_ENTRY_ACL_GROUP) {
1826                                 n = 1;
1827                                 name = field[1];
1828                                 isint(name.start, name.end, &id);
1829                         } else
1830                                 n = 0;
1831
1832                         if (!is_nfs4_perms(field[1 + n].start,
1833                             field[1 + n].end, &permset)) {
1834                                 /* Invalid NFSv4 perms, skip entry */
1835                                 ret = ARCHIVE_WARN;
1836                                 continue;
1837                         }
1838                         if (!is_nfs4_flags(field[2 + n].start,
1839                             field[2 + n].end, &permset)) {
1840                                 /* Invalid NFSv4 flags, skip entry */
1841                                 ret = ARCHIVE_WARN;
1842                                 continue;
1843                         }
1844                         s = field[3 + n].start;
1845                         len = field[3 + n].end - field[3 + n].start;
1846                         type = 0;
1847                         if (len == 4) {
1848                                 if (memcmp(s, "deny", 4) == 0)
1849                                         type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1850                         } else if (len == 5) {
1851                                 if (memcmp(s, "allow", 5) == 0)
1852                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1853                                 else if (memcmp(s, "audit", 5) == 0)
1854                                         type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1855                                 else if (memcmp(s, "alarm", 5) == 0)
1856                                         type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1857                         }
1858                         if (type == 0) {
1859                                 /* Invalid entry type, skip entry */
1860                                 ret = ARCHIVE_WARN;
1861                                 continue;
1862                         }
1863                         isint(field[4 + n].start, field[4 + n].end,
1864                             &id);
1865                 }
1866
1867                 /* Add entry to the internal list. */
1868                 r = archive_acl_add_entry_len_l(acl, type, permset,
1869                     tag, id, name.start, name.end - name.start, sc);
1870                 if (r < ARCHIVE_WARN)
1871                         return (r);
1872                 if (r != ARCHIVE_OK)
1873                         ret = ARCHIVE_WARN;
1874                 types |= type;
1875         }
1876
1877         /* Reset ACL */
1878         archive_acl_reset(acl, types);
1879
1880         return (ret);
1881 }
1882
1883 /*
1884  * Parse a string to a positive decimal integer.  Returns true if
1885  * the string is non-empty and consists only of decimal digits,
1886  * false otherwise.
1887  */
1888 static int
1889 isint(const char *start, const char *end, int *result)
1890 {
1891         int n = 0;
1892         if (start >= end)
1893                 return (0);
1894         while (start < end) {
1895                 if (*start < '0' || *start > '9')
1896                         return (0);
1897                 if (n > (INT_MAX / 10) ||
1898                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1899                         n = INT_MAX;
1900                 } else {
1901                         n *= 10;
1902                         n += *start - '0';
1903                 }
1904                 start++;
1905         }
1906         *result = n;
1907         return (1);
1908 }
1909
1910 /*
1911  * Parse a string as a mode field.  Returns true if
1912  * the string is non-empty and consists only of mode characters,
1913  * false otherwise.
1914  */
1915 static int
1916 ismode(const char *start, const char *end, int *permset)
1917 {
1918         const char *p;
1919
1920         if (start >= end)
1921                 return (0);
1922         p = start;
1923         *permset = 0;
1924         while (p < end) {
1925                 switch (*p++) {
1926                 case 'r': case 'R':
1927                         *permset |= ARCHIVE_ENTRY_ACL_READ;
1928                         break;
1929                 case 'w': case 'W':
1930                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1931                         break;
1932                 case 'x': case 'X':
1933                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1934                         break;
1935                 case '-':
1936                         break;
1937                 default:
1938                         return (0);
1939                 }
1940         }
1941         return (1);
1942 }
1943
1944 /*
1945  * Parse a string as a NFS4 ACL permission field.
1946  * Returns true if the string is non-empty and consists only of NFS4 ACL
1947  * permission characters, false otherwise
1948  */
1949 static int
1950 is_nfs4_perms(const char *start, const char *end, int *permset)
1951 {
1952         const char *p = start;
1953
1954         while (p < end) {
1955                 switch (*p++) {
1956                 case 'r':
1957                         *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1958                         break;
1959                 case 'w':
1960                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1961                         break;
1962                 case 'x':
1963                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1964                         break;
1965                 case 'p':
1966                         *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1967                         break;
1968                 case 'D':
1969                         *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1970                         break;
1971                 case 'd':
1972                         *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1973                         break;
1974                 case 'a':
1975                         *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1976                         break;
1977                 case 'A':
1978                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1979                         break;
1980                 case 'R':
1981                         *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1982                         break;
1983                 case 'W':
1984                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1985                         break;
1986                 case 'c':
1987                         *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1988                         break;
1989                 case 'C':
1990                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1991                         break;
1992                 case 'o':
1993                         *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1994                         break;
1995                 case 's':
1996                         *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1997                         break;
1998                 case '-':
1999                         break;
2000                 default:
2001                         return(0);
2002                 }
2003         }
2004         return (1);
2005 }
2006
2007 /*
2008  * Parse a string as a NFS4 ACL flags field.
2009  * Returns true if the string is non-empty and consists only of NFS4 ACL
2010  * flag characters, false otherwise
2011  */
2012 static int
2013 is_nfs4_flags(const char *start, const char *end, int *permset)
2014 {
2015         const char *p = start;
2016
2017         while (p < end) {
2018                 switch(*p++) {
2019                 case 'f':
2020                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
2021                         break;
2022                 case 'd':
2023                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
2024                         break;
2025                 case 'i':
2026                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
2027                         break;
2028                 case 'n':
2029                         *permset |=
2030                             ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
2031                         break;
2032                 case 'S':
2033                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
2034                         break;
2035                 case 'F':
2036                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
2037                         break;
2038                 case 'I':
2039                         *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
2040                         break;
2041                 case '-':
2042                         break;
2043                 default:
2044                         return (0);
2045                 }
2046         }
2047         return (1);
2048 }
2049
2050 /*
2051  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
2052  * to point to just after the separator.  *start points to the first
2053  * character of the matched text and *end just after the last
2054  * character of the matched identifier.  In particular *end - *start
2055  * is the length of the field body, not including leading or trailing
2056  * whitespace.
2057  */
2058 static void
2059 next_field(const char **p, const char **start,
2060     const char **end, char *sep)
2061 {
2062         /* Skip leading whitespace to find start of field. */
2063         while (**p == ' ' || **p == '\t' || **p == '\n') {
2064                 (*p)++;
2065         }
2066         *start = *p;
2067
2068         /* Scan for the separator. */
2069         while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
2070             **p != '#') {
2071                 (*p)++;
2072         }
2073         *sep = **p;
2074
2075         /* Locate end of field, trim trailing whitespace if necessary */
2076         if (*p == *start) {
2077                 *end = *p;
2078         } else {
2079                 *end = *p - 1;
2080                 while (**end == ' ' || **end == '\t' || **end == '\n') {
2081                         (*end)--;
2082                 }
2083                 (*end)++;
2084         }
2085
2086         /* Handle in-field comments */
2087         if (*sep == '#') {
2088                 while (**p != '\0' && **p != ',' && **p != '\n') {
2089                         (*p)++;
2090                 }
2091                 *sep = **p;
2092         }
2093
2094         /* Adjust scanner location. */
2095         if (**p != '\0')
2096                 (*p)++;
2097 }