Merge branch 'vendor/LIBARCHIVE'
[dragonfly.git] / contrib / libarchive / libarchive / archive_acl.c
1 /*-
2  * Copyright (c) 2003-2010 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
28
29 #ifdef HAVE_ERRNO_H
30 #include <errno.h>
31 #endif
32 #ifdef HAVE_LIMITS_H
33 #include <limits.h>
34 #endif
35 #ifdef HAVE_WCHAR_H
36 #include <wchar.h>
37 #endif
38
39 #include "archive_acl_private.h"
40 #include "archive_entry.h"
41 #include "archive_private.h"
42
43 #undef max
44 #define max(a, b)       ((a)>(b)?(a):(b))
45
46 #ifndef HAVE_WMEMCMP
47 /* Good enough for simple equality testing, but not for sorting. */
48 #define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
49 #endif
50
51 static int      acl_special(struct archive_acl *acl,
52                     int type, int permset, int tag);
53 static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
54                     int type, int permset, int tag, int id);
55 static int      archive_acl_add_entry_len_l(struct archive_acl *acl,
56                     int type, int permset, int tag, int id, const char *name,
57                     size_t len, struct archive_string_conv *sc);
58 static int      isint_w(const wchar_t *start, const wchar_t *end, int *result);
59 static int      ismode_w(const wchar_t *start, const wchar_t *end, int *result);
60 static void     next_field_w(const wchar_t **wp, const wchar_t **start,
61                     const wchar_t **end, wchar_t *sep);
62 static int      prefix_w(const wchar_t *start, const wchar_t *end,
63                     const wchar_t *test);
64 static void     append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
65                     const wchar_t *wname, int perm, int id);
66 static void     append_id_w(wchar_t **wp, int id);
67 static int      isint(const char *start, const char *end, int *result);
68 static int      ismode(const char *start, const char *end, int *result);
69 static void     next_field(const char **p, const char **start,
70                     const char **end, char *sep);
71 static int      prefix_c(const char *start, const char *end,
72                     const char *test);
73 static void     append_entry(char **p, const char *prefix, int tag,
74                     const char *name, int perm, int id);
75 static void     append_id(char **p, int id);
76
77 void
78 archive_acl_clear(struct archive_acl *acl)
79 {
80         struct archive_acl_entry *ap;
81
82         while (acl->acl_head != NULL) {
83                 ap = acl->acl_head->next;
84                 archive_mstring_clean(&acl->acl_head->name);
85                 free(acl->acl_head);
86                 acl->acl_head = ap;
87         }
88         if (acl->acl_text_w != NULL) {
89                 free(acl->acl_text_w);
90                 acl->acl_text_w = NULL;
91         }
92         if (acl->acl_text != NULL) {
93                 free(acl->acl_text);
94                 acl->acl_text = NULL;
95         }
96         acl->acl_p = NULL;
97         acl->acl_state = 0; /* Not counting. */
98 }
99
100 void
101 archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
102 {
103         struct archive_acl_entry *ap, *ap2;
104
105         archive_acl_clear(dest);
106
107         dest->mode = src->mode;
108         ap = src->acl_head;
109         while (ap != NULL) {
110                 ap2 = acl_new_entry(dest,
111                     ap->type, ap->permset, ap->tag, ap->id);
112                 if (ap2 != NULL)
113                         archive_mstring_copy(&ap2->name, &ap->name);
114                 ap = ap->next;
115         }
116 }
117
118 int
119 archive_acl_add_entry(struct archive_acl *acl,
120     int type, int permset, int tag, int id, const char *name)
121 {
122         struct archive_acl_entry *ap;
123
124         if (acl_special(acl, type, permset, tag) == 0)
125                 return ARCHIVE_OK;
126         ap = acl_new_entry(acl, type, permset, tag, id);
127         if (ap == NULL) {
128                 /* XXX Error XXX */
129                 return ARCHIVE_FAILED;
130         }
131         if (name != NULL  &&  *name != '\0')
132                 archive_mstring_copy_mbs(&ap->name, name);
133         else
134                 archive_mstring_clean(&ap->name);
135         return ARCHIVE_OK;
136 }
137
138 int
139 archive_acl_add_entry_w_len(struct archive_acl *acl,
140     int type, int permset, int tag, int id, const wchar_t *name, size_t len)
141 {
142         struct archive_acl_entry *ap;
143
144         if (acl_special(acl, type, permset, tag) == 0)
145                 return ARCHIVE_OK;
146         ap = acl_new_entry(acl, type, permset, tag, id);
147         if (ap == NULL) {
148                 /* XXX Error XXX */
149                 return ARCHIVE_FAILED;
150         }
151         if (name != NULL  &&  *name != L'\0' && len > 0)
152                 archive_mstring_copy_wcs_len(&ap->name, name, len);
153         else
154                 archive_mstring_clean(&ap->name);
155         return ARCHIVE_OK;
156 }
157
158 static int
159 archive_acl_add_entry_len_l(struct archive_acl *acl,
160     int type, int permset, int tag, int id, const char *name, size_t len,
161     struct archive_string_conv *sc)
162 {
163         struct archive_acl_entry *ap;
164         int r;
165
166         if (acl_special(acl, type, permset, tag) == 0)
167                 return ARCHIVE_OK;
168         ap = acl_new_entry(acl, type, permset, tag, id);
169         if (ap == NULL) {
170                 /* XXX Error XXX */
171                 return ARCHIVE_FAILED;
172         }
173         if (name != NULL  &&  *name != '\0' && len > 0) {
174                 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
175         } else {
176                 r = 0;
177                 archive_mstring_clean(&ap->name);
178         }
179         if (r == 0)
180                 return (ARCHIVE_OK);
181         else if (errno == ENOMEM)
182                 return (ARCHIVE_FATAL);
183         else
184                 return (ARCHIVE_WARN);
185 }
186
187 /*
188  * If this ACL entry is part of the standard POSIX permissions set,
189  * store the permissions in the stat structure and return zero.
190  */
191 static int
192 acl_special(struct archive_acl *acl, int type, int permset, int tag)
193 {
194         if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
195             && ((permset & ~007) == 0)) {
196                 switch (tag) {
197                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
198                         acl->mode &= ~0700;
199                         acl->mode |= (permset & 7) << 6;
200                         return (0);
201                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
202                         acl->mode &= ~0070;
203                         acl->mode |= (permset & 7) << 3;
204                         return (0);
205                 case ARCHIVE_ENTRY_ACL_OTHER:
206                         acl->mode &= ~0007;
207                         acl->mode |= permset & 7;
208                         return (0);
209                 }
210         }
211         return (1);
212 }
213
214 /*
215  * Allocate and populate a new ACL entry with everything but the
216  * name.
217  */
218 static struct archive_acl_entry *
219 acl_new_entry(struct archive_acl *acl,
220     int type, int permset, int tag, int id)
221 {
222         struct archive_acl_entry *ap, *aq;
223
224         /* Type argument must be a valid NFS4 or POSIX.1e type.
225          * The type must agree with anything already set and
226          * the permset must be compatible. */
227         if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
228                 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
229                         return (NULL);
230                 }
231                 if (permset &
232                     ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
233                         | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
234                         return (NULL);
235                 }
236         } else  if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
237                 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
238                         return (NULL);
239                 }
240                 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
241                         return (NULL);
242                 }
243         } else {
244                 return (NULL);
245         }
246
247         /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
248         switch (tag) {
249         case ARCHIVE_ENTRY_ACL_USER:
250         case ARCHIVE_ENTRY_ACL_USER_OBJ:
251         case ARCHIVE_ENTRY_ACL_GROUP:
252         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
253                 /* Tags valid in both NFS4 and POSIX.1e */
254                 break;
255         case ARCHIVE_ENTRY_ACL_MASK:
256         case ARCHIVE_ENTRY_ACL_OTHER:
257                 /* Tags valid only in POSIX.1e. */
258                 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
259                         return (NULL);
260                 }
261                 break;
262         case ARCHIVE_ENTRY_ACL_EVERYONE:
263                 /* Tags valid only in NFS4. */
264                 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
265                         return (NULL);
266                 }
267                 break;
268         default:
269                 /* No other values are valid. */
270                 return (NULL);
271         }
272
273         if (acl->acl_text_w != NULL) {
274                 free(acl->acl_text_w);
275                 acl->acl_text_w = NULL;
276         }
277         if (acl->acl_text != NULL) {
278                 free(acl->acl_text);
279                 acl->acl_text = NULL;
280         }
281
282         /* If there's a matching entry already in the list, overwrite it. */
283         ap = acl->acl_head;
284         aq = NULL;
285         while (ap != NULL) {
286                 if (ap->type == type && ap->tag == tag && ap->id == id) {
287                         ap->permset = permset;
288                         return (ap);
289                 }
290                 aq = ap;
291                 ap = ap->next;
292         }
293
294         /* Add a new entry to the end of the list. */
295         ap = (struct archive_acl_entry *)malloc(sizeof(*ap));
296         if (ap == NULL)
297                 return (NULL);
298         memset(ap, 0, sizeof(*ap));
299         if (aq == NULL)
300                 acl->acl_head = ap;
301         else
302                 aq->next = ap;
303         ap->type = type;
304         ap->tag = tag;
305         ap->id = id;
306         ap->permset = permset;
307         acl->acl_types |= type;
308         return (ap);
309 }
310
311 /*
312  * Return a count of entries matching "want_type".
313  */
314 int
315 archive_acl_count(struct archive_acl *acl, int want_type)
316 {
317         int count;
318         struct archive_acl_entry *ap;
319
320         count = 0;
321         ap = acl->acl_head;
322         while (ap != NULL) {
323                 if ((ap->type & want_type) != 0)
324                         count++;
325                 ap = ap->next;
326         }
327
328         if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
329                 count += 3;
330         return (count);
331 }
332
333 /*
334  * Prepare for reading entries from the ACL data.  Returns a count
335  * of entries matching "want_type", or zero if there are no
336  * non-extended ACL entries of that type.
337  */
338 int
339 archive_acl_reset(struct archive_acl *acl, int want_type)
340 {
341         int count, cutoff;
342
343         count = archive_acl_count(acl, want_type);
344
345         /*
346          * If the only entries are the three standard ones,
347          * then don't return any ACL data.  (In this case,
348          * client can just use chmod(2) to set permissions.)
349          */
350         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
351                 cutoff = 3;
352         else
353                 cutoff = 0;
354
355         if (count > cutoff)
356                 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
357         else
358                 acl->acl_state = 0;
359         acl->acl_p = acl->acl_head;
360         return (count);
361 }
362
363
364 /*
365  * Return the next ACL entry in the list.  Fake entries for the
366  * standard permissions and include them in the returned list.
367  */
368 int
369 archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
370     int *permset, int *tag, int *id, const char **name)
371 {
372         *name = NULL;
373         *id = -1;
374
375         /*
376          * The acl_state is either zero (no entries available), -1
377          * (reading from list), or an entry type (retrieve that type
378          * from ae_stat.aest_mode).
379          */
380         if (acl->acl_state == 0)
381                 return (ARCHIVE_WARN);
382
383         /* The first three access entries are special. */
384         if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
385                 switch (acl->acl_state) {
386                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
387                         *permset = (acl->mode >> 6) & 7;
388                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
389                         *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
390                         acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
391                         return (ARCHIVE_OK);
392                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
393                         *permset = (acl->mode >> 3) & 7;
394                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
395                         *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
396                         acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
397                         return (ARCHIVE_OK);
398                 case ARCHIVE_ENTRY_ACL_OTHER:
399                         *permset = acl->mode & 7;
400                         *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
401                         *tag = ARCHIVE_ENTRY_ACL_OTHER;
402                         acl->acl_state = -1;
403                         acl->acl_p = acl->acl_head;
404                         return (ARCHIVE_OK);
405                 default:
406                         break;
407                 }
408         }
409
410         while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
411                 acl->acl_p = acl->acl_p->next;
412         if (acl->acl_p == NULL) {
413                 acl->acl_state = 0;
414                 *type = 0;
415                 *permset = 0;
416                 *tag = 0;
417                 *id = -1;
418                 *name = NULL;
419                 return (ARCHIVE_EOF); /* End of ACL entries. */
420         }
421         *type = acl->acl_p->type;
422         *permset = acl->acl_p->permset;
423         *tag = acl->acl_p->tag;
424         *id = acl->acl_p->id;
425         if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
426                 if (errno == ENOMEM)
427                         return (ARCHIVE_FATAL);
428                 *name = NULL;
429         }
430         acl->acl_p = acl->acl_p->next;
431         return (ARCHIVE_OK);
432 }
433
434 /*
435  * Generate a text version of the ACL.  The flags parameter controls
436  * the style of the generated ACL.
437  */
438 const wchar_t *
439 archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
440 {
441         int count;
442         size_t length;
443         const wchar_t *wname;
444         const wchar_t *prefix;
445         wchar_t separator;
446         struct archive_acl_entry *ap;
447         int id, r;
448         wchar_t *wp;
449
450         if (acl->acl_text_w != NULL) {
451                 free (acl->acl_text_w);
452                 acl->acl_text_w = NULL;
453         }
454
455         separator = L',';
456         count = 0;
457         length = 0;
458         ap = acl->acl_head;
459         while (ap != NULL) {
460                 if ((ap->type & flags) != 0) {
461                         count++;
462                         if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
463                             (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
464                                 length += 8; /* "default:" */
465                         length += 5; /* tag name */
466                         length += 1; /* colon */
467                         r = archive_mstring_get_wcs(a, &ap->name, &wname);
468                         if (r == 0 && wname != NULL)
469                                 length += wcslen(wname);
470                         else if (r < 0 && errno == ENOMEM)
471                                 return (NULL);
472                         else
473                                 length += sizeof(uid_t) * 3 + 1;
474                         length ++; /* colon */
475                         length += 3; /* rwx */
476                         length += 1; /* colon */
477                         length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
478                         length ++; /* newline */
479                 }
480                 ap = ap->next;
481         }
482
483         if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
484                 length += 10; /* "user::rwx\n" */
485                 length += 11; /* "group::rwx\n" */
486                 length += 11; /* "other::rwx\n" */
487         }
488
489         if (count == 0)
490                 return (NULL);
491
492         /* Now, allocate the string and actually populate it. */
493         wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
494         if (wp == NULL)
495                 return (NULL);
496         count = 0;
497         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
498                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
499                     acl->mode & 0700, -1);
500                 *wp++ = ',';
501                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
502                     acl->mode & 0070, -1);
503                 *wp++ = ',';
504                 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
505                     acl->mode & 0007, -1);
506                 count += 3;
507
508                 ap = acl->acl_head;
509                 while (ap != NULL) {
510                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
511                                 r = archive_mstring_get_wcs(a, &ap->name, &wname);
512                                 if (r == 0) {
513                                         *wp++ = separator;
514                                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
515                                                 id = ap->id;
516                                         else
517                                                 id = -1;
518                                         append_entry_w(&wp, NULL, ap->tag, wname,
519                                             ap->permset, id);
520                                         count++;
521                                 } else if (r < 0 && errno == ENOMEM)
522                                         return (NULL);
523                         }
524                         ap = ap->next;
525                 }
526         }
527
528
529         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
530                 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
531                         prefix = L"default:";
532                 else
533                         prefix = NULL;
534                 ap = acl->acl_head;
535                 count = 0;
536                 while (ap != NULL) {
537                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
538                                 r = archive_mstring_get_wcs(a, &ap->name, &wname);
539                                 if (r == 0) {
540                                         if (count > 0)
541                                                 *wp++ = separator;
542                                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
543                                                 id = ap->id;
544                                         else
545                                                 id = -1;
546                                         append_entry_w(&wp, prefix, ap->tag,
547                                             wname, ap->permset, id);
548                                         count ++;
549                                 } else if (r < 0 && errno == ENOMEM)
550                                         return (NULL);
551                         }
552                         ap = ap->next;
553                 }
554         }
555
556         return (acl->acl_text_w);
557 }
558
559
560 static void
561 append_id_w(wchar_t **wp, int id)
562 {
563         if (id < 0)
564                 id = 0;
565         if (id > 9)
566                 append_id_w(wp, id / 10);
567         *(*wp)++ = L"0123456789"[id % 10];
568 }
569
570 static void
571 append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
572     const wchar_t *wname, int perm, int id)
573 {
574         if (prefix != NULL) {
575                 wcscpy(*wp, prefix);
576                 *wp += wcslen(*wp);
577         }
578         switch (tag) {
579         case ARCHIVE_ENTRY_ACL_USER_OBJ:
580                 wname = NULL;
581                 id = -1;
582                 /* FALLTHROUGH */
583         case ARCHIVE_ENTRY_ACL_USER:
584                 wcscpy(*wp, L"user");
585                 break;
586         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
587                 wname = NULL;
588                 id = -1;
589                 /* FALLTHROUGH */
590         case ARCHIVE_ENTRY_ACL_GROUP:
591                 wcscpy(*wp, L"group");
592                 break;
593         case ARCHIVE_ENTRY_ACL_MASK:
594                 wcscpy(*wp, L"mask");
595                 wname = NULL;
596                 id = -1;
597                 break;
598         case ARCHIVE_ENTRY_ACL_OTHER:
599                 wcscpy(*wp, L"other");
600                 wname = NULL;
601                 id = -1;
602                 break;
603         }
604         *wp += wcslen(*wp);
605         *(*wp)++ = L':';
606         if (wname != NULL) {
607                 wcscpy(*wp, wname);
608                 *wp += wcslen(*wp);
609         } else if (tag == ARCHIVE_ENTRY_ACL_USER
610             || tag == ARCHIVE_ENTRY_ACL_GROUP) {
611                 append_id_w(wp, id);
612                 id = -1;
613         }
614         *(*wp)++ = L':';
615         *(*wp)++ = (perm & 0444) ? L'r' : L'-';
616         *(*wp)++ = (perm & 0222) ? L'w' : L'-';
617         *(*wp)++ = (perm & 0111) ? L'x' : L'-';
618         if (id != -1) {
619                 *(*wp)++ = L':';
620                 append_id_w(wp, id);
621         }
622         **wp = L'\0';
623 }
624
625 int
626 archive_acl_text_l(struct archive_acl *acl, int flags,
627     const char **acl_text, size_t *acl_text_len,
628     struct archive_string_conv *sc)
629 {
630         int count;
631         size_t length;
632         const char *name;
633         const char *prefix;
634         char separator;
635         struct archive_acl_entry *ap;
636         size_t len;
637         int id, r;
638         char *p;
639
640         if (acl->acl_text != NULL) {
641                 free (acl->acl_text);
642                 acl->acl_text = NULL;
643         }
644
645         *acl_text = NULL;
646         if (acl_text_len != NULL)
647                 *acl_text_len = 0;
648         separator = ',';
649         count = 0;
650         length = 0;
651         ap = acl->acl_head;
652         while (ap != NULL) {
653                 if ((ap->type & flags) != 0) {
654                         count++;
655                         if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
656                             (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
657                                 length += 8; /* "default:" */
658                         length += 5; /* tag name */
659                         length += 1; /* colon */
660                         r = archive_mstring_get_mbs_l(
661                             &ap->name, &name, &len, sc);
662                         if (r != 0)
663                                 return (-1);
664                         if (len > 0 && name != NULL)
665                                 length += len;
666                         else
667                                 length += sizeof(uid_t) * 3 + 1;
668                         length ++; /* colon */
669                         length += 3; /* rwx */
670                         length += 1; /* colon */
671                         length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
672                         length ++; /* newline */
673                 }
674                 ap = ap->next;
675         }
676
677         if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
678                 length += 10; /* "user::rwx\n" */
679                 length += 11; /* "group::rwx\n" */
680                 length += 11; /* "other::rwx\n" */
681         }
682
683         if (count == 0)
684                 return (0);
685
686         /* Now, allocate the string and actually populate it. */
687         p = acl->acl_text = (char *)malloc(length);
688         if (p == NULL)
689                 return (-1);
690         count = 0;
691         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
692                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
693                     acl->mode & 0700, -1);
694                 *p++ = ',';
695                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
696                     acl->mode & 0070, -1);
697                 *p++ = ',';
698                 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
699                     acl->mode & 0007, -1);
700                 count += 3;
701
702                 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
703                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
704                                 continue;
705                         r = archive_mstring_get_mbs_l(
706                             &ap->name, &name, &len, sc);
707                         if (r != 0)
708                                 return (-1);
709                         *p++ = separator;
710                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
711                                 id = ap->id;
712                         else
713                                 id = -1;
714                         append_entry(&p, NULL, ap->tag, name,
715                             ap->permset, id);
716                         count++;
717                 }
718         }
719
720
721         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
722                 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
723                         prefix = "default:";
724                 else
725                         prefix = NULL;
726                 count = 0;
727                 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
728                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
729                                 continue;
730                         r = archive_mstring_get_mbs_l(
731                             &ap->name, &name, &len, sc);
732                         if (r != 0)
733                                 return (-1);
734                         if (count > 0)
735                                 *p++ = separator;
736                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
737                                 id = ap->id;
738                         else
739                                 id = -1;
740                         append_entry(&p, prefix, ap->tag,
741                             name, ap->permset, id);
742                         count ++;
743                 }
744         }
745
746         *acl_text = acl->acl_text;
747         if (acl_text_len != NULL)
748                 *acl_text_len = strlen(acl->acl_text);
749         return (0);
750 }
751
752 static void
753 append_id(char **p, int id)
754 {
755         if (id < 0)
756                 id = 0;
757         if (id > 9)
758                 append_id(p, id / 10);
759         *(*p)++ = "0123456789"[id % 10];
760 }
761
762 static void
763 append_entry(char **p, const char *prefix, int tag,
764     const char *name, int perm, int id)
765 {
766         if (prefix != NULL) {
767                 strcpy(*p, prefix);
768                 *p += strlen(*p);
769         }
770         switch (tag) {
771         case ARCHIVE_ENTRY_ACL_USER_OBJ:
772                 name = NULL;
773                 id = -1;
774                 /* FALLTHROUGH */
775         case ARCHIVE_ENTRY_ACL_USER:
776                 strcpy(*p, "user");
777                 break;
778         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
779                 name = NULL;
780                 id = -1;
781                 /* FALLTHROUGH */
782         case ARCHIVE_ENTRY_ACL_GROUP:
783                 strcpy(*p, "group");
784                 break;
785         case ARCHIVE_ENTRY_ACL_MASK:
786                 strcpy(*p, "mask");
787                 name = NULL;
788                 id = -1;
789                 break;
790         case ARCHIVE_ENTRY_ACL_OTHER:
791                 strcpy(*p, "other");
792                 name = NULL;
793                 id = -1;
794                 break;
795         }
796         *p += strlen(*p);
797         *(*p)++ = ':';
798         if (name != NULL) {
799                 strcpy(*p, name);
800                 *p += strlen(*p);
801         } else if (tag == ARCHIVE_ENTRY_ACL_USER
802             || tag == ARCHIVE_ENTRY_ACL_GROUP) {
803                 append_id(p, id);
804                 id = -1;
805         }
806         *(*p)++ = ':';
807         *(*p)++ = (perm & 0444) ? 'r' : '-';
808         *(*p)++ = (perm & 0222) ? 'w' : '-';
809         *(*p)++ = (perm & 0111) ? 'x' : '-';
810         if (id != -1) {
811                 *(*p)++ = ':';
812                 append_id(p, id);
813         }
814         **p = '\0';
815 }
816
817 /*
818  * Parse a textual ACL.  This automatically recognizes and supports
819  * extensions described above.  The 'type' argument is used to
820  * indicate the type that should be used for any entries not
821  * explicitly marked as "default:".
822  */
823 int
824 archive_acl_parse_w(struct archive_acl *acl,
825     const wchar_t *text, int default_type)
826 {
827         struct {
828                 const wchar_t *start;
829                 const wchar_t *end;
830         } field[4], name;
831
832         int fields, n;
833         int type, tag, permset, id;
834         wchar_t sep;
835
836         while (text != NULL  &&  *text != L'\0') {
837                 /*
838                  * Parse the fields out of the next entry,
839                  * advance 'text' to start of next entry.
840                  */
841                 fields = 0;
842                 do {
843                         const wchar_t *start, *end;
844                         next_field_w(&text, &start, &end, &sep);
845                         if (fields < 4) {
846                                 field[fields].start = start;
847                                 field[fields].end = end;
848                         }
849                         ++fields;
850                 } while (sep == L':');
851
852                 /* Set remaining fields to blank. */
853                 for (n = fields; n < 4; ++n)
854                         field[n].start = field[n].end = NULL;
855
856                 /* Check for a numeric ID in field 1 or 3. */
857                 id = -1;
858                 isint_w(field[1].start, field[1].end, &id);
859                 /* Field 3 is optional. */
860                 if (id == -1 && fields > 3)
861                         isint_w(field[3].start, field[3].end, &id);
862
863                 /*
864                  * Solaris extension:  "defaultuser::rwx" is the
865                  * default ACL corresponding to "user::rwx", etc.
866                  */
867                 if (field[0].end - field[0].start > 7
868                     && wmemcmp(field[0].start, L"default", 7) == 0) {
869                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
870                         field[0].start += 7;
871                 } else
872                         type = default_type;
873
874                 name.start = name.end = NULL;
875                 if (prefix_w(field[0].start, field[0].end, L"user")) {
876                         if (!ismode_w(field[2].start, field[2].end, &permset))
877                                 return (ARCHIVE_WARN);
878                         if (id != -1 || field[1].start < field[1].end) {
879                                 tag = ARCHIVE_ENTRY_ACL_USER;
880                                 name = field[1];
881                         } else
882                                 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
883                 } else if (prefix_w(field[0].start, field[0].end, L"group")) {
884                         if (!ismode_w(field[2].start, field[2].end, &permset))
885                                 return (ARCHIVE_WARN);
886                         if (id != -1 || field[1].start < field[1].end) {
887                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
888                                 name = field[1];
889                         } else
890                                 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
891                 } else if (prefix_w(field[0].start, field[0].end, L"other")) {
892                         if (fields == 2
893                             && field[1].start < field[1].end
894                             && ismode_w(field[1].start, field[1].end, &permset)) {
895                                 /* This is Solaris-style "other:rwx" */
896                         } else if (fields == 3
897                             && field[1].start == field[1].end
898                             && field[2].start < field[2].end
899                             && ismode_w(field[2].start, field[2].end, &permset)) {
900                                 /* This is FreeBSD-style "other::rwx" */
901                         } else
902                                 return (ARCHIVE_WARN);
903                         tag = ARCHIVE_ENTRY_ACL_OTHER;
904                 } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
905                         if (fields == 2
906                             && field[1].start < field[1].end
907                             && ismode_w(field[1].start, field[1].end, &permset)) {
908                                 /* This is Solaris-style "mask:rwx" */
909                         } else if (fields == 3
910                             && field[1].start == field[1].end
911                             && field[2].start < field[2].end
912                             && ismode_w(field[2].start, field[2].end, &permset)) {
913                                 /* This is FreeBSD-style "mask::rwx" */
914                         } else
915                                 return (ARCHIVE_WARN);
916                         tag = ARCHIVE_ENTRY_ACL_MASK;
917                 } else
918                         return (ARCHIVE_WARN);
919
920                 /* Add entry to the internal list. */
921                 archive_acl_add_entry_w_len(acl, type, permset,
922                     tag, id, name.start, name.end - name.start);
923         }
924         return (ARCHIVE_OK);
925 }
926
927 /*
928  * Parse a string to a positive decimal integer.  Returns true if
929  * the string is non-empty and consists only of decimal digits,
930  * false otherwise.
931  */
932 static int
933 isint_w(const wchar_t *start, const wchar_t *end, int *result)
934 {
935         int n = 0;
936         if (start >= end)
937                 return (0);
938         while (start < end) {
939                 if (*start < '0' || *start > '9')
940                         return (0);
941                 if (n > (INT_MAX / 10) ||
942                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
943                         n = INT_MAX;
944                 } else {
945                         n *= 10;
946                         n += *start - '0';
947                 }
948                 start++;
949         }
950         *result = n;
951         return (1);
952 }
953
954 /*
955  * Parse a string as a mode field.  Returns true if
956  * the string is non-empty and consists only of mode characters,
957  * false otherwise.
958  */
959 static int
960 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
961 {
962         const wchar_t *p;
963
964         if (start >= end)
965                 return (0);
966         p = start;
967         *permset = 0;
968         while (p < end) {
969                 switch (*p++) {
970                 case 'r': case 'R':
971                         *permset |= ARCHIVE_ENTRY_ACL_READ;
972                         break;
973                 case 'w': case 'W':
974                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
975                         break;
976                 case 'x': case 'X':
977                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
978                         break;
979                 case '-':
980                         break;
981                 default:
982                         return (0);
983                 }
984         }
985         return (1);
986 }
987
988 /*
989  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
990  * to point to just after the separator.  *start points to the first
991  * character of the matched text and *end just after the last
992  * character of the matched identifier.  In particular *end - *start
993  * is the length of the field body, not including leading or trailing
994  * whitespace.
995  */
996 static void
997 next_field_w(const wchar_t **wp, const wchar_t **start,
998     const wchar_t **end, wchar_t *sep)
999 {
1000         /* Skip leading whitespace to find start of field. */
1001         while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1002                 (*wp)++;
1003         }
1004         *start = *wp;
1005
1006         /* Scan for the separator. */
1007         while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1008             **wp != L'\n') {
1009                 (*wp)++;
1010         }
1011         *sep = **wp;
1012
1013         /* Trim trailing whitespace to locate end of field. */
1014         *end = *wp - 1;
1015         while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1016                 (*end)--;
1017         }
1018         (*end)++;
1019
1020         /* Adjust scanner location. */
1021         if (**wp != L'\0')
1022                 (*wp)++;
1023 }
1024
1025 /*
1026  * Return true if the characters [start...end) are a prefix of 'test'.
1027  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1028  */
1029 static int
1030 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1031 {
1032         if (start == end)
1033                 return (0);
1034
1035         if (*start++ != *test++)
1036                 return (0);
1037
1038         while (start < end  &&  *start++ == *test++)
1039                 ;
1040
1041         if (start < end)
1042                 return (0);
1043
1044         return (1);
1045 }
1046
1047 /*
1048  * Parse a textual ACL.  This automatically recognizes and supports
1049  * extensions described above.  The 'type' argument is used to
1050  * indicate the type that should be used for any entries not
1051  * explicitly marked as "default:".
1052  */
1053 int
1054 archive_acl_parse_l(struct archive_acl *acl,
1055     const char *text, int default_type, struct archive_string_conv *sc)
1056 {
1057         struct {
1058                 const char *start;
1059                 const char *end;
1060         } field[4], name;
1061
1062         int fields, n, r, ret = ARCHIVE_OK;
1063         int type, tag, permset, id;
1064         char sep;
1065
1066         while (text != NULL  &&  *text != '\0') {
1067                 /*
1068                  * Parse the fields out of the next entry,
1069                  * advance 'text' to start of next entry.
1070                  */
1071                 fields = 0;
1072                 do {
1073                         const char *start, *end;
1074                         next_field(&text, &start, &end, &sep);
1075                         if (fields < 4) {
1076                                 field[fields].start = start;
1077                                 field[fields].end = end;
1078                         }
1079                         ++fields;
1080                 } while (sep == ':');
1081
1082                 /* Set remaining fields to blank. */
1083                 for (n = fields; n < 4; ++n)
1084                         field[n].start = field[n].end = NULL;
1085
1086                 /* Check for a numeric ID in field 1 or 3. */
1087                 id = -1;
1088                 isint(field[1].start, field[1].end, &id);
1089                 /* Field 3 is optional. */
1090                 if (id == -1 && fields > 3)
1091                         isint(field[3].start, field[3].end, &id);
1092
1093                 /*
1094                  * Solaris extension:  "defaultuser::rwx" is the
1095                  * default ACL corresponding to "user::rwx", etc.
1096                  */
1097                 if (field[0].end - field[0].start > 7
1098                     && memcmp(field[0].start, "default", 7) == 0) {
1099                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1100                         field[0].start += 7;
1101                 } else
1102                         type = default_type;
1103
1104                 name.start = name.end = NULL;
1105                 if (prefix_c(field[0].start, field[0].end, "user")) {
1106                         if (!ismode(field[2].start, field[2].end, &permset))
1107                                 return (ARCHIVE_WARN);
1108                         if (id != -1 || field[1].start < field[1].end) {
1109                                 tag = ARCHIVE_ENTRY_ACL_USER;
1110                                 name = field[1];
1111                         } else
1112                                 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1113                 } else if (prefix_c(field[0].start, field[0].end, "group")) {
1114                         if (!ismode(field[2].start, field[2].end, &permset))
1115                                 return (ARCHIVE_WARN);
1116                         if (id != -1 || field[1].start < field[1].end) {
1117                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
1118                                 name = field[1];
1119                         } else
1120                                 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1121                 } else if (prefix_c(field[0].start, field[0].end, "other")) {
1122                         if (fields == 2
1123                             && field[1].start < field[1].end
1124                             && ismode(field[1].start, field[1].end, &permset)) {
1125                                 /* This is Solaris-style "other:rwx" */
1126                         } else if (fields == 3
1127                             && field[1].start == field[1].end
1128                             && field[2].start < field[2].end
1129                             && ismode(field[2].start, field[2].end, &permset)) {
1130                                 /* This is FreeBSD-style "other::rwx" */
1131                         } else
1132                                 return (ARCHIVE_WARN);
1133                         tag = ARCHIVE_ENTRY_ACL_OTHER;
1134                 } else if (prefix_c(field[0].start, field[0].end, "mask")) {
1135                         if (fields == 2
1136                             && field[1].start < field[1].end
1137                             && ismode(field[1].start, field[1].end, &permset)) {
1138                                 /* This is Solaris-style "mask:rwx" */
1139                         } else if (fields == 3
1140                             && field[1].start == field[1].end
1141                             && field[2].start < field[2].end
1142                             && ismode(field[2].start, field[2].end, &permset)) {
1143                                 /* This is FreeBSD-style "mask::rwx" */
1144                         } else
1145                                 return (ARCHIVE_WARN);
1146                         tag = ARCHIVE_ENTRY_ACL_MASK;
1147                 } else
1148                         return (ARCHIVE_WARN);
1149
1150                 /* Add entry to the internal list. */
1151                 r = archive_acl_add_entry_len_l(acl, type, permset,
1152                     tag, id, name.start, name.end - name.start, sc);
1153                 if (r < ARCHIVE_WARN)
1154                         return (r);
1155                 if (r != ARCHIVE_OK)
1156                         ret = ARCHIVE_WARN;
1157         }
1158         return (ret);
1159 }
1160
1161 /*
1162  * Parse a string to a positive decimal integer.  Returns true if
1163  * the string is non-empty and consists only of decimal digits,
1164  * false otherwise.
1165  */
1166 static int
1167 isint(const char *start, const char *end, int *result)
1168 {
1169         int n = 0;
1170         if (start >= end)
1171                 return (0);
1172         while (start < end) {
1173                 if (*start < '0' || *start > '9')
1174                         return (0);
1175                 if (n > (INT_MAX / 10) ||
1176                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1177                         n = INT_MAX;
1178                 } else {
1179                         n *= 10;
1180                         n += *start - '0';
1181                 }
1182                 start++;
1183         }
1184         *result = n;
1185         return (1);
1186 }
1187
1188 /*
1189  * Parse a string as a mode field.  Returns true if
1190  * the string is non-empty and consists only of mode characters,
1191  * false otherwise.
1192  */
1193 static int
1194 ismode(const char *start, const char *end, int *permset)
1195 {
1196         const char *p;
1197
1198         if (start >= end)
1199                 return (0);
1200         p = start;
1201         *permset = 0;
1202         while (p < end) {
1203                 switch (*p++) {
1204                 case 'r': case 'R':
1205                         *permset |= ARCHIVE_ENTRY_ACL_READ;
1206                         break;
1207                 case 'w': case 'W':
1208                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1209                         break;
1210                 case 'x': case 'X':
1211                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1212                         break;
1213                 case '-':
1214                         break;
1215                 default:
1216                         return (0);
1217                 }
1218         }
1219         return (1);
1220 }
1221
1222 /*
1223  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1224  * to point to just after the separator.  *start points to the first
1225  * character of the matched text and *end just after the last
1226  * character of the matched identifier.  In particular *end - *start
1227  * is the length of the field body, not including leading or trailing
1228  * whitespace.
1229  */
1230 static void
1231 next_field(const char **p, const char **start,
1232     const char **end, char *sep)
1233 {
1234         /* Skip leading whitespace to find start of field. */
1235         while (**p == ' ' || **p == '\t' || **p == '\n') {
1236                 (*p)++;
1237         }
1238         *start = *p;
1239
1240         /* Scan for the separator. */
1241         while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1242                 (*p)++;
1243         }
1244         *sep = **p;
1245
1246         /* Trim trailing whitespace to locate end of field. */
1247         *end = *p - 1;
1248         while (**end == ' ' || **end == '\t' || **end == '\n') {
1249                 (*end)--;
1250         }
1251         (*end)++;
1252
1253         /* Adjust scanner location. */
1254         if (**p != '\0')
1255                 (*p)++;
1256 }
1257
1258 /*
1259  * Return true if the characters [start...end) are a prefix of 'test'.
1260  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1261  */
1262 static int
1263 prefix_c(const char *start, const char *end, const char *test)
1264 {
1265         if (start == end)
1266                 return (0);
1267
1268         if (*start++ != *test++)
1269                 return (0);
1270
1271         while (start < end  &&  *start++ == *test++)
1272                 ;
1273
1274         if (start < end)
1275                 return (0);
1276
1277         return (1);
1278 }