From: Tim Kientzle Date: Sat, 20 Nov 2010 07:17:02 +0000 (-0500) Subject: Move the abstract ACL manipulations into a separate source file. X-Git-Tag: v3.0.0a~847 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ab9806b4b7388dec10992d33c706c9db27bbf513;p=thirdparty%2Flibarchive.git Move the abstract ACL manipulations into a separate source file. SVN-Revision: 2793 --- diff --git a/Makefile.am b/Makefile.am index 9e7c0f252..5bbd14efb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -91,6 +91,7 @@ distclean-local: include_HEADERS= libarchive/archive.h libarchive/archive_entry.h libarchive_la_SOURCES= \ + libarchive/archive_acl_private.h \ libarchive/archive_check_magic.c \ libarchive/archive_crc32.h \ libarchive/archive_endian.h \ diff --git a/configure.ac b/configure.ac index 865a7cd30..92e2c0236 100644 --- a/configure.ac +++ b/configure.ac @@ -92,7 +92,7 @@ AC_SUBST(PLATFORMCPPFLAGS) AC_PROG_CC AM_PROG_CC_C_O AC_USE_SYSTEM_EXTENSIONS -AC_LIBTOOL_WIN32_DLL +#AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL AC_CHECK_TOOL([STRIP],[strip]) diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index f0a924875..c50ac9b01 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -13,6 +13,7 @@ SET(include_HEADERS # Sources and private headers SET(libarchive_SOURCES + archive_acl.c archive_check_magic.c archive_endian.h archive_entry.c diff --git a/libarchive/archive_acl.c b/libarchive/archive_acl.c new file mode 100644 index 000000000..d4195480b --- /dev/null +++ b/libarchive/archive_acl.c @@ -0,0 +1,732 @@ +/*- + * Copyright (c) 2003-2010 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_LIMITS_H +#include +#endif + +#include "archive_acl_private.h" +#include "archive_entry.h" +#include "archive_private.h" + +#undef max +#define max(a, b) ((a)>(b)?(a):(b)) + +static int acl_special(struct archive_acl *acl, + int type, int permset, int tag); +static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, + int type, int permset, int tag, int id); +static int isint_w(const wchar_t *start, const wchar_t *end, int *result); +static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); +static void next_field_w(const wchar_t **wp, const wchar_t **start, + const wchar_t **end, wchar_t *sep); +static int prefix_w(const wchar_t *start, const wchar_t *end, + const wchar_t *test); +static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, + const wchar_t *wname, int perm, int id); +static void append_id_w(wchar_t **wp, int id); + +void +archive_acl_clear(struct archive_acl *acl) +{ + struct archive_acl_entry *ap; + + while (acl->acl_head != NULL) { + ap = acl->acl_head->next; + archive_mstring_clean(&acl->acl_head->name); + free(acl->acl_head); + acl->acl_head = ap; + } + if (acl->acl_text_w != NULL) { + free(acl->acl_text_w); + acl->acl_text_w = NULL; + } + acl->acl_p = NULL; + acl->acl_state = 0; /* Not counting. */ +} + +void +archive_acl_copy(struct archive_acl *dest, struct archive_acl *src) +{ + struct archive_acl_entry *ap, *ap2; + + archive_acl_clear(dest); + + dest->mode = src->mode; + ap = src->acl_head; + while (ap != NULL) { + ap2 = acl_new_entry(dest, + ap->type, ap->permset, ap->tag, ap->id); + if (ap2 != NULL) + archive_mstring_copy(&ap2->name, &ap->name); + ap = ap->next; + } +} + +void +archive_acl_add_entry(struct archive_acl *acl, + int type, int permset, int tag, int id, const char *name) +{ + struct archive_acl_entry *ap; + + if (acl_special(acl, type, permset, tag) == 0) + return; + ap = acl_new_entry(acl, type, permset, tag, id); + if (ap == NULL) { + /* XXX Error XXX */ + return; + } + if (name != NULL && *name != '\0') + archive_mstring_copy_mbs(&ap->name, name); + else + archive_mstring_clean(&ap->name); +} + +void +archive_acl_add_entry_w_len(struct archive_acl *acl, + int type, int permset, int tag, int id, const wchar_t *name, size_t len) +{ + struct archive_acl_entry *ap; + + if (acl_special(acl, type, permset, tag) == 0) + return; + ap = acl_new_entry(acl, type, permset, tag, id); + if (ap == NULL) { + /* XXX Error XXX */ + return; + } + if (name != NULL && *name != L'\0' && len > 0) + archive_mstring_copy_wcs_len(&ap->name, name, len); + else + archive_mstring_clean(&ap->name); +} + +/* + * If this ACL entry is part of the standard POSIX permissions set, + * store the permissions in the stat structure and return zero. + */ +static int +acl_special(struct archive_acl *acl, int type, int permset, int tag) +{ + if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { + switch (tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + acl->mode &= ~0700; + acl->mode |= (permset & 7) << 6; + return (0); + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + acl->mode &= ~0070; + acl->mode |= (permset & 7) << 3; + return (0); + case ARCHIVE_ENTRY_ACL_OTHER: + acl->mode &= ~0007; + acl->mode |= permset & 7; + return (0); + } + } + return (1); +} + +/* + * Allocate and populate a new ACL entry with everything but the + * name. + */ +static struct archive_acl_entry * +acl_new_entry(struct archive_acl *acl, + int type, int permset, int tag, int id) +{ + struct archive_acl_entry *ap, *aq; + + if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS && + type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) + return (NULL); + if (acl->acl_text_w != NULL) { + free(acl->acl_text_w); + acl->acl_text_w = NULL; + } + + /* XXX TODO: More sanity-checks on the arguments XXX */ + + /* If there's a matching entry already in the list, overwrite it. */ + ap = acl->acl_head; + aq = NULL; + while (ap != NULL) { + if (ap->type == type && ap->tag == tag && ap->id == id) { + ap->permset = permset; + return (ap); + } + aq = ap; + ap = ap->next; + } + + /* Add a new entry to the end of the list. */ + ap = (struct archive_acl_entry *)malloc(sizeof(*ap)); + if (ap == NULL) + return (NULL); + memset(ap, 0, sizeof(*ap)); + if (aq == NULL) + acl->acl_head = ap; + else + aq->next = ap; + ap->type = type; + ap->tag = tag; + ap->id = id; + ap->permset = permset; + return (ap); +} + +/* + * Return a count of entries matching "want_type". + */ +int +archive_acl_count(struct archive_acl *acl, int want_type) +{ + int count; + struct archive_acl_entry *ap; + + count = 0; + ap = acl->acl_head; + while (ap != NULL) { + if ((ap->type & want_type) != 0) + count++; + ap = ap->next; + } + + if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) + count += 3; + return (count); +} + +/* + * Prepare for reading entries from the ACL data. Returns a count + * of entries matching "want_type", or zero if there are no + * non-extended ACL entries of that type. + */ +int +archive_acl_reset(struct archive_acl *acl, int want_type) +{ + int count, cutoff; + + count = archive_acl_count(acl, want_type); + + /* + * If the only entries are the three standard ones, + * then don't return any ACL data. (In this case, + * client can just use chmod(2) to set permissions.) + */ + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) + cutoff = 3; + else + cutoff = 0; + + if (count > cutoff) + acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; + else + acl->acl_state = 0; + acl->acl_p = acl->acl_head; + return (count); +} + + +/* + * Return the next ACL entry in the list. Fake entries for the + * standard permissions and include them in the returned list. + */ +int +archive_acl_next(struct archive_acl *acl, int want_type, int *type, + int *permset, int *tag, int *id, const char **name) +{ + *name = NULL; + *id = -1; + + /* + * The acl_state is either zero (no entries available), -1 + * (reading from list), or an entry type (retrieve that type + * from ae_stat.aest_mode). + */ + if (acl->acl_state == 0) + return (ARCHIVE_WARN); + + /* The first three access entries are special. */ + if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + switch (acl->acl_state) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + *permset = (acl->mode >> 6) & 7; + *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + return (ARCHIVE_OK); + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + *permset = (acl->mode >> 3) & 7; + *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; + return (ARCHIVE_OK); + case ARCHIVE_ENTRY_ACL_OTHER: + *permset = acl->mode & 7; + *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + *tag = ARCHIVE_ENTRY_ACL_OTHER; + acl->acl_state = -1; + acl->acl_p = acl->acl_head; + return (ARCHIVE_OK); + default: + break; + } + } + + while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) + acl->acl_p = acl->acl_p->next; + if (acl->acl_p == NULL) { + acl->acl_state = 0; + *type = 0; + *permset = 0; + *tag = 0; + *id = -1; + *name = NULL; + return (ARCHIVE_EOF); /* End of ACL entries. */ + } + *type = acl->acl_p->type; + *permset = acl->acl_p->permset; + *tag = acl->acl_p->tag; + *id = acl->acl_p->id; + *name = archive_mstring_get_mbs(&acl->acl_p->name); + acl->acl_p = acl->acl_p->next; + return (ARCHIVE_OK); +} + +/* + * Generate a text version of the ACL. The flags parameter controls + * the style of the generated ACL. + */ +const wchar_t * +archive_acl_text_w(struct archive_acl *acl, int flags) +{ + int count; + size_t length; + const wchar_t *wname; + const wchar_t *prefix; + wchar_t separator; + struct archive_acl_entry *ap; + int id; + wchar_t *wp; + + if (acl->acl_text_w != NULL) { + free (acl->acl_text_w); + acl->acl_text_w = NULL; + } + + separator = L','; + count = 0; + length = 0; + ap = acl->acl_head; + while (ap != NULL) { + if ((ap->type & flags) != 0) { + count++; + if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && + (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) + length += 8; /* "default:" */ + length += 5; /* tag name */ + length += 1; /* colon */ + wname = archive_mstring_get_wcs(&ap->name); + if (wname != NULL) + length += wcslen(wname); + else + length += sizeof(uid_t) * 3 + 1; + length ++; /* colon */ + length += 3; /* rwx */ + length += 1; /* colon */ + length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; + length ++; /* newline */ + } + ap = ap->next; + } + + if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { + length += 10; /* "user::rwx\n" */ + length += 11; /* "group::rwx\n" */ + length += 11; /* "other::rwx\n" */ + } + + if (count == 0) + return (NULL); + + /* Now, allocate the string and actually populate it. */ + wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); + if (wp == NULL) + __archive_errx(1, "No memory to generate the text version of the ACL"); + count = 0; + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, + acl->mode & 0700, -1); + *wp++ = ','; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, + acl->mode & 0070, -1); + *wp++ = ','; + append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, + acl->mode & 0007, -1); + count += 3; + + ap = acl->acl_head; + while (ap != NULL) { + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + wname = archive_mstring_get_wcs(&ap->name); + *wp++ = separator; + if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + id = ap->id; + else + id = -1; + append_entry_w(&wp, NULL, ap->tag, wname, + ap->permset, id); + count++; + } + ap = ap->next; + } + } + + + if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { + if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) + prefix = L"default:"; + else + prefix = NULL; + ap = acl->acl_head; + count = 0; + while (ap != NULL) { + if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { + wname = archive_mstring_get_wcs(&ap->name); + if (count > 0) + *wp++ = separator; + if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + id = ap->id; + else + id = -1; + append_entry_w(&wp, prefix, ap->tag, + wname, ap->permset, id); + count ++; + } + ap = ap->next; + } + } + + return (acl->acl_text_w); +} + + +static void +append_id_w(wchar_t **wp, int id) +{ + if (id < 0) + id = 0; + if (id > 9) + append_id_w(wp, id / 10); + *(*wp)++ = L"0123456789"[id % 10]; +} + +static void +append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, + const wchar_t *wname, int perm, int id) +{ + if (prefix != NULL) { + wcscpy(*wp, prefix); + *wp += wcslen(*wp); + } + switch (tag) { + case ARCHIVE_ENTRY_ACL_USER_OBJ: + wname = NULL; + id = -1; + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_USER: + wcscpy(*wp, L"user"); + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + wname = NULL; + id = -1; + /* FALLTHROUGH */ + case ARCHIVE_ENTRY_ACL_GROUP: + wcscpy(*wp, L"group"); + break; + case ARCHIVE_ENTRY_ACL_MASK: + wcscpy(*wp, L"mask"); + wname = NULL; + id = -1; + break; + case ARCHIVE_ENTRY_ACL_OTHER: + wcscpy(*wp, L"other"); + wname = NULL; + id = -1; + break; + } + *wp += wcslen(*wp); + *(*wp)++ = L':'; + if (wname != NULL) { + wcscpy(*wp, wname); + *wp += wcslen(*wp); + } else if (tag == ARCHIVE_ENTRY_ACL_USER + || tag == ARCHIVE_ENTRY_ACL_GROUP) { + append_id_w(wp, id); + id = -1; + } + *(*wp)++ = L':'; + *(*wp)++ = (perm & 0444) ? L'r' : L'-'; + *(*wp)++ = (perm & 0222) ? L'w' : L'-'; + *(*wp)++ = (perm & 0111) ? L'x' : L'-'; + if (id != -1) { + *(*wp)++ = L':'; + append_id_w(wp, id); + } + **wp = L'\0'; +} + +/* + * Parse a textual ACL. This automatically recognizes and supports + * extensions described above. The 'type' argument is used to + * indicate the type that should be used for any entries not + * explicitly marked as "default:". + */ +int +archive_acl_parse_w(struct archive_acl *acl, + const wchar_t *text, int default_type) +{ + struct { + const wchar_t *start; + const wchar_t *end; + } field[4], name; + + int fields, n; + int type, tag, permset, id; + wchar_t sep; + + while (text != NULL && *text != L'\0') { + /* + * Parse the fields out of the next entry, + * advance 'text' to start of next entry. + */ + fields = 0; + do { + const wchar_t *start, *end; + next_field_w(&text, &start, &end, &sep); + if (fields < 4) { + field[fields].start = start; + field[fields].end = end; + } + ++fields; + } while (sep == L':'); + + /* Set remaining fields to blank. */ + for (n = fields; n < 4; ++n) + field[n].start = field[n].end = NULL; + + /* Check for a numeric ID in field 1 or 3. */ + id = -1; + isint_w(field[1].start, field[1].end, &id); + /* Field 3 is optional. */ + if (id == -1 && fields > 3) + isint_w(field[3].start, field[3].end, &id); + + /* + * Solaris extension: "defaultuser::rwx" is the + * default ACL corresponding to "user::rwx", etc. + */ + if (field[0].end - field[0].start > 7 + && wmemcmp(field[0].start, L"default", 7) == 0) { + type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; + field[0].start += 7; + } else + type = default_type; + + name.start = name.end = NULL; + if (prefix_w(field[0].start, field[0].end, L"user")) { + if (!ismode_w(field[2].start, field[2].end, &permset)) + return (ARCHIVE_WARN); + if (id != -1 || field[1].start < field[1].end) { + tag = ARCHIVE_ENTRY_ACL_USER; + name = field[1]; + } else + tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (prefix_w(field[0].start, field[0].end, L"group")) { + if (!ismode_w(field[2].start, field[2].end, &permset)) + return (ARCHIVE_WARN); + if (id != -1 || field[1].start < field[1].end) { + tag = ARCHIVE_ENTRY_ACL_GROUP; + name = field[1]; + } else + tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (prefix_w(field[0].start, field[0].end, L"other")) { + if (fields == 2 + && field[1].start < field[1].end + && ismode_w(field[1].start, field[1].end, &permset)) { + /* This is Solaris-style "other:rwx" */ + } else if (fields == 3 + && field[1].start == field[1].end + && field[2].start < field[2].end + && ismode_w(field[2].start, field[2].end, &permset)) { + /* This is FreeBSD-style "other::rwx" */ + } else + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_OTHER; + } else if (prefix_w(field[0].start, field[0].end, L"mask")) { + if (fields == 2 + && field[1].start < field[1].end + && ismode_w(field[1].start, field[1].end, &permset)) { + /* This is Solaris-style "mask:rwx" */ + } else if (fields == 3 + && field[1].start == field[1].end + && field[2].start < field[2].end + && ismode_w(field[2].start, field[2].end, &permset)) { + /* This is FreeBSD-style "mask::rwx" */ + } else + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_MASK; + } else + return (ARCHIVE_WARN); + + /* Add entry to the internal list. */ + archive_acl_add_entry_w_len(acl, type, permset, + tag, id, name.start, name.end - name.start); + } + return (ARCHIVE_OK); +} + +/* + * Parse a string to a positive decimal integer. Returns true if + * the string is non-empty and consists only of decimal digits, + * false otherwise. + */ +static int +isint_w(const wchar_t *start, const wchar_t *end, int *result) +{ + int n = 0; + if (start >= end) + return (0); + while (start < end) { + if (*start < '0' || *start > '9') + return (0); + if (n > (INT_MAX / 10)) + n = INT_MAX; + else { + n *= 10; + n += *start - '0'; + } + start++; + } + *result = n; + return (1); +} + +/* + * Parse a string as a mode field. Returns true if + * the string is non-empty and consists only of mode characters, + * false otherwise. + */ +static int +ismode_w(const wchar_t *start, const wchar_t *end, int *permset) +{ + const wchar_t *p; + + if (start >= end) + return (0); + p = start; + *permset = 0; + while (p < end) { + switch (*p++) { + case 'r': case 'R': + *permset |= ARCHIVE_ENTRY_ACL_READ; + break; + case 'w': case 'W': + *permset |= ARCHIVE_ENTRY_ACL_WRITE; + break; + case 'x': case 'X': + *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; + break; + case '-': + break; + default: + return (0); + } + } + return (1); +} + +/* + * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated + * to point to just after the separator. *start points to the first + * character of the matched text and *end just after the last + * character of the matched identifier. In particular *end - *start + * is the length of the field body, not including leading or trailing + * whitespace. + */ +static void +next_field_w(const wchar_t **wp, const wchar_t **start, + const wchar_t **end, wchar_t *sep) +{ + /* Skip leading whitespace to find start of field. */ + while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { + (*wp)++; + } + *start = *wp; + + /* Scan for the separator. */ + while (**wp != L'\0' && **wp != L',' && **wp != L':' && + **wp != L'\n') { + (*wp)++; + } + *sep = **wp; + + /* Trim trailing whitespace to locate end of field. */ + *end = *wp - 1; + while (**end == L' ' || **end == L'\t' || **end == L'\n') { + (*end)--; + } + (*end)++; + + /* Adjust scanner location. */ + if (**wp != L'\0') + (*wp)++; +} + +/* + * Return true if the characters [start...end) are a prefix of 'test'. + * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. + */ +static int +prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) +{ + if (start == end) + return (0); + + if (*start++ != *test++) + return (0); + + while (start < end && *start++ == *test++) + ; + + if (start < end) + return (0); + + return (1); +} diff --git a/libarchive/archive_acl_private.h b/libarchive/archive_acl_private.h new file mode 100644 index 000000000..265284ad4 --- /dev/null +++ b/libarchive/archive_acl_private.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2003-2010 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + +#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED +#define ARCHIVE_ACL_PRIVATE_H_INCLUDED + +#include "archive_string.h" + +struct archive_acl_entry { + struct archive_acl_entry *next; + int type; /* E.g., access or default */ + int tag; /* E.g., user/group/other/mask */ + int permset; /* r/w/x bits */ + int id; /* uid/gid for user/group */ + struct archive_mstring name; /* uname/gname */ +}; + +struct archive_acl { + mode_t mode; + struct archive_acl_entry *acl_head; + struct archive_acl_entry *acl_p; + int acl_state; /* See acl_next for details. */ + wchar_t *acl_text_w; +}; + +void archive_acl_clear(struct archive_acl *); +void archive_acl_copy(struct archive_acl *, struct archive_acl *); +int archive_acl_count(struct archive_acl *, int); +int archive_acl_reset(struct archive_acl *, int); +int archive_acl_next(struct archive_acl *, int, + int *, int *, int *, int *, const char **); + +void archive_acl_add_entry(struct archive_acl *, int, int, int, int, const char *); +//void archive_acl_add_entry_w(struct archive_acl *, +// int, int, int, int, const wchar_t *); +void archive_acl_add_entry_w_len(struct archive_acl *, + int, int, int, int, const wchar_t *, size_t); + +const wchar_t *archive_acl_text_w(struct archive_acl *, int); + +/* + * Private ACL parser. This is private because it handles some + * very weird formats that clients should not be messing with. + * Clients should only deal with their platform-native formats. + * Because of the need to support many formats cleanly, new arguments + * are likely to get added on a regular basis. Clients who try to use + * this interface are likely to be surprised when it changes. + */ +int archive_acl_parse_w(struct archive_acl *, + const wchar_t *, int /* type */); + +#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index 6af94d2d3..e9246f8af 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -68,13 +68,11 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41: #endif #include "archive.h" +#include "archive_acl_private.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_entry_private.h" -#undef max -#define max(a, b) ((a)>(b)?(a):(b)) - #if !defined(HAVE_MAJOR) && !defined(major) /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) @@ -118,24 +116,6 @@ static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); static const char *ae_strtofflags(const char *stringp, unsigned long *setp, unsigned long *clrp); -static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, - const wchar_t *wname, int perm, int id); -static void append_id_w(wchar_t **wp, int id); - -static int acl_special(struct archive_entry *entry, - int type, int permset, int tag); -static struct ae_ace *acl_new_entry(struct archive_entry *entry, - int type, int permset, int tag, int id); -static int isint_w(const wchar_t *start, const wchar_t *end, int *result); -static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); -static void next_field_w(const wchar_t **wp, const wchar_t **start, - const wchar_t **end, wchar_t *sep); -static int prefix_w(const wchar_t *start, const wchar_t *end, - const wchar_t *test); -static void -archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, - int permset, int tag, int id, const wchar_t *name, size_t); - #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) @@ -182,7 +162,7 @@ archive_entry_clear(struct archive_entry *entry) archive_mstring_clean(&entry->ae_symlink); archive_mstring_clean(&entry->ae_uname); archive_entry_copy_mac_metadata(entry, NULL, 0); - archive_entry_acl_clear(entry); + archive_acl_clear(&entry->acl); archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); @@ -194,7 +174,6 @@ struct archive_entry * archive_entry_clone(struct archive_entry *entry) { struct archive_entry *entry2; - struct ae_ace *ap, *ap2; struct ae_xattr *xp; struct ae_sparse *sp; size_t s; @@ -218,14 +197,7 @@ archive_entry_clone(struct archive_entry *entry) archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy ACL data over. */ - ap = entry->acl.acl_head; - while (ap != NULL) { - ap2 = acl_new_entry(entry2, - ap->type, ap->permset, ap->tag, ap->id); - if (ap2 != NULL) - archive_mstring_copy(&ap2->name, &ap->name); - ap = ap->next; - } + archive_acl_copy(&entry2->acl, &entry->acl); /* Copy Mac OS metadata. */ p = archive_entry_mac_metadata(entry, &s); @@ -358,7 +330,7 @@ archive_entry_devminor(struct archive_entry *entry) mode_t archive_entry_filetype(struct archive_entry *entry) { - return (AE_IFMT & entry->ae_stat.aest_mode); + return (AE_IFMT & entry->acl.mode); } void @@ -463,7 +435,7 @@ archive_entry_ino64(struct archive_entry *entry) mode_t archive_entry_mode(struct archive_entry *entry) { - return (entry->ae_stat.aest_mode); + return (entry->acl.mode); } time_t @@ -505,7 +477,7 @@ archive_entry_pathname_w(struct archive_entry *entry) mode_t archive_entry_perm(struct archive_entry *entry) { - return (~AE_IFMT & entry->ae_stat.aest_mode); + return (~AE_IFMT & entry->acl.mode); } dev_t @@ -601,8 +573,8 @@ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; - entry->ae_stat.aest_mode &= ~AE_IFMT; - entry->ae_stat.aest_mode |= AE_IFMT & type; + entry->acl.mode &= ~AE_IFMT; + entry->acl.mode |= AE_IFMT & type; } void @@ -849,7 +821,7 @@ void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; - entry->ae_stat.aest_mode = m; + entry->acl.mode = m; } void @@ -904,8 +876,8 @@ void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; - entry->ae_stat.aest_mode &= AE_IFMT; - entry->ae_stat.aest_mode |= ~AE_IFMT & p; + entry->acl.mode &= AE_IFMT; + entry->acl.mode |= ~AE_IFMT & p; } void @@ -1062,23 +1034,16 @@ archive_entry_copy_mac_metadata(struct archive_entry *entry, * uninitiated. */ +struct archive_acl * +archive_entry_acl(struct archive_entry *entry) +{ + return &entry->acl; +} + void archive_entry_acl_clear(struct archive_entry *entry) { - struct ae_ace *ap; - - while (entry->acl.acl_head != NULL) { - ap = entry->acl.acl_head->next; - archive_mstring_clean(&entry->acl.acl_head->name); - free(entry->acl.acl_head); - entry->acl.acl_head = ap; - } - if (entry->acl.acl_text_w != NULL) { - free(entry->acl.acl_text_w); - entry->acl.acl_text_w = NULL; - } - entry->acl.acl_p = NULL; - entry->acl.acl_state = 0; /* Not counting. */ + archive_acl_clear(&entry->acl); } /* @@ -1088,19 +1053,7 @@ void archive_entry_acl_add_entry(struct archive_entry *entry, int type, int permset, int tag, int id, const char *name) { - struct ae_ace *ap; - - if (acl_special(entry, type, permset, tag) == 0) - return; - ap = acl_new_entry(entry, type, permset, tag, id); - if (ap == NULL) { - /* XXX Error XXX */ - return; - } - if (name != NULL && *name != '\0') - archive_mstring_copy_mbs(&ap->name, name); - else - archive_mstring_clean(&ap->name); + archive_acl_add_entry(&entry->acl, type, permset, tag, id, name); } /* @@ -1110,100 +1063,8 @@ void archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { - archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name)); -} - -static void -archive_entry_acl_add_entry_w_len(struct archive_entry *entry, - int type, int permset, int tag, int id, const wchar_t *name, size_t len) -{ - struct ae_ace *ap; - - if (acl_special(entry, type, permset, tag) == 0) - return; - ap = acl_new_entry(entry, type, permset, tag, id); - if (ap == NULL) { - /* XXX Error XXX */ - return; - } - if (name != NULL && *name != L'\0' && len > 0) - archive_mstring_copy_wcs_len(&ap->name, name, len); - else - archive_mstring_clean(&ap->name); -} - -/* - * If this ACL entry is part of the standard POSIX permissions set, - * store the permissions in the stat structure and return zero. - */ -static int -acl_special(struct archive_entry *entry, int type, int permset, int tag) -{ - if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { - switch (tag) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - entry->ae_stat.aest_mode &= ~0700; - entry->ae_stat.aest_mode |= (permset & 7) << 6; - return (0); - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - entry->ae_stat.aest_mode &= ~0070; - entry->ae_stat.aest_mode |= (permset & 7) << 3; - return (0); - case ARCHIVE_ENTRY_ACL_OTHER: - entry->ae_stat.aest_mode &= ~0007; - entry->ae_stat.aest_mode |= permset & 7; - return (0); - } - } - return (1); -} - -/* - * Allocate and populate a new ACL entry with everything but the - * name. - */ -static struct ae_ace * -acl_new_entry(struct archive_entry *entry, - int type, int permset, int tag, int id) -{ - struct ae_ace *ap, *aq; - - if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS && - type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) - return (NULL); - if (entry->acl.acl_text_w != NULL) { - free(entry->acl.acl_text_w); - entry->acl.acl_text_w = NULL; - } - - /* XXX TODO: More sanity-checks on the arguments XXX */ - - /* If there's a matching entry already in the list, overwrite it. */ - ap = entry->acl.acl_head; - aq = NULL; - while (ap != NULL) { - if (ap->type == type && ap->tag == tag && ap->id == id) { - ap->permset = permset; - return (ap); - } - aq = ap; - ap = ap->next; - } - - /* Add a new entry to the end of the list. */ - ap = (struct ae_ace *)malloc(sizeof(*ap)); - if (ap == NULL) - return (NULL); - memset(ap, 0, sizeof(*ap)); - if (aq == NULL) - entry->acl.acl_head = ap; - else - aq->next = ap; - ap->type = type; - ap->tag = tag; - ap->id = id; - ap->permset = permset; - return (ap); + archive_acl_add_entry_w_len(&entry->acl, + type, permset, tag, id, name, wcslen(name)); } /* @@ -1212,20 +1073,7 @@ acl_new_entry(struct archive_entry *entry, int archive_entry_acl_count(struct archive_entry *entry, int want_type) { - int count; - struct ae_ace *ap; - - count = 0; - ap = entry->acl.acl_head; - while (ap != NULL) { - if ((ap->type & want_type) != 0) - count++; - ap = ap->next; - } - - if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) - count += 3; - return (count); + return archive_acl_count(&entry->acl, want_type); } /* @@ -1236,93 +1084,18 @@ archive_entry_acl_count(struct archive_entry *entry, int want_type) int archive_entry_acl_reset(struct archive_entry *entry, int want_type) { - int count, cutoff; - - count = archive_entry_acl_count(entry, want_type); - - /* - * If the only entries are the three standard ones, - * then don't return any ACL data. (In this case, - * client can just use chmod(2) to set permissions.) - */ - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) - cutoff = 3; - else - cutoff = 0; - - if (count > cutoff) - entry->acl.acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; - else - entry->acl.acl_state = 0; - entry->acl.acl_p = entry->acl.acl_head; - return (count); + return archive_acl_reset(&entry->acl, want_type); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ - int archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { - *name = NULL; - *id = -1; - - /* - * The acl_state is either zero (no entries available), -1 - * (reading from list), or an entry type (retrieve that type - * from ae_stat.aest_mode). - */ - if (entry->acl.acl_state == 0) - return (ARCHIVE_WARN); - - /* The first three access entries are special. */ - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - switch (entry->acl.acl_state) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - *permset = (entry->ae_stat.aest_mode >> 6) & 7; - *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - entry->acl.acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - return (ARCHIVE_OK); - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - *permset = (entry->ae_stat.aest_mode >> 3) & 7; - *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - entry->acl.acl_state = ARCHIVE_ENTRY_ACL_OTHER; - return (ARCHIVE_OK); - case ARCHIVE_ENTRY_ACL_OTHER: - *permset = entry->ae_stat.aest_mode & 7; - *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; - *tag = ARCHIVE_ENTRY_ACL_OTHER; - entry->acl.acl_state = -1; - entry->acl.acl_p = entry->acl.acl_head; - return (ARCHIVE_OK); - default: - break; - } - } - - while (entry->acl.acl_p != NULL && (entry->acl.acl_p->type & want_type) == 0) - entry->acl.acl_p = entry->acl.acl_p->next; - if (entry->acl.acl_p == NULL) { - entry->acl.acl_state = 0; - *type = 0; - *permset = 0; - *tag = 0; - *id = -1; - *name = NULL; - return (ARCHIVE_EOF); /* End of ACL entries. */ - } - *type = entry->acl.acl_p->type; - *permset = entry->acl.acl_p->permset; - *tag = entry->acl.acl_p->tag; - *id = entry->acl.acl_p->id; - *name = archive_mstring_get_mbs(&entry->acl.acl_p->name); - entry->acl.acl_p = entry->acl.acl_p->next; - return (ARCHIVE_OK); + return archive_acl_next(&entry->acl, want_type, type, permset, tag, id, name); } /* @@ -1332,411 +1105,9 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { - int count; - size_t length; - const wchar_t *wname; - const wchar_t *prefix; - wchar_t separator; - struct ae_ace *ap; - int id; - wchar_t *wp; - - if (entry->acl.acl_text_w != NULL) { - free (entry->acl.acl_text_w); - entry->acl.acl_text_w = NULL; - } - - separator = L','; - count = 0; - length = 0; - ap = entry->acl.acl_head; - while (ap != NULL) { - if ((ap->type & flags) != 0) { - count++; - if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && - (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) - length += 8; /* "default:" */ - length += 5; /* tag name */ - length += 1; /* colon */ - wname = archive_mstring_get_wcs(&ap->name); - if (wname != NULL) - length += wcslen(wname); - else - length += sizeof(uid_t) * 3 + 1; - length ++; /* colon */ - length += 3; /* rwx */ - length += 1; /* colon */ - length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; - length ++; /* newline */ - } - ap = ap->next; - } - - if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { - length += 10; /* "user::rwx\n" */ - length += 11; /* "group::rwx\n" */ - length += 11; /* "other::rwx\n" */ - } - - if (count == 0) - return (NULL); - - /* Now, allocate the string and actually populate it. */ - wp = entry->acl.acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); - if (wp == NULL) - __archive_errx(1, "No memory to generate the text version of the ACL"); - count = 0; - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, - entry->ae_stat.aest_mode & 0700, -1); - *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, - entry->ae_stat.aest_mode & 0070, -1); - *wp++ = ','; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, - entry->ae_stat.aest_mode & 0007, -1); - count += 3; - - ap = entry->acl.acl_head; - while (ap != NULL) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - wname = archive_mstring_get_wcs(&ap->name); - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry_w(&wp, NULL, ap->tag, wname, - ap->permset, id); - count++; - } - ap = ap->next; - } - } - - - if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { - if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) - prefix = L"default:"; - else - prefix = NULL; - ap = entry->acl.acl_head; - count = 0; - while (ap != NULL) { - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { - wname = archive_mstring_get_wcs(&ap->name); - if (count > 0) - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) - id = ap->id; - else - id = -1; - append_entry_w(&wp, prefix, ap->tag, - wname, ap->permset, id); - count ++; - } - ap = ap->next; - } - } - - return (entry->acl.acl_text_w); -} - -static void -append_id_w(wchar_t **wp, int id) -{ - if (id < 0) - id = 0; - if (id > 9) - append_id_w(wp, id / 10); - *(*wp)++ = L"0123456789"[id % 10]; -} - -static void -append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, - const wchar_t *wname, int perm, int id) -{ - if (prefix != NULL) { - wcscpy(*wp, prefix); - *wp += wcslen(*wp); - } - switch (tag) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - wname = NULL; - id = -1; - /* FALLTHROUGH */ - case ARCHIVE_ENTRY_ACL_USER: - wcscpy(*wp, L"user"); - break; - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - wname = NULL; - id = -1; - /* FALLTHROUGH */ - case ARCHIVE_ENTRY_ACL_GROUP: - wcscpy(*wp, L"group"); - break; - case ARCHIVE_ENTRY_ACL_MASK: - wcscpy(*wp, L"mask"); - wname = NULL; - id = -1; - break; - case ARCHIVE_ENTRY_ACL_OTHER: - wcscpy(*wp, L"other"); - wname = NULL; - id = -1; - break; - } - *wp += wcslen(*wp); - *(*wp)++ = L':'; - if (wname != NULL) { - wcscpy(*wp, wname); - *wp += wcslen(*wp); - } else if (tag == ARCHIVE_ENTRY_ACL_USER - || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id_w(wp, id); - id = -1; - } - *(*wp)++ = L':'; - *(*wp)++ = (perm & 0444) ? L'r' : L'-'; - *(*wp)++ = (perm & 0222) ? L'w' : L'-'; - *(*wp)++ = (perm & 0111) ? L'x' : L'-'; - if (id != -1) { - *(*wp)++ = L':'; - append_id_w(wp, id); - } - **wp = L'\0'; -} - -/* - * Parse a textual ACL. This automatically recognizes and supports - * extensions described above. The 'type' argument is used to - * indicate the type that should be used for any entries not - * explicitly marked as "default:". - */ -int -__archive_entry_acl_parse_w(struct archive_entry *entry, - const wchar_t *text, int default_type) -{ - struct { - const wchar_t *start; - const wchar_t *end; - } field[4], name; - - int fields, n; - int type, tag, permset, id; - wchar_t sep; - - while (text != NULL && *text != L'\0') { - /* - * Parse the fields out of the next entry, - * advance 'text' to start of next entry. - */ - fields = 0; - do { - const wchar_t *start, *end; - next_field_w(&text, &start, &end, &sep); - if (fields < 4) { - field[fields].start = start; - field[fields].end = end; - } - ++fields; - } while (sep == L':'); - - /* Set remaining fields to blank. */ - for (n = fields; n < 4; ++n) - field[n].start = field[n].end = NULL; - - /* Check for a numeric ID in field 1 or 3. */ - id = -1; - isint_w(field[1].start, field[1].end, &id); - /* Field 3 is optional. */ - if (id == -1 && fields > 3) - isint_w(field[3].start, field[3].end, &id); - - /* - * Solaris extension: "defaultuser::rwx" is the - * default ACL corresponding to "user::rwx", etc. - */ - if (field[0].end - field[0].start > 7 - && wmemcmp(field[0].start, L"default", 7) == 0) { - type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - field[0].start += 7; - } else - type = default_type; - - name.start = name.end = NULL; - if (prefix_w(field[0].start, field[0].end, L"user")) { - if (!ismode_w(field[2].start, field[2].end, &permset)) - return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { - tag = ARCHIVE_ENTRY_ACL_USER; - name = field[1]; - } else - tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (prefix_w(field[0].start, field[0].end, L"group")) { - if (!ismode_w(field[2].start, field[2].end, &permset)) - return (ARCHIVE_WARN); - if (id != -1 || field[1].start < field[1].end) { - tag = ARCHIVE_ENTRY_ACL_GROUP; - name = field[1]; - } else - tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (prefix_w(field[0].start, field[0].end, L"other")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode_w(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "other:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode_w(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "other::rwx" */ - } else - return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_OTHER; - } else if (prefix_w(field[0].start, field[0].end, L"mask")) { - if (fields == 2 - && field[1].start < field[1].end - && ismode_w(field[1].start, field[1].end, &permset)) { - /* This is Solaris-style "mask:rwx" */ - } else if (fields == 3 - && field[1].start == field[1].end - && field[2].start < field[2].end - && ismode_w(field[2].start, field[2].end, &permset)) { - /* This is FreeBSD-style "mask::rwx" */ - } else - return (ARCHIVE_WARN); - tag = ARCHIVE_ENTRY_ACL_MASK; - } else - return (ARCHIVE_WARN); - - /* Add entry to the internal list. */ - archive_entry_acl_add_entry_w_len(entry, type, permset, - tag, id, name.start, name.end - name.start); - } - return (ARCHIVE_OK); -} - -/* - * Parse a string to a positive decimal integer. Returns true if - * the string is non-empty and consists only of decimal digits, - * false otherwise. - */ -static int -isint_w(const wchar_t *start, const wchar_t *end, int *result) -{ - int n = 0; - if (start >= end) - return (0); - while (start < end) { - if (*start < '0' || *start > '9') - return (0); - if (n > (INT_MAX / 10)) - n = INT_MAX; - else { - n *= 10; - n += *start - '0'; - } - start++; - } - *result = n; - return (1); -} - -/* - * Parse a string as a mode field. Returns true if - * the string is non-empty and consists only of mode characters, - * false otherwise. - */ -static int -ismode_w(const wchar_t *start, const wchar_t *end, int *permset) -{ - const wchar_t *p; - - if (start >= end) - return (0); - p = start; - *permset = 0; - while (p < end) { - switch (*p++) { - case 'r': case 'R': - *permset |= ARCHIVE_ENTRY_ACL_READ; - break; - case 'w': case 'W': - *permset |= ARCHIVE_ENTRY_ACL_WRITE; - break; - case 'x': case 'X': - *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; - break; - case '-': - break; - default: - return (0); - } - } - return (1); + return archive_acl_text_w(&entry->acl, flags); } -/* - * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated - * to point to just after the separator. *start points to the first - * character of the matched text and *end just after the last - * character of the matched identifier. In particular *end - *start - * is the length of the field body, not including leading or trailing - * whitespace. - */ -static void -next_field_w(const wchar_t **wp, const wchar_t **start, - const wchar_t **end, wchar_t *sep) -{ - /* Skip leading whitespace to find start of field. */ - while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { - (*wp)++; - } - *start = *wp; - - /* Scan for the separator. */ - while (**wp != L'\0' && **wp != L',' && **wp != L':' && - **wp != L'\n') { - (*wp)++; - } - *sep = **wp; - - /* Trim trailing whitespace to locate end of field. */ - *end = *wp - 1; - while (**end == L' ' || **end == L'\t' || **end == L'\n') { - (*end)--; - } - (*end)++; - - /* Adjust scanner location. */ - if (**wp != L'\0') - (*wp)++; -} - -/* - * Return true if the characters [start...end) are a prefix of 'test'. - * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. - */ -static int -prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) -{ - if (start == end) - return (0); - - if (*start++ != *test++) - return (0); - - while (start < end && *start++ == *test++) - ; - - if (start < end) - return (0); - - return (1); -} - - /* * Following code is modified from UC Berkeley sources, and * is subject to the following copyright notice. diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index c84df2193..93c142c64 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -430,6 +430,11 @@ __LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, /* Return a count of entries matching 'want_type' */ __LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); +/* Return an opaque ACL object. */ +/* There's not yet anything clients can actually do with this... */ +struct archive_acl; +__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *); + /* * extended attributes diff --git a/libarchive/archive_entry_private.h b/libarchive/archive_entry_private.h index 785ca5f6c..8c65d52f6 100644 --- a/libarchive/archive_entry_private.h +++ b/libarchive/archive_entry_private.h @@ -32,24 +32,9 @@ #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED #define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED +#include "archive_acl_private.h" #include "archive_string.h" -struct ae_ace { - struct ae_ace *next; - int type; /* E.g., access or default */ - int tag; /* E.g., user/group/other/mask */ - int permset; /* r/w/x bits */ - int id; /* uid/gid for user/group */ - struct archive_mstring name; /* uname/gname */ -}; - -struct ae_acl { - struct ae_ace *acl_head; - struct ae_ace *acl_p; - int acl_state; /* See acl_next for details. */ - wchar_t *acl_text_w; -}; - struct ae_xattr { struct ae_xattr *next; @@ -115,7 +100,6 @@ struct archive_entry { uint32_t aest_birthtime_nsec; int64_t aest_gid; int64_t aest_ino; - mode_t aest_mode; uint32_t aest_nlink; uint64_t aest_size; int64_t aest_uid; @@ -165,7 +149,7 @@ struct archive_entry { size_t mac_metadata_size; /* ACL support. */ - struct ae_acl acl; + struct archive_acl acl; /* extattr support. */ struct ae_xattr *xattr_head; @@ -180,15 +164,4 @@ struct archive_entry { char strmode[12]; }; -/* - * Private ACL parser. This is private because it handles some - * very weird formats that clients should not be messing with. - * Clients should only deal with their platform-native formats. - * Because of the need to support many formats cleanly, new arguments - * are likely to get added on a regular basis. Clients who try to use - * this interface are likely to be surprised when it changes. - */ -int __archive_entry_acl_parse_w(struct archive_entry *, - const wchar_t *, int /* type */); - #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index 6c1802965..45c878b3f 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -24,10 +24,10 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if defined(_WIN32) && !defined(__CYGWIN__) - #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk.c 189429 2009-03-06 04:35:31Z kientzle $"); +__FBSDID("$FreeBSD$"); + +#if defined(_WIN32) && !defined(__CYGWIN__) #ifdef HAVE_SYS_PARAM_H #include diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 9d9718b48..c2ebeba58 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -68,8 +68,8 @@ static size_t wcslen(const wchar_t *s) #endif #include "archive.h" +#include "archive_acl_private.h" /* For ACL parsing routines. */ #include "archive_entry.h" -#include "archive_entry_private.h" /* For ACL parsing routines. */ #include "archive_private.h" #include "archive_read_private.h" @@ -875,7 +875,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, p++; wp = utf8_decode(tar, acl, p - acl); - err = __archive_entry_acl_parse_w(entry, wp, + err = archive_acl_parse_w(archive_entry_acl(entry), wp, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); if (err != ARCHIVE_OK) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -1610,12 +1610,12 @@ pax_attribute(struct tar *tar, struct archive_entry *entry, if (strcmp(key, "SCHILY.acl.access")==0) { wp = utf8_decode(tar, value, strlen(value)); /* TODO: if (wp == NULL) */ - __archive_entry_acl_parse_w(entry, wp, + archive_acl_parse_w(archive_entry_acl(entry), wp, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); } else if (strcmp(key, "SCHILY.acl.default")==0) { wp = utf8_decode(tar, value, strlen(value)); /* TODO: if (wp == NULL) */ - __archive_entry_acl_parse_w(entry, wp, + archive_acl_parse_w(archive_entry_acl(entry), wp, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); } else if (strcmp(key, "SCHILY.devmajor")==0) { archive_entry_set_rdevmajor(entry,