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