2 * Copyright (c) 2003-2010 Tim Kientzle
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
39 #include "archive_acl_private.h"
40 #include "archive_entry.h"
41 #include "archive_private.h"
44 #define max(a, b) ((a)>(b)?(a):(b))
47 /* Good enough for simple equality testing, but not for sorting. */
48 #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
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
,
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
,
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
);
78 archive_acl_clear(struct archive_acl
*acl
)
80 struct archive_acl_entry
*ap
;
82 while (acl
->acl_head
!= NULL
) {
83 ap
= acl
->acl_head
->next
;
84 archive_mstring_clean(&acl
->acl_head
->name
);
88 if (acl
->acl_text_w
!= NULL
) {
89 free(acl
->acl_text_w
);
90 acl
->acl_text_w
= NULL
;
92 if (acl
->acl_text
!= NULL
) {
97 acl
->acl_state
= 0; /* Not counting. */
101 archive_acl_copy(struct archive_acl
*dest
, struct archive_acl
*src
)
103 struct archive_acl_entry
*ap
, *ap2
;
105 archive_acl_clear(dest
);
107 dest
->mode
= src
->mode
;
110 ap2
= acl_new_entry(dest
,
111 ap
->type
, ap
->permset
, ap
->tag
, ap
->id
);
113 archive_mstring_copy(&ap2
->name
, &ap
->name
);
119 archive_acl_add_entry(struct archive_acl
*acl
,
120 int type
, int permset
, int tag
, int id
, const char *name
)
122 struct archive_acl_entry
*ap
;
124 if (acl_special(acl
, type
, permset
, tag
) == 0)
126 ap
= acl_new_entry(acl
, type
, permset
, tag
, id
);
129 return ARCHIVE_FAILED
;
131 if (name
!= NULL
&& *name
!= '\0')
132 archive_mstring_copy_mbs(&ap
->name
, name
);
134 archive_mstring_clean(&ap
->name
);
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
)
142 struct archive_acl_entry
*ap
;
144 if (acl_special(acl
, type
, permset
, tag
) == 0)
146 ap
= acl_new_entry(acl
, type
, permset
, tag
, id
);
149 return ARCHIVE_FAILED
;
151 if (name
!= NULL
&& *name
!= L
'\0' && len
> 0)
152 archive_mstring_copy_wcs_len(&ap
->name
, name
, len
);
154 archive_mstring_clean(&ap
->name
);
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
)
163 struct archive_acl_entry
*ap
;
166 if (acl_special(acl
, type
, permset
, tag
) == 0)
168 ap
= acl_new_entry(acl
, type
, permset
, tag
, id
);
171 return ARCHIVE_FAILED
;
173 if (name
!= NULL
&& *name
!= '\0' && len
> 0) {
174 r
= archive_mstring_copy_mbs_len_l(&ap
->name
, name
, len
, sc
);
177 archive_mstring_clean(&ap
->name
);
181 else if (errno
== ENOMEM
)
182 return (ARCHIVE_FATAL
);
184 return (ARCHIVE_WARN
);
188 * If this ACL entry is part of the standard POSIX permissions set,
189 * store the permissions in the stat structure and return zero.
192 acl_special(struct archive_acl
*acl
, int type
, int permset
, int tag
)
194 if (type
== ARCHIVE_ENTRY_ACL_TYPE_ACCESS
195 && ((permset
& ~007) == 0)) {
197 case ARCHIVE_ENTRY_ACL_USER_OBJ
:
199 acl
->mode
|= (permset
& 7) << 6;
201 case ARCHIVE_ENTRY_ACL_GROUP_OBJ
:
203 acl
->mode
|= (permset
& 7) << 3;
205 case ARCHIVE_ENTRY_ACL_OTHER
:
207 acl
->mode
|= permset
& 7;
215 * Allocate and populate a new ACL entry with everything but the
218 static struct archive_acl_entry
*
219 acl_new_entry(struct archive_acl
*acl
,
220 int type
, int permset
, int tag
, int id
)
222 struct archive_acl_entry
*ap
, *aq
;
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
) {
232 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
233 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4
)) {
236 } else if (type
& ARCHIVE_ENTRY_ACL_TYPE_POSIX1E
) {
237 if (acl
->acl_types
& ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E
) {
240 if (permset
& ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E
) {
247 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
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 */
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
) {
262 case ARCHIVE_ENTRY_ACL_EVERYONE
:
263 /* Tags valid only in NFS4. */
264 if (type
& ~ARCHIVE_ENTRY_ACL_TYPE_NFS4
) {
269 /* No other values are valid. */
273 if (acl
->acl_text_w
!= NULL
) {
274 free(acl
->acl_text_w
);
275 acl
->acl_text_w
= NULL
;
277 if (acl
->acl_text
!= NULL
) {
279 acl
->acl_text
= NULL
;
282 /* If there's a matching entry already in the list, overwrite it. */
286 if (ap
->type
== type
&& ap
->tag
== tag
&& ap
->id
== id
) {
287 ap
->permset
= permset
;
294 /* Add a new entry to the end of the list. */
295 ap
= (struct archive_acl_entry
*)malloc(sizeof(*ap
));
298 memset(ap
, 0, sizeof(*ap
));
306 ap
->permset
= permset
;
307 acl
->acl_types
|= type
;
312 * Return a count of entries matching "want_type".
315 archive_acl_count(struct archive_acl
*acl
, int want_type
)
318 struct archive_acl_entry
*ap
;
323 if ((ap
->type
& want_type
) != 0)
328 if (count
> 0 && ((want_type
& ARCHIVE_ENTRY_ACL_TYPE_ACCESS
) != 0))
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.
339 archive_acl_reset(struct archive_acl
*acl
, int want_type
)
343 count
= archive_acl_count(acl
, want_type
);
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.)
350 if ((want_type
& ARCHIVE_ENTRY_ACL_TYPE_ACCESS
) != 0)
356 acl
->acl_state
= ARCHIVE_ENTRY_ACL_USER_OBJ
;
359 acl
->acl_p
= acl
->acl_head
;
365 * Return the next ACL entry in the list. Fake entries for the
366 * standard permissions and include them in the returned list.
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
)
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).
380 if (acl
->acl_state
== 0)
381 return (ARCHIVE_WARN
);
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
;
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
;
398 case ARCHIVE_ENTRY_ACL_OTHER
:
399 *permset
= acl
->mode
& 7;
400 *type
= ARCHIVE_ENTRY_ACL_TYPE_ACCESS
;
401 *tag
= ARCHIVE_ENTRY_ACL_OTHER
;
403 acl
->acl_p
= acl
->acl_head
;
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
) {
419 return (ARCHIVE_EOF
); /* End of ACL entries. */
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) {
427 return (ARCHIVE_FATAL
);
430 acl
->acl_p
= acl
->acl_p
->next
;
435 * Generate a text version of the ACL. The flags parameter controls
436 * the style of the generated ACL.
439 archive_acl_text_w(struct archive
*a
, struct archive_acl
*acl
, int flags
)
443 const wchar_t *wname
;
444 const wchar_t *prefix
;
446 struct archive_acl_entry
*ap
;
450 if (acl
->acl_text_w
!= NULL
) {
451 free (acl
->acl_text_w
);
452 acl
->acl_text_w
= NULL
;
460 if ((ap
->type
& flags
) != 0) {
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
)
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 */
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" */
492 /* Now, allocate the string and actually populate it. */
493 wp
= acl
->acl_text_w
= (wchar_t *)malloc(length
* sizeof(wchar_t));
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);
501 append_entry_w(&wp
, NULL
, ARCHIVE_ENTRY_ACL_GROUP_OBJ
, NULL
,
502 acl
->mode
& 0070, -1);
504 append_entry_w(&wp
, NULL
, ARCHIVE_ENTRY_ACL_OTHER
, NULL
,
505 acl
->mode
& 0007, -1);
510 if ((ap
->type
& ARCHIVE_ENTRY_ACL_TYPE_ACCESS
) != 0) {
511 r
= archive_mstring_get_wcs(a
, &ap
->name
, &wname
);
514 if (flags
& ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
)
518 append_entry_w(&wp
, NULL
, ap
->tag
, wname
,
521 } else if (r
< 0 && errno
== ENOMEM
)
529 if ((flags
& ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
) != 0) {
530 if (flags
& ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
)
531 prefix
= L
"default:";
537 if ((ap
->type
& ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
) != 0) {
538 r
= archive_mstring_get_wcs(a
, &ap
->name
, &wname
);
542 if (flags
& ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
)
546 append_entry_w(&wp
, prefix
, ap
->tag
,
547 wname
, ap
->permset
, id
);
549 } else if (r
< 0 && errno
== ENOMEM
)
556 return (acl
->acl_text_w
);
561 append_id_w(wchar_t **wp
, int id
)
566 append_id_w(wp
, id
/ 10);
567 *(*wp
)++ = L
"0123456789"[id
% 10];
571 append_entry_w(wchar_t **wp
, const wchar_t *prefix
, int tag
,
572 const wchar_t *wname
, int perm
, int id
)
574 if (prefix
!= NULL
) {
579 case ARCHIVE_ENTRY_ACL_USER_OBJ
:
583 case ARCHIVE_ENTRY_ACL_USER
:
584 wcscpy(*wp
, L
"user");
586 case ARCHIVE_ENTRY_ACL_GROUP_OBJ
:
590 case ARCHIVE_ENTRY_ACL_GROUP
:
591 wcscpy(*wp
, L
"group");
593 case ARCHIVE_ENTRY_ACL_MASK
:
594 wcscpy(*wp
, L
"mask");
598 case ARCHIVE_ENTRY_ACL_OTHER
:
599 wcscpy(*wp
, L
"other");
609 } else if (tag
== ARCHIVE_ENTRY_ACL_USER
610 || tag
== ARCHIVE_ENTRY_ACL_GROUP
) {
615 *(*wp
)++ = (perm
& 0444) ? L
'r' : L
'-';
616 *(*wp
)++ = (perm
& 0222) ? L
'w' : L
'-';
617 *(*wp
)++ = (perm
& 0111) ? L
'x' : L
'-';
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
)
635 struct archive_acl_entry
*ap
;
640 if (acl
->acl_text
!= NULL
) {
641 free (acl
->acl_text
);
642 acl
->acl_text
= NULL
;
646 if (acl_text_len
!= NULL
)
653 if ((ap
->type
& flags
) != 0) {
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
);
664 if (len
> 0 && name
!= NULL
)
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 */
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" */
686 /* Now, allocate the string and actually populate it. */
687 p
= acl
->acl_text
= (char *)malloc(length
);
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);
695 append_entry(&p
, NULL
, ARCHIVE_ENTRY_ACL_GROUP_OBJ
, NULL
,
696 acl
->mode
& 0070, -1);
698 append_entry(&p
, NULL
, ARCHIVE_ENTRY_ACL_OTHER
, NULL
,
699 acl
->mode
& 0007, -1);
702 for (ap
= acl
->acl_head
; ap
!= NULL
; ap
= ap
->next
) {
703 if ((ap
->type
& ARCHIVE_ENTRY_ACL_TYPE_ACCESS
) == 0)
705 r
= archive_mstring_get_mbs_l(
706 &ap
->name
, &name
, &len
, sc
);
710 if (flags
& ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
)
714 append_entry(&p
, NULL
, ap
->tag
, name
,
721 if ((flags
& ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
) != 0) {
722 if (flags
& ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
)
727 for (ap
= acl
->acl_head
; ap
!= NULL
; ap
= ap
->next
) {
728 if ((ap
->type
& ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
) == 0)
730 r
= archive_mstring_get_mbs_l(
731 &ap
->name
, &name
, &len
, sc
);
736 if (flags
& ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
)
740 append_entry(&p
, prefix
, ap
->tag
,
741 name
, ap
->permset
, id
);
746 *acl_text
= acl
->acl_text
;
747 if (acl_text_len
!= NULL
)
748 *acl_text_len
= strlen(acl
->acl_text
);
753 append_id(char **p
, int id
)
758 append_id(p
, id
/ 10);
759 *(*p
)++ = "0123456789"[id
% 10];
763 append_entry(char **p
, const char *prefix
, int tag
,
764 const char *name
, int perm
, int id
)
766 if (prefix
!= NULL
) {
771 case ARCHIVE_ENTRY_ACL_USER_OBJ
:
775 case ARCHIVE_ENTRY_ACL_USER
:
778 case ARCHIVE_ENTRY_ACL_GROUP_OBJ
:
782 case ARCHIVE_ENTRY_ACL_GROUP
:
785 case ARCHIVE_ENTRY_ACL_MASK
:
790 case ARCHIVE_ENTRY_ACL_OTHER
:
801 } else if (tag
== ARCHIVE_ENTRY_ACL_USER
802 || tag
== ARCHIVE_ENTRY_ACL_GROUP
) {
807 *(*p
)++ = (perm
& 0444) ? 'r' : '-';
808 *(*p
)++ = (perm
& 0222) ? 'w' : '-';
809 *(*p
)++ = (perm
& 0111) ? 'x' : '-';
818 * Parse a textual ACL. This automatically recognizes and supports
819 * extensions described above. The 'type' argument is used to
820 * indicate the type that should be used for any entries not
821 * explicitly marked as "default:".
824 archive_acl_parse_w(struct archive_acl
*acl
,
825 const wchar_t *text
, int default_type
)
828 const wchar_t *start
;
833 int type
, tag
, permset
, id
;
836 while (text
!= NULL
&& *text
!= L
'\0') {
838 * Parse the fields out of the next entry,
839 * advance 'text' to start of next entry.
843 const wchar_t *start
, *end
;
844 next_field_w(&text
, &start
, &end
, &sep
);
846 field
[fields
].start
= start
;
847 field
[fields
].end
= end
;
850 } while (sep
== L
':');
852 /* Set remaining fields to blank. */
853 for (n
= fields
; n
< 4; ++n
)
854 field
[n
].start
= field
[n
].end
= NULL
;
856 /* Check for a numeric ID in field 1 or 3. */
858 isint_w(field
[1].start
, field
[1].end
, &id
);
859 /* Field 3 is optional. */
860 if (id
== -1 && fields
> 3)
861 isint_w(field
[3].start
, field
[3].end
, &id
);
864 * Solaris extension: "defaultuser::rwx" is the
865 * default ACL corresponding to "user::rwx", etc.
867 if (field
[0].end
- field
[0].start
> 7
868 && wmemcmp(field
[0].start
, L
"default", 7) == 0) {
869 type
= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
;
874 name
.start
= name
.end
= NULL
;
875 if (prefix_w(field
[0].start
, field
[0].end
, L
"user")) {
876 if (!ismode_w(field
[2].start
, field
[2].end
, &permset
))
877 return (ARCHIVE_WARN
);
878 if (id
!= -1 || field
[1].start
< field
[1].end
) {
879 tag
= ARCHIVE_ENTRY_ACL_USER
;
882 tag
= ARCHIVE_ENTRY_ACL_USER_OBJ
;
883 } else if (prefix_w(field
[0].start
, field
[0].end
, L
"group")) {
884 if (!ismode_w(field
[2].start
, field
[2].end
, &permset
))
885 return (ARCHIVE_WARN
);
886 if (id
!= -1 || field
[1].start
< field
[1].end
) {
887 tag
= ARCHIVE_ENTRY_ACL_GROUP
;
890 tag
= ARCHIVE_ENTRY_ACL_GROUP_OBJ
;
891 } else if (prefix_w(field
[0].start
, field
[0].end
, L
"other")) {
893 && field
[1].start
< field
[1].end
894 && ismode_w(field
[1].start
, field
[1].end
, &permset
)) {
895 /* This is Solaris-style "other:rwx" */
896 } else if (fields
== 3
897 && field
[1].start
== field
[1].end
898 && field
[2].start
< field
[2].end
899 && ismode_w(field
[2].start
, field
[2].end
, &permset
)) {
900 /* This is FreeBSD-style "other::rwx" */
902 return (ARCHIVE_WARN
);
903 tag
= ARCHIVE_ENTRY_ACL_OTHER
;
904 } else if (prefix_w(field
[0].start
, field
[0].end
, L
"mask")) {
906 && field
[1].start
< field
[1].end
907 && ismode_w(field
[1].start
, field
[1].end
, &permset
)) {
908 /* This is Solaris-style "mask:rwx" */
909 } else if (fields
== 3
910 && field
[1].start
== field
[1].end
911 && field
[2].start
< field
[2].end
912 && ismode_w(field
[2].start
, field
[2].end
, &permset
)) {
913 /* This is FreeBSD-style "mask::rwx" */
915 return (ARCHIVE_WARN
);
916 tag
= ARCHIVE_ENTRY_ACL_MASK
;
918 return (ARCHIVE_WARN
);
920 /* Add entry to the internal list. */
921 archive_acl_add_entry_w_len(acl
, type
, permset
,
922 tag
, id
, name
.start
, name
.end
- name
.start
);
928 * Parse a string to a positive decimal integer. Returns true if
929 * the string is non-empty and consists only of decimal digits,
933 isint_w(const wchar_t *start
, const wchar_t *end
, int *result
)
938 while (start
< end
) {
939 if (*start
< '0' || *start
> '9')
941 if (n
> (INT_MAX
/ 10) ||
942 (n
== INT_MAX
/ 10 && (*start
- '0') > INT_MAX
% 10)) {
955 * Parse a string as a mode field. Returns true if
956 * the string is non-empty and consists only of mode characters,
960 ismode_w(const wchar_t *start
, const wchar_t *end
, int *permset
)
971 *permset
|= ARCHIVE_ENTRY_ACL_READ
;
974 *permset
|= ARCHIVE_ENTRY_ACL_WRITE
;
977 *permset
|= ARCHIVE_ENTRY_ACL_EXECUTE
;
989 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
990 * to point to just after the separator. *start points to the first
991 * character of the matched text and *end just after the last
992 * character of the matched identifier. In particular *end - *start
993 * is the length of the field body, not including leading or trailing
997 next_field_w(const wchar_t **wp
, const wchar_t **start
,
998 const wchar_t **end
, wchar_t *sep
)
1000 /* Skip leading whitespace to find start of field. */
1001 while (**wp
== L
' ' || **wp
== L
'\t' || **wp
== L
'\n') {
1006 /* Scan for the separator. */
1007 while (**wp
!= L
'\0' && **wp
!= L
',' && **wp
!= L
':' &&
1013 /* Trim trailing whitespace to locate end of field. */
1015 while (**end
== L
' ' || **end
== L
'\t' || **end
== L
'\n') {
1020 /* Adjust scanner location. */
1026 * Return true if the characters [start...end) are a prefix of 'test'.
1027 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1030 prefix_w(const wchar_t *start
, const wchar_t *end
, const wchar_t *test
)
1035 if (*start
++ != *test
++)
1038 while (start
< end
&& *start
++ == *test
++)
1048 * Parse a textual ACL. This automatically recognizes and supports
1049 * extensions described above. The 'type' argument is used to
1050 * indicate the type that should be used for any entries not
1051 * explicitly marked as "default:".
1054 archive_acl_parse_l(struct archive_acl
*acl
,
1055 const char *text
, int default_type
, struct archive_string_conv
*sc
)
1062 int fields
, n
, r
, ret
= ARCHIVE_OK
;
1063 int type
, tag
, permset
, id
;
1066 while (text
!= NULL
&& *text
!= '\0') {
1068 * Parse the fields out of the next entry,
1069 * advance 'text' to start of next entry.
1073 const char *start
, *end
;
1074 next_field(&text
, &start
, &end
, &sep
);
1076 field
[fields
].start
= start
;
1077 field
[fields
].end
= end
;
1080 } while (sep
== ':');
1082 /* Set remaining fields to blank. */
1083 for (n
= fields
; n
< 4; ++n
)
1084 field
[n
].start
= field
[n
].end
= NULL
;
1086 /* Check for a numeric ID in field 1 or 3. */
1088 isint(field
[1].start
, field
[1].end
, &id
);
1089 /* Field 3 is optional. */
1090 if (id
== -1 && fields
> 3)
1091 isint(field
[3].start
, field
[3].end
, &id
);
1094 * Solaris extension: "defaultuser::rwx" is the
1095 * default ACL corresponding to "user::rwx", etc.
1097 if (field
[0].end
- field
[0].start
> 7
1098 && memcmp(field
[0].start
, "default", 7) == 0) {
1099 type
= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
;
1100 field
[0].start
+= 7;
1102 type
= default_type
;
1104 name
.start
= name
.end
= NULL
;
1105 if (prefix_c(field
[0].start
, field
[0].end
, "user")) {
1106 if (!ismode(field
[2].start
, field
[2].end
, &permset
))
1107 return (ARCHIVE_WARN
);
1108 if (id
!= -1 || field
[1].start
< field
[1].end
) {
1109 tag
= ARCHIVE_ENTRY_ACL_USER
;
1112 tag
= ARCHIVE_ENTRY_ACL_USER_OBJ
;
1113 } else if (prefix_c(field
[0].start
, field
[0].end
, "group")) {
1114 if (!ismode(field
[2].start
, field
[2].end
, &permset
))
1115 return (ARCHIVE_WARN
);
1116 if (id
!= -1 || field
[1].start
< field
[1].end
) {
1117 tag
= ARCHIVE_ENTRY_ACL_GROUP
;
1120 tag
= ARCHIVE_ENTRY_ACL_GROUP_OBJ
;
1121 } else if (prefix_c(field
[0].start
, field
[0].end
, "other")) {
1123 && field
[1].start
< field
[1].end
1124 && ismode(field
[1].start
, field
[1].end
, &permset
)) {
1125 /* This is Solaris-style "other:rwx" */
1126 } else if (fields
== 3
1127 && field
[1].start
== field
[1].end
1128 && field
[2].start
< field
[2].end
1129 && ismode(field
[2].start
, field
[2].end
, &permset
)) {
1130 /* This is FreeBSD-style "other::rwx" */
1132 return (ARCHIVE_WARN
);
1133 tag
= ARCHIVE_ENTRY_ACL_OTHER
;
1134 } else if (prefix_c(field
[0].start
, field
[0].end
, "mask")) {
1136 && field
[1].start
< field
[1].end
1137 && ismode(field
[1].start
, field
[1].end
, &permset
)) {
1138 /* This is Solaris-style "mask:rwx" */
1139 } else if (fields
== 3
1140 && field
[1].start
== field
[1].end
1141 && field
[2].start
< field
[2].end
1142 && ismode(field
[2].start
, field
[2].end
, &permset
)) {
1143 /* This is FreeBSD-style "mask::rwx" */
1145 return (ARCHIVE_WARN
);
1146 tag
= ARCHIVE_ENTRY_ACL_MASK
;
1148 return (ARCHIVE_WARN
);
1150 /* Add entry to the internal list. */
1151 r
= archive_acl_add_entry_len_l(acl
, type
, permset
,
1152 tag
, id
, name
.start
, name
.end
- name
.start
, sc
);
1153 if (r
< ARCHIVE_WARN
)
1155 if (r
!= ARCHIVE_OK
)
1162 * Parse a string to a positive decimal integer. Returns true if
1163 * the string is non-empty and consists only of decimal digits,
1167 isint(const char *start
, const char *end
, int *result
)
1172 while (start
< end
) {
1173 if (*start
< '0' || *start
> '9')
1175 if (n
> (INT_MAX
/ 10) ||
1176 (n
== INT_MAX
/ 10 && (*start
- '0') > INT_MAX
% 10)) {
1189 * Parse a string as a mode field. Returns true if
1190 * the string is non-empty and consists only of mode characters,
1194 ismode(const char *start
, const char *end
, int *permset
)
1205 *permset
|= ARCHIVE_ENTRY_ACL_READ
;
1208 *permset
|= ARCHIVE_ENTRY_ACL_WRITE
;
1211 *permset
|= ARCHIVE_ENTRY_ACL_EXECUTE
;
1223 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
1224 * to point to just after the separator. *start points to the first
1225 * character of the matched text and *end just after the last
1226 * character of the matched identifier. In particular *end - *start
1227 * is the length of the field body, not including leading or trailing
1231 next_field(const char **p
, const char **start
,
1232 const char **end
, char *sep
)
1234 /* Skip leading whitespace to find start of field. */
1235 while (**p
== ' ' || **p
== '\t' || **p
== '\n') {
1240 /* Scan for the separator. */
1241 while (**p
!= '\0' && **p
!= ',' && **p
!= ':' && **p
!= '\n') {
1246 /* Trim trailing whitespace to locate end of field. */
1248 while (**end
== ' ' || **end
== '\t' || **end
== '\n') {
1253 /* Adjust scanner location. */
1259 * Return true if the characters [start...end) are a prefix of 'test'.
1260 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
1263 prefix_c(const char *start
, const char *end
, const char *test
)
1268 if (*start
++ != *test
++)
1271 while (start
< end
&& *start
++ == *test
++)