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