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