]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Move the abstract ACL manipulations into a separate source file.
authorTim Kientzle <kientzle@gmail.com>
Sat, 20 Nov 2010 07:17:02 +0000 (02:17 -0500)
committerTim Kientzle <kientzle@gmail.com>
Sat, 20 Nov 2010 07:17:02 +0000 (02:17 -0500)
SVN-Revision: 2793

Makefile.am
configure.ac
libarchive/CMakeLists.txt
libarchive/archive_acl.c [new file with mode: 0644]
libarchive/archive_acl_private.h [new file with mode: 0644]
libarchive/archive_entry.c
libarchive/archive_entry.h
libarchive/archive_entry_private.h
libarchive/archive_read_disk_windows.c
libarchive/archive_read_support_format_tar.c

index 9e7c0f252f7471038c7114f158ed051d6ce9a90b..5bbd14efba99cd3822e2e228a17bc1ab9ce8e8e2 100644 (file)
@@ -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                             \
index 865a7cd300bf9e84f85171a7bf1e1d8ff8fbed59..92e2c0236143bb1f4190b3e86b3f104f043bece5 100644 (file)
@@ -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])
 
index f0a9248750b3b8440e937042b75e9269cbb6f628..c50ac9b0118320a4730ee651f6c9cb3f53e313b5 100644 (file)
@@ -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 (file)
index 0000000..d419548
--- /dev/null
@@ -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 <limits.h>
+#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 (file)
index 0000000..265284a
--- /dev/null
@@ -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 */
index 6af94d2d325fb2e5a03826a512bb3ff5dcc3f687..e9246f8af7b8c0385792321c343a035856c3f68b 100644 (file)
@@ -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.
index c84df219381775edf0e610d8c7da04e18d39cf2a..93c142c6489c56951da44b6a506a7a885ff9bbe3 100644 (file)
@@ -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
index 785ca5f6cd7b91a0158881c784cf1dbd3ed1df62..8c65d52f6d0f392551aee10b0323be8839e85e2c 100644 (file)
 #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 */
index 6c1802965112256dfaa16a7045af68ee7a9c8980..45c878b3fcbf3f5f0a39ead5ef3f5cd82c075920 100644 (file)
  * (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 <sys/param.h>
index 9d9718b48c0df3ca679bbdba7b12024db58ab5fa..c2ebeba586f4c2e0c152268594077980dae01e66 100644 (file)
@@ -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,