]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_acl.c
MFC r305422:
[FreeBSD/stable/10.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 (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
711                                 id = ap->id;
712                         } else {
713                                 id = -1;
714                         }
715                         append_entry(&p, NULL, ap->tag, name,
716                             ap->permset, id);
717                         count++;
718                 }
719         }
720
721
722         if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
723                 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
724                         prefix = "default:";
725                 else
726                         prefix = NULL;
727                 count = 0;
728                 for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
729                         if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
730                                 continue;
731                         r = archive_mstring_get_mbs_l(
732                             &ap->name, &name, &len, sc);
733                         if (r != 0)
734                                 return (-1);
735                         if (count > 0)
736                                 *p++ = separator;
737                         if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
738                                 id = ap->id;
739                         else
740                                 id = -1;
741                         append_entry(&p, prefix, ap->tag,
742                             name, ap->permset, id);
743                         count ++;
744                 }
745         }
746
747         *acl_text = acl->acl_text;
748         if (acl_text_len != NULL)
749                 *acl_text_len = strlen(acl->acl_text);
750         return (0);
751 }
752
753 static void
754 append_id(char **p, int id)
755 {
756         if (id < 0)
757                 id = 0;
758         if (id > 9)
759                 append_id(p, id / 10);
760         *(*p)++ = "0123456789"[id % 10];
761 }
762
763 static void
764 append_entry(char **p, const char *prefix, int tag,
765     const char *name, int perm, int id)
766 {
767         if (prefix != NULL) {
768                 strcpy(*p, prefix);
769                 *p += strlen(*p);
770         }
771         switch (tag) {
772         case ARCHIVE_ENTRY_ACL_USER_OBJ:
773                 name = NULL;
774                 id = -1;
775                 /* FALLTHROUGH */
776         case ARCHIVE_ENTRY_ACL_USER:
777                 strcpy(*p, "user");
778                 break;
779         case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
780                 name = NULL;
781                 id = -1;
782                 /* FALLTHROUGH */
783         case ARCHIVE_ENTRY_ACL_GROUP:
784                 strcpy(*p, "group");
785                 break;
786         case ARCHIVE_ENTRY_ACL_MASK:
787                 strcpy(*p, "mask");
788                 name = NULL;
789                 id = -1;
790                 break;
791         case ARCHIVE_ENTRY_ACL_OTHER:
792                 strcpy(*p, "other");
793                 name = NULL;
794                 id = -1;
795                 break;
796         }
797         *p += strlen(*p);
798         *(*p)++ = ':';
799         if (name != NULL) {
800                 strcpy(*p, name);
801                 *p += strlen(*p);
802         } else if (tag == ARCHIVE_ENTRY_ACL_USER
803             || tag == ARCHIVE_ENTRY_ACL_GROUP) {
804                 append_id(p, id);
805                 id = -1;
806         }
807         *(*p)++ = ':';
808         *(*p)++ = (perm & 0444) ? 'r' : '-';
809         *(*p)++ = (perm & 0222) ? 'w' : '-';
810         *(*p)++ = (perm & 0111) ? 'x' : '-';
811         if (id != -1) {
812                 *(*p)++ = ':';
813                 append_id(p, id);
814         }
815         **p = '\0';
816 }
817
818 /*
819  * Parse a textual ACL.  This automatically recognizes and supports
820  * extensions described above.  The 'type' argument is used to
821  * indicate the type that should be used for any entries not
822  * explicitly marked as "default:".
823  */
824 int
825 archive_acl_parse_w(struct archive_acl *acl,
826     const wchar_t *text, int default_type)
827 {
828         struct {
829                 const wchar_t *start;
830                 const wchar_t *end;
831         } field[4], name;
832
833         int fields, n;
834         int type, tag, permset, id;
835         wchar_t sep;
836
837         while (text != NULL  &&  *text != L'\0') {
838                 /*
839                  * Parse the fields out of the next entry,
840                  * advance 'text' to start of next entry.
841                  */
842                 fields = 0;
843                 do {
844                         const wchar_t *start, *end;
845                         next_field_w(&text, &start, &end, &sep);
846                         if (fields < 4) {
847                                 field[fields].start = start;
848                                 field[fields].end = end;
849                         }
850                         ++fields;
851                 } while (sep == L':');
852
853                 /* Set remaining fields to blank. */
854                 for (n = fields; n < 4; ++n)
855                         field[n].start = field[n].end = NULL;
856
857                 /* Check for a numeric ID in field 1 or 3. */
858                 id = -1;
859                 isint_w(field[1].start, field[1].end, &id);
860                 /* Field 3 is optional. */
861                 if (id == -1 && fields > 3)
862                         isint_w(field[3].start, field[3].end, &id);
863
864                 /*
865                  * Solaris extension:  "defaultuser::rwx" is the
866                  * default ACL corresponding to "user::rwx", etc.
867                  */
868                 if (field[0].end - field[0].start > 7
869                     && wmemcmp(field[0].start, L"default", 7) == 0) {
870                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
871                         field[0].start += 7;
872                 } else
873                         type = default_type;
874
875                 name.start = name.end = NULL;
876                 if (prefix_w(field[0].start, field[0].end, L"user")) {
877                         if (!ismode_w(field[2].start, field[2].end, &permset))
878                                 return (ARCHIVE_WARN);
879                         if (id != -1 || field[1].start < field[1].end) {
880                                 tag = ARCHIVE_ENTRY_ACL_USER;
881                                 name = field[1];
882                         } else
883                                 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
884                 } else if (prefix_w(field[0].start, field[0].end, L"group")) {
885                         if (!ismode_w(field[2].start, field[2].end, &permset))
886                                 return (ARCHIVE_WARN);
887                         if (id != -1 || field[1].start < field[1].end) {
888                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
889                                 name = field[1];
890                         } else
891                                 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
892                 } else if (prefix_w(field[0].start, field[0].end, L"other")) {
893                         if (fields == 2
894                             && field[1].start < field[1].end
895                             && ismode_w(field[1].start, field[1].end, &permset)) {
896                                 /* This is Solaris-style "other:rwx" */
897                         } else if (fields == 3
898                             && field[1].start == field[1].end
899                             && field[2].start < field[2].end
900                             && ismode_w(field[2].start, field[2].end, &permset)) {
901                                 /* This is FreeBSD-style "other::rwx" */
902                         } else
903                                 return (ARCHIVE_WARN);
904                         tag = ARCHIVE_ENTRY_ACL_OTHER;
905                 } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
906                         if (fields == 2
907                             && field[1].start < field[1].end
908                             && ismode_w(field[1].start, field[1].end, &permset)) {
909                                 /* This is Solaris-style "mask:rwx" */
910                         } else if (fields == 3
911                             && field[1].start == field[1].end
912                             && field[2].start < field[2].end
913                             && ismode_w(field[2].start, field[2].end, &permset)) {
914                                 /* This is FreeBSD-style "mask::rwx" */
915                         } else
916                                 return (ARCHIVE_WARN);
917                         tag = ARCHIVE_ENTRY_ACL_MASK;
918                 } else
919                         return (ARCHIVE_WARN);
920
921                 /* Add entry to the internal list. */
922                 archive_acl_add_entry_w_len(acl, type, permset,
923                     tag, id, name.start, name.end - name.start);
924         }
925         return (ARCHIVE_OK);
926 }
927
928 /*
929  * Parse a string to a positive decimal integer.  Returns true if
930  * the string is non-empty and consists only of decimal digits,
931  * false otherwise.
932  */
933 static int
934 isint_w(const wchar_t *start, const wchar_t *end, int *result)
935 {
936         int n = 0;
937         if (start >= end)
938                 return (0);
939         while (start < end) {
940                 if (*start < '0' || *start > '9')
941                         return (0);
942                 if (n > (INT_MAX / 10) ||
943                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
944                         n = INT_MAX;
945                 } else {
946                         n *= 10;
947                         n += *start - '0';
948                 }
949                 start++;
950         }
951         *result = n;
952         return (1);
953 }
954
955 /*
956  * Parse a string as a mode field.  Returns true if
957  * the string is non-empty and consists only of mode characters,
958  * false otherwise.
959  */
960 static int
961 ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
962 {
963         const wchar_t *p;
964
965         if (start >= end)
966                 return (0);
967         p = start;
968         *permset = 0;
969         while (p < end) {
970                 switch (*p++) {
971                 case 'r': case 'R':
972                         *permset |= ARCHIVE_ENTRY_ACL_READ;
973                         break;
974                 case 'w': case 'W':
975                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
976                         break;
977                 case 'x': case 'X':
978                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
979                         break;
980                 case '-':
981                         break;
982                 default:
983                         return (0);
984                 }
985         }
986         return (1);
987 }
988
989 /*
990  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
991  * to point to just after the separator.  *start points to the first
992  * character of the matched text and *end just after the last
993  * character of the matched identifier.  In particular *end - *start
994  * is the length of the field body, not including leading or trailing
995  * whitespace.
996  */
997 static void
998 next_field_w(const wchar_t **wp, const wchar_t **start,
999     const wchar_t **end, wchar_t *sep)
1000 {
1001         /* Skip leading whitespace to find start of field. */
1002         while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1003                 (*wp)++;
1004         }
1005         *start = *wp;
1006
1007         /* Scan for the separator. */
1008         while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1009             **wp != L'\n') {
1010                 (*wp)++;
1011         }
1012         *sep = **wp;
1013
1014         /* Trim trailing whitespace to locate end of field. */
1015         *end = *wp - 1;
1016         while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1017                 (*end)--;
1018         }
1019         (*end)++;
1020
1021         /* Adjust scanner location. */
1022         if (**wp != L'\0')
1023                 (*wp)++;
1024 }
1025
1026 /*
1027  * Return true if the characters [start...end) are a prefix of 'test'.
1028  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1029  */
1030 static int
1031 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
1032 {
1033         if (start == end)
1034                 return (0);
1035
1036         if (*start++ != *test++)
1037                 return (0);
1038
1039         while (start < end  &&  *start++ == *test++)
1040                 ;
1041
1042         if (start < end)
1043                 return (0);
1044
1045         return (1);
1046 }
1047
1048 /*
1049  * Parse a textual ACL.  This automatically recognizes and supports
1050  * extensions described above.  The 'type' argument is used to
1051  * indicate the type that should be used for any entries not
1052  * explicitly marked as "default:".
1053  */
1054 int
1055 archive_acl_parse_l(struct archive_acl *acl,
1056     const char *text, int default_type, struct archive_string_conv *sc)
1057 {
1058         struct {
1059                 const char *start;
1060                 const char *end;
1061         } field[4], name;
1062
1063         int fields, n, r, ret = ARCHIVE_OK;
1064         int type, tag, permset, id;
1065         char sep;
1066
1067         while (text != NULL  &&  *text != '\0') {
1068                 /*
1069                  * Parse the fields out of the next entry,
1070                  * advance 'text' to start of next entry.
1071                  */
1072                 fields = 0;
1073                 do {
1074                         const char *start, *end;
1075                         next_field(&text, &start, &end, &sep);
1076                         if (fields < 4) {
1077                                 field[fields].start = start;
1078                                 field[fields].end = end;
1079                         }
1080                         ++fields;
1081                 } while (sep == ':');
1082
1083                 /* Set remaining fields to blank. */
1084                 for (n = fields; n < 4; ++n)
1085                         field[n].start = field[n].end = NULL;
1086
1087                 /* Check for a numeric ID in field 1 or 3. */
1088                 id = -1;
1089                 isint(field[1].start, field[1].end, &id);
1090                 /* Field 3 is optional. */
1091                 if (id == -1 && fields > 3)
1092                         isint(field[3].start, field[3].end, &id);
1093
1094                 /*
1095                  * Solaris extension:  "defaultuser::rwx" is the
1096                  * default ACL corresponding to "user::rwx", etc.
1097                  */
1098                 if (field[0].end - field[0].start > 7
1099                     && memcmp(field[0].start, "default", 7) == 0) {
1100                         type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1101                         field[0].start += 7;
1102                 } else
1103                         type = default_type;
1104
1105                 name.start = name.end = NULL;
1106                 if (prefix_c(field[0].start, field[0].end, "user")) {
1107                         if (!ismode(field[2].start, field[2].end, &permset))
1108                                 return (ARCHIVE_WARN);
1109                         if (id != -1 || field[1].start < field[1].end) {
1110                                 tag = ARCHIVE_ENTRY_ACL_USER;
1111                                 name = field[1];
1112                         } else
1113                                 tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1114                 } else if (prefix_c(field[0].start, field[0].end, "group")) {
1115                         if (!ismode(field[2].start, field[2].end, &permset))
1116                                 return (ARCHIVE_WARN);
1117                         if (id != -1 || field[1].start < field[1].end) {
1118                                 tag = ARCHIVE_ENTRY_ACL_GROUP;
1119                                 name = field[1];
1120                         } else
1121                                 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1122                 } else if (prefix_c(field[0].start, field[0].end, "other")) {
1123                         if (fields == 2
1124                             && field[1].start < field[1].end
1125                             && ismode(field[1].start, field[1].end, &permset)) {
1126                                 /* This is Solaris-style "other:rwx" */
1127                         } else if (fields == 3
1128                             && field[1].start == field[1].end
1129                             && field[2].start < field[2].end
1130                             && ismode(field[2].start, field[2].end, &permset)) {
1131                                 /* This is FreeBSD-style "other::rwx" */
1132                         } else
1133                                 return (ARCHIVE_WARN);
1134                         tag = ARCHIVE_ENTRY_ACL_OTHER;
1135                 } else if (prefix_c(field[0].start, field[0].end, "mask")) {
1136                         if (fields == 2
1137                             && field[1].start < field[1].end
1138                             && ismode(field[1].start, field[1].end, &permset)) {
1139                                 /* This is Solaris-style "mask:rwx" */
1140                         } else if (fields == 3
1141                             && field[1].start == field[1].end
1142                             && field[2].start < field[2].end
1143                             && ismode(field[2].start, field[2].end, &permset)) {
1144                                 /* This is FreeBSD-style "mask::rwx" */
1145                         } else
1146                                 return (ARCHIVE_WARN);
1147                         tag = ARCHIVE_ENTRY_ACL_MASK;
1148                 } else
1149                         return (ARCHIVE_WARN);
1150
1151                 /* Add entry to the internal list. */
1152                 r = archive_acl_add_entry_len_l(acl, type, permset,
1153                     tag, id, name.start, name.end - name.start, sc);
1154                 if (r < ARCHIVE_WARN)
1155                         return (r);
1156                 if (r != ARCHIVE_OK)
1157                         ret = ARCHIVE_WARN;
1158         }
1159         return (ret);
1160 }
1161
1162 /*
1163  * Parse a string to a positive decimal integer.  Returns true if
1164  * the string is non-empty and consists only of decimal digits,
1165  * false otherwise.
1166  */
1167 static int
1168 isint(const char *start, const char *end, int *result)
1169 {
1170         int n = 0;
1171         if (start >= end)
1172                 return (0);
1173         while (start < end) {
1174                 if (*start < '0' || *start > '9')
1175                         return (0);
1176                 if (n > (INT_MAX / 10) ||
1177                     (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
1178                         n = INT_MAX;
1179                 } else {
1180                         n *= 10;
1181                         n += *start - '0';
1182                 }
1183                 start++;
1184         }
1185         *result = n;
1186         return (1);
1187 }
1188
1189 /*
1190  * Parse a string as a mode field.  Returns true if
1191  * the string is non-empty and consists only of mode characters,
1192  * false otherwise.
1193  */
1194 static int
1195 ismode(const char *start, const char *end, int *permset)
1196 {
1197         const char *p;
1198
1199         if (start >= end)
1200                 return (0);
1201         p = start;
1202         *permset = 0;
1203         while (p < end) {
1204                 switch (*p++) {
1205                 case 'r': case 'R':
1206                         *permset |= ARCHIVE_ENTRY_ACL_READ;
1207                         break;
1208                 case 'w': case 'W':
1209                         *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1210                         break;
1211                 case 'x': case 'X':
1212                         *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1213                         break;
1214                 case '-':
1215                         break;
1216                 default:
1217                         return (0);
1218                 }
1219         }
1220         return (1);
1221 }
1222
1223 /*
1224  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1225  * to point to just after the separator.  *start points to the first
1226  * character of the matched text and *end just after the last
1227  * character of the matched identifier.  In particular *end - *start
1228  * is the length of the field body, not including leading or trailing
1229  * whitespace.
1230  */
1231 static void
1232 next_field(const char **p, const char **start,
1233     const char **end, char *sep)
1234 {
1235         /* Skip leading whitespace to find start of field. */
1236         while (**p == ' ' || **p == '\t' || **p == '\n') {
1237                 (*p)++;
1238         }
1239         *start = *p;
1240
1241         /* Scan for the separator. */
1242         while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
1243                 (*p)++;
1244         }
1245         *sep = **p;
1246
1247         /* Trim trailing whitespace to locate end of field. */
1248         *end = *p - 1;
1249         while (**end == ' ' || **end == '\t' || **end == '\n') {
1250                 (*end)--;
1251         }
1252         (*end)++;
1253
1254         /* Adjust scanner location. */
1255         if (**p != '\0')
1256                 (*p)++;
1257 }
1258
1259 /*
1260  * Return true if the characters [start...end) are a prefix of 'test'.
1261  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1262  */
1263 static int
1264 prefix_c(const char *start, const char *end, const char *test)
1265 {
1266         if (start == end)
1267                 return (0);
1268
1269         if (*start++ != *test++)
1270                 return (0);
1271
1272         while (start < end  &&  *start++ == *test++)
1273                 ;
1274
1275         if (start < end)
1276                 return (0);
1277
1278         return (1);
1279 }