]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Extend NFSv4 ACL support in libarchive
authorMartin Matuska <martin@matuska.org>
Tue, 27 Dec 2016 22:21:39 +0000 (23:21 +0100)
committerMartin Matuska <martin@matuska.org>
Tue, 27 Dec 2016 22:21:39 +0000 (23:21 +0100)
Break up, simplify and improve OS-independent ACL code
Add star-compatible NFSv4 ACL read/write support to pax (SCHILY.acl.ace)
Remap ARCHIVE_ACL_ENTRY_STYLE_* flags

New functions:
  archive_entry_acl_from_text()
  archive_entry_acl_from_text_w()
  archive_entry_acl_to_text()
  archive_entry_acl_to_text_w()

Deprecated functions:
  archive_entry_acl_text()
  archive_entry_acl_text_w()

Closes #771

27 files changed:
Makefile.am
NEWS
libarchive/archive_acl.c
libarchive/archive_acl_private.h
libarchive/archive_entry.c
libarchive/archive_entry.h
libarchive/archive_entry_acl.3
libarchive/archive_entry_locale.h
libarchive/archive_read_disk_entry_from_file.c
libarchive/archive_read_support_format_tar.c
libarchive/archive_write_set_format_pax.c
libarchive/libarchive-formats.5
libarchive/tar.5
libarchive/test/CMakeLists.txt
libarchive/test/main.c
libarchive/test/test.h
libarchive/test/test_acl_freebsd_nfs4.c
libarchive/test/test_acl_freebsd_posix1e.c
libarchive/test/test_acl_nfs4.c
libarchive/test/test_acl_pax.c
libarchive/test/test_acl_pax_nfs4.tar.uu [new file with mode: 0644]
libarchive/test/test_acl_pax_posix1e.tar.uu [moved from libarchive/test/test_acl_pax.tar.uu with 99% similarity]
libarchive/test/test_acl_posix1e.c
libarchive/test/test_acl_text.c [new file with mode: 0644]
libarchive/test/test_compat_star_acl.c [new file with mode: 0644]
libarchive/test/test_compat_star_acl_nfs4.tar.uu [new file with mode: 0644]
libarchive/test/test_compat_star_acl_posix1e.c [deleted file]

index 6ed04959aff481d6d13951faf1ce29dce76a87c3..580fa2a6ec3b88a4e8e099d2a1170729492a99f4 100644 (file)
@@ -328,6 +328,7 @@ libarchive_test_SOURCES= \
        libarchive/test/test_acl_nfs4.c \
        libarchive/test/test_acl_pax.c \
        libarchive/test/test_acl_posix1e.c \
+       libarchive/test/test_acl_text.c \
        libarchive/test/test_archive_api_feature.c \
        libarchive/test/test_archive_clear_error.c \
        libarchive/test/test_archive_cmdline.c \
@@ -376,7 +377,7 @@ libarchive_test_SOURCES= \
        libarchive/test/test_compat_plexus_archiver_tar.c \
        libarchive/test/test_compat_solaris_tar_acl.c \
        libarchive/test/test_compat_solaris_pax_sparse.c \
-       libarchive/test/test_compat_star_acl_posix1e.c \
+       libarchive/test/test_compat_star_acl.c \
        libarchive/test/test_compat_tar_hardlink.c \
        libarchive/test/test_compat_uudecode.c \
        libarchive/test/test_compat_uudecode_large.c \
@@ -598,7 +599,8 @@ libarchive_TESTS_ENVIRONMENT= LIBARCHIVE_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/
 
 libarchive_test_EXTRA_DIST=\
        libarchive/test/list.h \
-       libarchive/test/test_acl_pax.tar.uu \
+       libarchive/test/test_acl_pax_posix1e.tar.uu \
+       libarchive/test/test_acl_pax_nfs4.tar.uu \
        libarchive/test/test_archive_string_conversion.txt.Z.uu \
        libarchive/test/test_compat_bzip2_1.tbz.uu \
        libarchive/test/test_compat_bzip2_2.tbz.uu \
@@ -635,6 +637,7 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_compat_solaris_pax_sparse_1.pax.Z.uu \
        libarchive/test/test_compat_solaris_pax_sparse_2.pax.Z.uu \
        libarchive/test/test_compat_solaris_tar_acl.tar.uu \
+       libarchive/test/test_compat_star_acl_nfs4.tar.uu \
        libarchive/test/test_compat_star_acl_posix1e.tar.uu \
        libarchive/test/test_compat_tar_hardlink_1.tar.uu \
        libarchive/test/test_compat_uudecode_large.tar.Z.uu \
diff --git a/NEWS b/NEWS
index 6f16b889ac86e11ed853d05e289cabb966e40c16..4d875b2a9df56acdcf5e4db2ca329c7401c38a05 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+Dec 27, 2016: NFSv4 ACL read and write support for pax
+    Deprecated functions: archive_entry_acl_text(), archive_entry_acl_text_w()
+
 Oct 26, 2016: Remove liblzmadec support
 
 Oct 23, 2016: libarchive 3.2.2 released
index 3653d6fc7b4a323a8d9247781f723b9c80e061de..b8e8ba40d2ec48e8b7d4bb3f399a4f73b0a44ab1 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -55,23 +56,31 @@ static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
 static int     archive_acl_add_entry_len_l(struct archive_acl *acl,
                    int type, int permset, int tag, int id, const char *name,
                    size_t len, struct archive_string_conv *sc);
+static int     archive_acl_text_want_type(struct archive_acl *acl, int flags);
+static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type,
+                   int flags, int wide, struct archive *a,
+                   struct archive_string_conv *sc);
 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 int     is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
+                   int *result);
+static int     is_nfs4_perms_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_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+                   int tag, int flags, const wchar_t *wname, int perm, int id);
 static void    append_id_w(wchar_t **wp, int id);
 static int     isint(const char *start, const char *end, int *result);
 static int     ismode(const char *start, const char *end, int *result);
+static int     is_nfs4_flags(const char *start, const char *end,
+                   int *result);
+static int     is_nfs4_perms(const char *start, const char *end,
+                   int *result);
 static void    next_field(const char **p, const char **start,
                    const char **end, char *sep);
-static int     prefix_c(const char *start, const char *end,
-                   const char *test);
-static void    append_entry(char **p, const char *prefix, int tag,
-                   const char *name, int perm, int id);
+static void    append_entry(char **p, const char *prefix, int type,
+                   int tag, int flags, const char *name, int perm, int id);
 static void    append_id(char **p, int id);
 
 void
@@ -375,8 +384,8 @@ archive_acl_reset(struct archive_acl *acl, int want_type)
  * standard permissions and include them in the returned list.
  */
 int
-archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
-    int *permset, int *tag, int *id, const char **name)
+archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
+    int *type, int *permset, int *tag, int *id, const char **name)
 {
        *name = NULL;
        *id = -1;
@@ -441,130 +450,273 @@ archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int
 }
 
 /*
- * Generate a text version of the ACL.  The flags parameter controls
- * the style of the generated ACL.
+ * Determine what type of ACL do we want
  */
-const wchar_t *
-archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
+static int
+archive_acl_text_want_type(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, r;
-       wchar_t *wp;
+       int want_type;
 
-       if (acl->acl_text_w != NULL) {
-               free (acl->acl_text_w);
-               acl->acl_text_w = NULL;
+       /* Check if ACL is NFSv4 */
+       if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+               /* NFSv4 should never mix with POSIX.1e */
+               if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+                       return (0);
+               else
+                       return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
        }
 
-       separator = L',';
+       /* Now deal with POSIX.1e ACLs */
+
+       want_type = 0;
+       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+               want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+               want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+
+       /* By default we want both access and default ACLs */
+       if (want_type == 0)
+               return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
+
+       return (want_type);
+}
+
+/*
+ * Calculate ACL text string length
+ */
+static ssize_t
+archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
+    int wide, struct archive *a, struct archive_string_conv *sc) {
+       struct archive_acl_entry *ap;
+       const char *name;
+       const wchar_t *wname;
+       int count, idlen, tmp, r;
+       ssize_t length;
+       size_t len;
+
        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 */
-                       r = archive_mstring_get_wcs(a, &ap->name, &wname);
-                       if (r == 0 && wname != NULL)
-                               length += wcslen(wname);
-                       else if (r < 0 && errno == ENOMEM)
-                               return (NULL);
-                       else
-                               length += sizeof(uid_t) * 3 + 1;
-                       length ++; /* colon */
+       for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+               if ((ap->type & want_type) == 0)
+                       continue;
+               /*
+                * Filemode-mapping ACL entries are stored exclusively in
+                * ap->mode so they should not be in the list
+                */
+               if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+                   && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+                   || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+                   || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+                       continue;
+               count++;
+               if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
+                   && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+                       length += 8; /* "default:" */
+               switch (ap->tag) {
+               case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+                               length += 6; /* "owner@" */
+                               break;
+                       }
+                       /* FALLTHROUGH */
+               case ARCHIVE_ENTRY_ACL_USER:
+               case ARCHIVE_ENTRY_ACL_MASK:
+                       length += 4; /* "user", "mask" */
+                       break;
+               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+                               length += 6; /* "group@" */
+                               break;
+                       }
+                       /* FALLTHROUGH */
+               case ARCHIVE_ENTRY_ACL_GROUP:
+               case ARCHIVE_ENTRY_ACL_OTHER:
+                       length += 5; /* "group", "other" */
+                       break;
+               case ARCHIVE_ENTRY_ACL_EVERYONE:
+                       length += 9; /* "everyone@" */
+                       break;
+               }
+               length += 1; /* colon after tag */
+               if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+                   ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
+                       if (wide) {
+                               r = archive_mstring_get_wcs(a, &ap->name,
+                                   &wname);
+                               if (r == 0 && wname != NULL)
+                                       length += wcslen(wname);
+                               else if (r < 0 && errno == ENOMEM)
+                                       return (0);
+                               else
+                                       length += sizeof(uid_t) * 3 + 1;
+                       } else {
+                               r = archive_mstring_get_mbs_l(&ap->name, &name,
+                                   &len, sc);
+                               if (r != 0)
+                                       return (0);
+                               if (len > 0 && name != NULL)
+                                       length += len;
+                               else
+                                       length += sizeof(uid_t) * 3 + 1;
+                       }
+                       length += 1; /* colon after user or group name */
+               } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+                       length += 1; /* 2nd colon empty user,group or other */
+
+               if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
+                   && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+                   && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
+                   || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
+                       /* Solaris has no colon after other: and mask: */
+                       length = length - 1;
+               }
+
+               if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+                       /* rwxpdDaARWcCos:fdinSFI:deny */
+                       length += 27;
+                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
+                               length += 1; /* allow, alarm, audit */
+               } else
                        length += 3; /* rwx */
+
+               if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+                   ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
+                   (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
                        length += 1; /* colon */
-                       length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
-                       length ++; /* newline */
+                       /* ID digit count */
+                       idlen = 1;
+                       tmp = ap->id;
+                       while (tmp > 9) {
+                               tmp = tmp / 10;
+                               idlen++;
+                       }
+                       length += idlen;
                }
-               ap = ap->next;
+               length ++; /* entry separator */
        }
 
-       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" */
-       }
+       /* Add filemode-mapping access entries to the length */
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+               if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
+                       /* "user::rwx\ngroup::rwx\nother:rwx\n" */
+                       length += 31;
+               } else {
+                       /* "user::rwx\ngroup::rwx\nother::rwx\n" */
+                       length += 32;
+               }
+       } else if (count == 0)
+               return (0);
+
+       /* The terminating character is included in count */
+       return (length);
+}
 
-       if (count == 0)
+/*
+ * Generate a wide text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+wchar_t *
+archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
+    struct archive *a)
+{
+       int count;
+       ssize_t length;
+       size_t len;
+       const wchar_t *wname;
+       const wchar_t *prefix;
+       wchar_t separator;
+       struct archive_acl_entry *ap;
+       int id, r, want_type;
+       wchar_t *wp, *ws;
+
+       want_type = archive_acl_text_want_type(acl, flags);
+
+       /* Both NFSv4 and POSIX.1 types found */
+       if (want_type == 0)
                return (NULL);
 
+       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+               flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+       length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
+
+       if (length == 0)
+               return (NULL);
+
+       if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+               separator = L',';
+       else
+               separator = L'\n';
+
        /* Now, allocate the string and actually populate it. */
-       wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
-       if (wp == NULL)
+       wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
+       if (wp == NULL) {
+               if (errno == ENOMEM)
+                       __archive_errx(1, "No memory");
                return (NULL);
+       }
        count = 0;
-       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
+
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+                   ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
                    acl->mode & 0700, -1);
-               *wp++ = ',';
-               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
+               *wp++ = separator;
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+                   ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
                    acl->mode & 0070, -1);
-               *wp++ = ',';
-               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
+               *wp++ = separator;
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+                   ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
                    acl->mode & 0007, -1);
                count += 3;
-
-               ap = acl->acl_head;
-               while (ap != NULL) {
-                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-                               r = archive_mstring_get_wcs(a, &ap->name, &wname);
-                               if (r == 0) {
-                                       *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++;
-                               } else if (r < 0 && errno == ENOMEM)
-                                       return (NULL);
-                       }
-                       ap = ap->next;
-               }
        }
 
-
-       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-               if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
+       for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+               if ((ap->type & want_type) == 0)
+                       continue;
+               /*
+                * Filemode-mapping ACL entries are stored exclusively in
+                * ap->mode so they should not be in the list
+                */
+               if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+                   && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+                   || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+                   || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+                       continue;
+               if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+                   (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
                        prefix = L"default:";
                else
                        prefix = NULL;
-               ap = acl->acl_head;
-               count = 0;
-               while (ap != NULL) {
-                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-                               r = archive_mstring_get_wcs(a, &ap->name, &wname);
-                               if (r == 0) {
-                                       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 ++;
-                               } else if (r < 0 && errno == ENOMEM)
-                                       return (NULL);
-                       }
-                       ap = ap->next;
-               }
+               r = archive_mstring_get_wcs(a, &ap->name, &wname);
+               if (r == 0) {
+                       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->type, ap->tag, flags,
+                           wname, ap->permset, id);
+                       count++;
+               } else if (r < 0 && errno == ENOMEM)
+                       return (NULL);
        }
 
-       return (acl->acl_text_w);
-}
+       /* Add terminating character */
+       *wp++ = L'\0';
+
+       len = wcslen(ws);
 
+       if ((ssize_t)len > (length - 1))
+               __archive_errx(1, "Buffer overrun");
+
+       if (text_len != NULL)
+               *text_len = len;
+
+       return (ws);
+}
 
 static void
 append_id_w(wchar_t **wp, int id)
@@ -577,8 +729,8 @@ append_id_w(wchar_t **wp, int id)
 }
 
 static void
-append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
-    const wchar_t *wname, int perm, int id)
+append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+    int tag, int flags, const wchar_t *wname, int perm, int id)
 {
        if (prefix != NULL) {
                wcscpy(*wp, prefix);
@@ -588,6 +740,10 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
        case ARCHIVE_ENTRY_ACL_USER_OBJ:
                wname = NULL;
                id = -1;
+               if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+                       wcscpy(*wp, L"owner@");
+                       break;
+               }
                /* FALLTHROUGH */
        case ARCHIVE_ENTRY_ACL_USER:
                wcscpy(*wp, L"user");
@@ -595,6 +751,10 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
        case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
                wname = NULL;
                id = -1;
+               if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+                       wcscpy(*wp, L"group@");
+                       break;
+               }
                /* FALLTHROUGH */
        case ARCHIVE_ENTRY_ACL_GROUP:
                wcscpy(*wp, L"group");
@@ -609,154 +769,209 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
                wname = NULL;
                id = -1;
                break;
+       case ARCHIVE_ENTRY_ACL_EVERYONE:
+               wcscpy(*wp, L"everyone@");
+               wname = NULL;
+               id = -1;
+               break;
        }
        *wp += wcslen(*wp);
        *(*wp)++ = L':';
-       if (wname != NULL) {
-               wcscpy(*wp, wname);
+       if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+           tag == ARCHIVE_ENTRY_ACL_USER ||
+           tag == ARCHIVE_ENTRY_ACL_GROUP) {
+               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;
+               }
+               /* Solaris style has no second colon after other and mask */
+               if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+                   || (tag != ARCHIVE_ENTRY_ACL_OTHER
+                   && tag != ARCHIVE_ENTRY_ACL_MASK))
+                       *(*wp)++ = L':';
+       }
+       if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+               /* POSIX.1e ACL perms */
+               *(*wp)++ = (perm & 0444) ? L'r' : L'-';
+               *(*wp)++ = (perm & 0222) ? L'w' : L'-';
+               *(*wp)++ = (perm & 0111) ? L'x' : L'-';
+       } else {
+               /* NFS4 ACL perms */
+               *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA |
+                   ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? L'r' : L'-';
+               *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA |
+                   ARCHIVE_ENTRY_ACL_ADD_FILE)) ? L'w' : L'-';
+               *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_EXECUTE) ? L'x' : L'-';
+               *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA |
+                   ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? L'p' : L'-';
+               *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? L'd' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? L'D' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? L'a' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? L'A' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? L'R' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? L'W' : L'-';
+               *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ACL) ? L'c' : L'-';
+               *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ACL) ? L'C' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? L'o' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? L's' : L'-';
+               *(*wp)++ = L':';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? L'f' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? L'd' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? L'i' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? L'n' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? L'S' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? L'F' : L'-';
+               *(*wp)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? L'I' : L'-';
+               *(*wp)++ = L':';
+               switch (type) {
+               case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+                       wcscpy(*wp, L"allow");
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+                       wcscpy(*wp, L"deny");
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+                       wcscpy(*wp, L"audit");
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+                       wcscpy(*wp, L"alarm");
+                       break;
+               default:
+                       break;
+               }
                *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';
 }
 
-int
-archive_acl_text_l(struct archive_acl *acl, int flags,
-    const char **acl_text, size_t *acl_text_len,
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+char *
+archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
     struct archive_string_conv *sc)
 {
        int count;
-       size_t length;
+       ssize_t length;
+       size_t len;
        const char *name;
        const char *prefix;
        char separator;
        struct archive_acl_entry *ap;
-       size_t len;
-       int id, r;
-       char *p;
+       int id, r, want_type;
+       char *p, *s;
 
-       if (acl->acl_text != NULL) {
-               free (acl->acl_text);
-               acl->acl_text = NULL;
-       }
+       want_type = archive_acl_text_want_type(acl, flags);
 
-       *acl_text = NULL;
-       if (acl_text_len != NULL)
-               *acl_text_len = 0;
-       separator = ',';
-       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 */
-                       r = archive_mstring_get_mbs_l(
-                           &ap->name, &name, &len, sc);
-                       if (r != 0)
-                               return (-1);
-                       if (len > 0 && name != NULL)
-                               length += len;
-                       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;
-       }
+       /* Both NFSv4 and POSIX.1 types found */
+       if (want_type == 0)
+               return (NULL);
 
-       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 (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+               flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
 
-       if (count == 0)
-               return (0);
+       length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
+
+       if (length == 0)
+               return (NULL);
+
+       if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+               separator = ',';
+       else
+               separator = '\n';
 
        /* Now, allocate the string and actually populate it. */
-       p = acl->acl_text = (char *)malloc(length);
-       if (p == NULL)
-               return (-1);
+       p = s = (char *)malloc(length * sizeof(char));
+       if (p == NULL) {
+               if (errno == ENOMEM)
+                       __archive_errx(1, "No memory");
+               return (NULL);
+       }
        count = 0;
-       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-               append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
+
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+               append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+                   ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
                    acl->mode & 0700, -1);
-               *p++ = ',';
-               append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
+               *p++ = separator;
+               append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+                   ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
                    acl->mode & 0070, -1);
-               *p++ = ',';
-               append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
+               *p++ = separator;
+               append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+                   ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
                    acl->mode & 0007, -1);
                count += 3;
-
-               for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
-                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
-                               continue;
-                       r = archive_mstring_get_mbs_l(
-                           &ap->name, &name, &len, sc);
-                       if (r != 0)
-                               return (-1);
-                       *p++ = separator;
-                       if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
-                               id = ap->id;
-                       } else {
-                               id = -1;
-                       }
-                       append_entry(&p, NULL, ap->tag, name,
-                           ap->permset, id);
-                       count++;
-               }
        }
 
-
-       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-               if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
+       for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+               if ((ap->type & want_type) == 0)
+                       continue;
+               /*
+                * Filemode-mapping ACL entries are stored exclusively in
+                * ap->mode so they should not be in the list
+                */
+               if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+                   && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+                   || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+                   || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+                       continue;
+               if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+                   (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
                        prefix = "default:";
                else
                        prefix = NULL;
-               count = 0;
-               for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
-                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
-                               continue;
-                       r = archive_mstring_get_mbs_l(
-                           &ap->name, &name, &len, sc);
-                       if (r != 0)
-                               return (-1);
-                       if (count > 0)
-                               *p++ = separator;
-                       if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
-                               id = ap->id;
-                       else
-                               id = -1;
-                       append_entry(&p, prefix, ap->tag,
-                           name, ap->permset, id);
-                       count ++;
+               r = archive_mstring_get_mbs_l(
+                   &ap->name, &name, &len, sc);
+               if (r != 0)
+                       return (NULL);
+               if (count > 0)
+                       *p++ = separator;
+               if (name == NULL ||
+                   (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
+                       id = ap->id;
+               } else {
+                       id = -1;
                }
+               append_entry(&p, prefix, ap->type, ap->tag, flags, name,
+                   ap->permset, id);
+               count++;
        }
 
-       *acl_text = acl->acl_text;
-       if (acl_text_len != NULL)
-               *acl_text_len = strlen(acl->acl_text);
-       return (0);
+       /* Add terminating character */
+       *p++ = '\0';
+
+       len = strlen(s);
+
+       if ((ssize_t)len > (length - 1))
+               __archive_errx(1, "Buffer overrun");
+
+       if (text_len != NULL)
+               *text_len = len;
+
+       return (s);
 }
 
 static void
@@ -770,8 +985,8 @@ append_id(char **p, int id)
 }
 
 static void
-append_entry(char **p, const char *prefix, int tag,
-    const char *name, int perm, int id)
+append_entry(char **p, const char *prefix, int type,
+    int tag, int flags, const char *name, int perm, int id)
 {
        if (prefix != NULL) {
                strcpy(*p, prefix);
@@ -781,6 +996,10 @@ append_entry(char **p, const char *prefix, int tag,
        case ARCHIVE_ENTRY_ACL_USER_OBJ:
                name = NULL;
                id = -1;
+               if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+                       strcpy(*p, "owner@");
+                       break;
+               }
                /* FALLTHROUGH */
        case ARCHIVE_ENTRY_ACL_USER:
                strcpy(*p, "user");
@@ -788,6 +1007,10 @@ append_entry(char **p, const char *prefix, int tag,
        case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
                name = NULL;
                id = -1;
+               if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+                       strcpy(*p, "group@");
+                       break;
+               }
                /* FALLTHROUGH */
        case ARCHIVE_ENTRY_ACL_GROUP:
                strcpy(*p, "group");
@@ -802,48 +1025,146 @@ append_entry(char **p, const char *prefix, int tag,
                name = NULL;
                id = -1;
                break;
+       case ARCHIVE_ENTRY_ACL_EVERYONE:
+               strcpy(*p, "everyone@");
+               name = NULL;
+               id = -1;
+               break;
        }
        *p += strlen(*p);
        *(*p)++ = ':';
-       if (name != NULL) {
-               strcpy(*p, name);
+       if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+           tag == ARCHIVE_ENTRY_ACL_USER ||
+           tag == ARCHIVE_ENTRY_ACL_GROUP) {
+               if (name != NULL) {
+                       strcpy(*p, name);
+                       *p += strlen(*p);
+               } else if (tag == ARCHIVE_ENTRY_ACL_USER
+                   || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+                       append_id(p, id);
+                       id = -1;
+               }
+               /* Solaris style has no second colon after other and mask */
+               if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+                   || (tag != ARCHIVE_ENTRY_ACL_OTHER
+                   && tag != ARCHIVE_ENTRY_ACL_MASK))
+                       *(*p)++ = ':';
+       }
+       if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+               /* POSIX.1e ACL perms */
+               *(*p)++ = (perm & 0444) ? 'r' : '-';
+               *(*p)++ = (perm & 0222) ? 'w' : '-';
+               *(*p)++ = (perm & 0111) ? 'x' : '-';
+       } else {
+               /* NFS4 ACL perms */
+               *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA |
+                   ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? 'r' : '-';
+               *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA |
+                   ARCHIVE_ENTRY_ACL_ADD_FILE)) ? 'w' : '-';
+               *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_EXECUTE)) ? 'x' : '-';
+               *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA |
+                   ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? 'p' : '-';
+               *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? 'd' : '-';
+               *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? 'D' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? 'a' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? 'A' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? 'R' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? 'W' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_READ_ACL) ? 'c' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_WRITE_ACL) ? 'C' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? 'o' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? 's' : '-';
+               *(*p)++ = ':';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? 'f' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? 'd' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? 'i' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? 'n' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? 'S' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? 'F' : '-';
+               *(*p)++ = (perm &
+                   ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? 'I' : '-';
+               *(*p)++ = ':';
+               switch (type) {
+               case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+                       strcpy(*p, "allow");
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+                       strcpy(*p, "deny");
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+                       strcpy(*p, "audit");
+                       break;
+               case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+                       strcpy(*p, "alarm");
+                       break;
+               }
                *p += strlen(*p);
-       } else if (tag == ARCHIVE_ENTRY_ACL_USER
-           || tag == ARCHIVE_ENTRY_ACL_GROUP) {
-               append_id(p, id);
-               id = -1;
        }
-       *(*p)++ = ':';
-       *(*p)++ = (perm & 0444) ? 'r' : '-';
-       *(*p)++ = (perm & 0222) ? 'w' : '-';
-       *(*p)++ = (perm & 0111) ? 'x' : '-';
        if (id != -1) {
                *(*p)++ = ':';
                append_id(p, id);
        }
-       **p = '\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:".
+ * Parse a wide ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
  */
 int
-archive_acl_parse_w(struct archive_acl *acl,
-    const wchar_t *text, int default_type)
+archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
+    int want_type)
 {
        struct {
                const wchar_t *start;
                const wchar_t *end;
-       } field[4], name;
+       } field[6], name;
 
-       int fields, n;
-       int type, tag, permset, id;
+       const wchar_t *s, *st;
+
+       int numfields, fields, n, r, ret;
+       int type, types, tag, permset, id;
+       size_t len;
        wchar_t sep;
 
-       while (text != NULL  &&  *text != L'\0') {
+       ret = ARCHIVE_OK;
+       types = 0;
+
+       switch (want_type) {
+       case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+               want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+       case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+       case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+               numfields = 5;
+               break;
+       case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+               numfields = 6;
+               break;
+       default:
+               return (ARCHIVE_FATAL);
+       }
+
+       while (text != NULL && *text != L'\0') {
                /*
                 * Parse the fields out of the next entry,
                 * advance 'text' to start of next entry.
@@ -852,7 +1173,7 @@ archive_acl_parse_w(struct archive_acl *acl,
                do {
                        const wchar_t *start, *end;
                        next_field_w(&text, &start, &end, &sep);
-                       if (fields < 4) {
+                       if (fields < numfields) {
                                field[fields].start = start;
                                field[fields].end = end;
                        }
@@ -860,78 +1181,204 @@ archive_acl_parse_w(struct archive_acl *acl,
                } while (sep == L':');
 
                /* Set remaining fields to blank. */
-               for (n = fields; n < 4; ++n)
+               for (n = fields; n < numfields; ++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;
+               if (field[0].start != NULL && *(field[0].start) == L'#') {
+                       /* Comment, skip entry */
+                       continue;
+               }
 
+               n = 0;
+               id = -1;
+               permset = 0;
                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];
+
+               if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+                       /* POSIX.1e ACLs */
+                       /*
+                        * Default keyword "default:user::rwx"
+                        * if found, we have one more field
+                        *
+                        * We also support old Solaris extension:
+                        * "defaultuser::rwx" is the default ACL corresponding
+                        * to "user::rwx", etc. valid only for first field
+                        */
+                       s = field[0].start;
+                       len = field[0].end - field[0].start;
+                       if (*s == L'd' && (len == 1 || (len >= 7
+                           && wmemcmp((s + 1), L"efault", 6) == 0))) {
+                               type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+                               if (len > 7)
+                                       field[0].start += 7;
+                               else
+                                       n = 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;
+                               type = want_type;
+
+                       /* Check for a numeric ID in field n+1 or n+3. */
+                       isint_w(field[n + 1].start, field[n + 1].end, &id);
+                       /* Field n+3 is optional. */
+                       if (id == -1 && fields > n+3)
+                               isint_w(field[n + 3].start, field[n + 3].end,
+                                   &id);
+
+                       tag = 0;
+                       s = field[n].start;
+                       st = field[n].start + 1;
+                       len = field[n].end - field[n].start;
+
+                       switch (*s) {
+                       case L'u':
+                               if (len == 1 || (len == 4
+                                   && wmemcmp(st, L"ser", 3) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+                               break;
+                       case L'g':
+                               if (len == 1 || (len == 5
+                                   && wmemcmp(st, L"roup", 4) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                               break;
+                       case L'o':
+                               if (len == 1 || (len == 5
+                                   && wmemcmp(st, L"ther", 4) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_OTHER;
+                               break;
+                       case L'm':
+                               if (len == 1 || (len == 4
+                                   && wmemcmp(st, L"ask", 3) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_MASK;
+                               break;
+                       default:
+                                       break;
+                       }
+
+                       switch (tag) {
+                       case ARCHIVE_ENTRY_ACL_OTHER:
+                       case ARCHIVE_ENTRY_ACL_MASK:
+                               if (fields == (n + 2)
+                                   && field[n + 1].start < field[n + 1].end
+                                   && ismode_w(field[n + 1].start,
+                                   field[n + 1].end, &permset)) {
+                                       /* This is Solaris-style "other:rwx" */
+                               } else if (fields == (n + 3) &&
+                                   field[n + 1].start < field[n + 1].end) {
+                                       /* Invalid mask or other field */
+                                       ret = ARCHIVE_WARN;
+                                       continue;
+                               }
+                               break;
+                       case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                               if (id != -1 ||
+                                   field[n + 1].start < field[n + 1].end) {
+                                       name = field[n + 1];
+                                       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+                                               tag = ARCHIVE_ENTRY_ACL_USER;
+                                       else
+                                               tag = ARCHIVE_ENTRY_ACL_GROUP;
+                               }
+                               break;
+                       default:
+                               /* Invalid tag, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+
+                       /* Without "default:" we expect mode in field 2 */
+                       if (permset == 0 && !ismode_w(field[n + 2].start,
+                           field[n + 2].end, &permset)) {
+                               /* Invalid mode, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+               } else {
+                       /* NFS4 ACLs */
+                       s = field[0].start;
+                       len = field[0].end - field[0].start;
+                       tag = 0;
+
+                       switch (len) {
+                       case 4:
+                               if (wmemcmp(s, L"user", 4) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_USER;
+                               break;
+                       case 5:
+                               if (wmemcmp(s, L"group", 5) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_GROUP;
+                               break;
+                       case 6:
+                               if (wmemcmp(s, L"owner@", 6) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+                               else if (wmemcmp(s, L"group@", len) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                               break;
+                       case 9:
+                               if (wmemcmp(s, L"everyone@", 9) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+                       default:
+                               break;
+                       }
+
+                       if (tag == 0) {
+                               /* Invalid tag, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+                           tag == ARCHIVE_ENTRY_ACL_GROUP) {
+                               n = 1;
                                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);
+                               n = 0;
+
+                       if (!is_nfs4_perms_w(field[1 + n].start,
+                           field[1 + n].end, &permset)) {
+                               /* Invalid NFSv4 perms, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+                       if (!is_nfs4_flags_w(field[2 + n].start,
+                           field[2 + n].end, &permset)) {
+                               /* Invalid NFSv4 flags, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+                       s = field[3 + n].start;
+                       len = field[3 + n].end - field[3 + n].start;
+                       type = 0;
+                       if (len == 4) {
+                               if (wmemcmp(s, L"deny", 4) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+                       } else if (len == 5) {
+                               if (wmemcmp(s, L"allow", 5) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+                               else if (wmemcmp(s, L"audit", 5) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+                               else if (wmemcmp(s, L"alarm", 5) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+                       }
+                       if (type == 0) {
+                               /* Invalid entry type, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+                       isint_w(field[4 + n].start, field[4 + n].end, &id);
+               }
 
                /* Add entry to the internal list. */
-               archive_acl_add_entry_w_len(acl, type, permset,
+               r = archive_acl_add_entry_w_len(acl, type, permset,
                    tag, id, name.start, name.end - name.start);
+               if (r < ARCHIVE_WARN)
+                       return (r);
+               if (r != ARCHIVE_OK)
+                       ret = ARCHIVE_WARN;
+               types |= type;
        }
-       return (ARCHIVE_OK);
+
+       /* Reset ACL */
+       archive_acl_reset(acl, types);
+
+       return (ret);
 }
 
 /*
@@ -977,16 +1424,128 @@ ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
        *permset = 0;
        while (p < end) {
                switch (*p++) {
-               case 'r': case 'R':
+               case L'r': case L'R':
                        *permset |= ARCHIVE_ENTRY_ACL_READ;
                        break;
-               case 'w': case 'W':
+               case L'w': case L'W':
                        *permset |= ARCHIVE_ENTRY_ACL_WRITE;
                        break;
-               case 'x': case 'X':
+               case L'x': case L'X':
                        *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
                        break;
-               case '-':
+               case L'-':
+                       break;
+               default:
+                       return (0);
+               }
+       }
+       return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+       const wchar_t *p;
+
+       if (start >= end)
+               return (0);
+       p = start;
+       while (p < end) {
+               switch (*p++) {
+               case L'r':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+                       break;
+               case L'w':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+                       break;
+               case L'x':
+                       *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+                       break;
+               case L'p':
+                       *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+                       break;
+               case L'D':
+                       *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+                       break;
+               case L'd':
+                       *permset |= ARCHIVE_ENTRY_ACL_DELETE;
+                       break;
+               case L'a':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+                       break;
+               case L'A':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+                       break;
+               case L'R':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+                       break;
+               case L'W':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+                       break;
+               case L'c':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+                       break;
+               case L'C':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+                       break;
+               case L'o':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+                       break;
+               case L's':
+                       *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+                       break;
+               case L'-':
+                       break;
+               default:
+                       return(0);
+               }
+       }
+       return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+       const wchar_t *p;
+
+       if (start >= end)
+               return (0);
+       p = start;
+       while (p < end) {
+               switch(*p++) {
+               case L'f':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+                       break;
+               case L'd':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+                       break;
+               case L'i':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+                       break;
+               case L'n':
+                       *permset |=
+                           ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+                       break;
+               case L'S':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+                       break;
+               case L'F':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+                       break;
+               case L'I':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+                       break;
+               case L'-':
                        break;
                default:
                        return (0);
@@ -1033,46 +1592,48 @@ next_field_w(const wchar_t **wp, const wchar_t **start,
 }
 
 /*
- * 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);
-}
-
-/*
- * 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:".
+ * Parse an ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
  */
 int
-archive_acl_parse_l(struct archive_acl *acl,
-    const char *text, int default_type, struct archive_string_conv *sc)
+archive_acl_from_text_l(struct archive_acl *acl, const char *text,
+    int want_type, struct archive_string_conv *sc)
 {
        struct {
                const char *start;
                const char *end;
-       } field[4], name;
+       } field[6], name;
 
-       int fields, n, r, ret = ARCHIVE_OK;
-       int type, tag, permset, id;
+       const char *s, *st;
+       int numfields, fields, n, r, ret;
+       int type, types, tag, permset, id;
+       size_t len;
        char sep;
 
+       switch (want_type) {
+       case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+               want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+       case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+       case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+               numfields = 5;
+               break;
+       case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+               numfields = 6;
+               break;
+       default:
+               return (ARCHIVE_FATAL);
+       }
+
+       ret = ARCHIVE_OK;
+       types = 0;
+
        while (text != NULL  &&  *text != '\0') {
                /*
                 * Parse the fields out of the next entry,
@@ -1082,7 +1643,7 @@ archive_acl_parse_l(struct archive_acl *acl,
                do {
                        const char *start, *end;
                        next_field(&text, &start, &end, &sep);
-                       if (fields < 4) {
+                       if (fields < numfields) {
                                field[fields].start = start;
                                field[fields].end = end;
                        }
@@ -1090,72 +1651,191 @@ archive_acl_parse_l(struct archive_acl *acl,
                } while (sep == ':');
 
                /* Set remaining fields to blank. */
-               for (n = fields; n < 4; ++n)
+               for (n = fields; n < numfields; ++n)
                        field[n].start = field[n].end = NULL;
 
-               /* Check for a numeric ID in field 1 or 3. */
-               id = -1;
-               isint(field[1].start, field[1].end, &id);
-               /* Field 3 is optional. */
-               if (id == -1 && fields > 3)
-                       isint(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
-                   && memcmp(field[0].start, "default", 7) == 0) {
-                       type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
-                       field[0].start += 7;
-               } else
-                       type = default_type;
+               if (field[0].start != NULL && *(field[0].start) == '#') {
+                       /* Comment, skip entry */
+                       continue;
+               }
 
+               n = 0;
+               id = -1;
+               permset = 0;
                name.start = name.end = NULL;
-               if (prefix_c(field[0].start, field[0].end, "user")) {
-                       if (!ismode(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];
+
+               if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+                       /* POSIX.1e ACLs */
+                       /*
+                        * Default keyword "default:user::rwx"
+                        * if found, we have one more field
+                        *
+                        * We also support old Solaris extension:
+                        * "defaultuser::rwx" is the default ACL corresponding
+                        * to "user::rwx", etc. valid only for first field
+                        */
+                       s = field[0].start;
+                       len = field[0].end - field[0].start;
+                       if (*s == 'd' && (len == 1 || (len >= 7
+                           && memcmp((s + 1), "efault", 6) == 0))) {
+                               type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+                               if (len > 7)
+                                       field[0].start += 7;
+                               else
+                                       n = 1;
                        } else
-                               tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-               } else if (prefix_c(field[0].start, field[0].end, "group")) {
-                       if (!ismode(field[2].start, field[2].end, &permset))
-                               return (ARCHIVE_WARN);
-                       if (id != -1 || field[1].start < field[1].end) {
-                               tag = ARCHIVE_ENTRY_ACL_GROUP;
+                               type = want_type;
+
+                       /* Check for a numeric ID in field n+1 or n+3. */
+                       isint(field[n + 1].start, field[n + 1].end, &id);
+                       /* Field n+3 is optional. */
+                       if (id == -1 && fields > (n + 3))
+                               isint(field[n + 3].start, field[n + 3].end,
+                                   &id);
+
+                       tag = 0;
+                       s = field[n].start;
+                       st = field[n].start + 1;
+                       len = field[n].end - field[n].start;
+
+                       switch (*s) {
+                       case 'u':
+                               if (len == 1 || (len == 4
+                                   && memcmp(st, "ser", 3) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+                               break;
+                       case 'g':
+                               if (len == 1 || (len == 5
+                                   && memcmp(st, "roup", 4) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                               break;
+                       case 'o':
+                               if (len == 1 || (len == 5
+                                   && memcmp(st, "ther", 4) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_OTHER;
+                               break;
+                       case 'm':
+                               if (len == 1 || (len == 4
+                                   && memcmp(st, "ask", 3) == 0))
+                                       tag = ARCHIVE_ENTRY_ACL_MASK;
+                               break;
+                       default:
+                                       break;
+                       }
+
+                       switch (tag) {
+                       case ARCHIVE_ENTRY_ACL_OTHER:
+                       case ARCHIVE_ENTRY_ACL_MASK:
+                               if (fields == (n + 2)
+                                   && field[n + 1].start < field[n + 1].end
+                                   && ismode(field[n + 1].start,
+                                   field[n + 1].end, &permset)) {
+                                       /* This is Solaris-style "other:rwx" */
+                               } else if (fields == (n + 3) &&
+                                   field[n + 1].start < field[n + 1].end) {
+                                       /* Invalid mask or other field */
+                                       ret = ARCHIVE_WARN;
+                                       continue;
+                               }
+                               break;
+                       case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                               if (id != -1 ||
+                                   field[n + 1].start < field[n + 1].end) {
+                                       name = field[n + 1];
+                                       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+                                               tag = ARCHIVE_ENTRY_ACL_USER;
+                                       else
+                                               tag = ARCHIVE_ENTRY_ACL_GROUP;
+                               }
+                               break;
+                       default:
+                               /* Invalid tag, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+
+                       /* Without "default:" we expect mode in field 2 */
+                       if (permset == 0 && !ismode(field[n + 2].start,
+                           field[n + 2].end, &permset)) {
+                               /* Invalid mode, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+               } else {
+                       /* NFS4 ACLs */
+                       s = field[0].start;
+                       len = field[0].end - field[0].start;
+                       tag = 0;
+
+                       switch (len) {
+                       case 4:
+                               if (memcmp(s, "user", 4) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_USER;
+                               break;
+                       case 5:
+                               if (memcmp(s, "group", 5) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_GROUP;
+                               break;
+                       case 6:
+                               if (memcmp(s, "owner@", 6) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+                               else if (memcmp(s, "group@", 6) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                               break;
+                       case 9:
+                               if (memcmp(s, "everyone@", 9) == 0)
+                                       tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+                               break;
+                       default:
+                               break;
+                       }
+
+                       if (tag == 0) {
+                               /* Invalid tag, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+                           tag == ARCHIVE_ENTRY_ACL_GROUP) {
+                               n = 1;
                                name = field[1];
                        } else
-                               tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-               } else if (prefix_c(field[0].start, field[0].end, "other")) {
-                       if (fields == 2
-                           && field[1].start < field[1].end
-                           && ismode(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(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_c(field[0].start, field[0].end, "mask")) {
-                       if (fields == 2
-                           && field[1].start < field[1].end
-                           && ismode(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(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);
+                               n = 0;
+
+                       if (!is_nfs4_perms(field[1 + n].start,
+                           field[1 + n].end, &permset)) {
+                               /* Invalid NFSv4 perms, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+                       if (!is_nfs4_flags(field[2 + n].start,
+                           field[2 + n].end, &permset)) {
+                               /* Invalid NFSv4 flags, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+                       s = field[3 + n].start;
+                       len = field[3 + n].end - field[3 + n].start;
+                       type = 0;
+                       if (len == 4) {
+                               if (memcmp(s, "deny", 4) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+                       } else if (len == 5) {
+                               if (memcmp(s, "allow", 5) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+                               else if (memcmp(s, "audit", 5) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+                               else if (memcmp(s, "alarm", 5) == 0)
+                                       type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+                       }
+                       if (type == 0) {
+                               /* Invalid entry type, skip entry */
+                               ret = ARCHIVE_WARN;
+                               continue;
+                       }
+                       isint(field[4 + n].start, field[4 + n].end,
+                           &id);
+               }
 
                /* Add entry to the internal list. */
                r = archive_acl_add_entry_len_l(acl, type, permset,
@@ -1164,7 +1844,12 @@ archive_acl_parse_l(struct archive_acl *acl,
                        return (r);
                if (r != ARCHIVE_OK)
                        ret = ARCHIVE_WARN;
+               types |= type;
        }
+
+       /* Reset ACL */
+       archive_acl_reset(acl, types);
+
        return (ret);
 }
 
@@ -1229,6 +1914,118 @@ ismode(const char *start, const char *end, int *permset)
        return (1);
 }
 
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms(const char *start, const char *end, int *permset)
+{
+       const char *p;
+
+       if (start >= end)
+               return (0);
+       p = start;
+       while (p < end) {
+               switch (*p++) {
+               case 'r':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+                       break;
+               case 'w':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+                       break;
+               case 'x':
+                       *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+                       break;
+               case 'p':
+                       *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+                       break;
+               case 'D':
+                       *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+                       break;
+               case 'd':
+                       *permset |= ARCHIVE_ENTRY_ACL_DELETE;
+                       break;
+               case 'a':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+                       break;
+               case 'A':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+                       break;
+               case 'R':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+                       break;
+               case 'W':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+                       break;
+               case 'c':
+                       *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+                       break;
+               case 'C':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+                       break;
+               case 'o':
+                       *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+                       break;
+               case 's':
+                       *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+                       break;
+               case '-':
+                       break;
+               default:
+                       return(0);
+               }
+       }
+       return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags(const char *start, const char *end, int *permset)
+{
+       const char *p;
+
+       if (start >= end)
+               return (0);
+       p = start;
+       while (p < end) {
+               switch(*p++) {
+               case 'f':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+                       break;
+               case 'd':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+                       break;
+               case 'i':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+                       break;
+               case 'n':
+                       *permset |=
+                           ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+                       break;
+               case 'S':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+                       break;
+               case 'F':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+                       break;
+               case 'I':
+                       *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+                       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
@@ -1264,25 +2061,3 @@ next_field(const char **p, const char **start,
        if (**p != '\0')
                (*p)++;
 }
-
-/*
- * 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_c(const char *start, const char *end, const char *test)
-{
-       if (start == end)
-               return (0);
-
-       if (*start++ != *test++)
-               return (0);
-
-       while (start < end  &&  *start++ == *test++)
-               ;
-
-       if (start < end)
-               return (0);
-
-       return (1);
-}
index 1421adbf8a23b2575369cfbdeffa3dcd1b140a22..0aac6fff2d9d284db908f21f8b74f9d4ca75c989 100644 (file)
@@ -66,22 +66,17 @@ int archive_acl_add_entry_w_len(struct archive_acl *,
 int archive_acl_add_entry_len(struct archive_acl *,
     int, int, int, int, const char *, size_t);
 
-const wchar_t *archive_acl_text_w(struct archive *, struct archive_acl *, int);
-int archive_acl_text_l(struct archive_acl *, int, const char **, size_t *,
+wchar_t *archive_acl_to_text_w(struct archive_acl *, ssize_t *, int,
+    struct archive *);
+char *archive_acl_to_text_l(struct archive_acl *, ssize_t *, int,
     struct archive_string_conv *);
 
 /*
- * 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.
+ * ACL text parser.
  */
-int archive_acl_parse_w(struct archive_acl *,
-                   const wchar_t *, int /* type */);
-int archive_acl_parse_l(struct archive_acl *,
-                   const char *, int /* type */,
-                   struct archive_string_conv *);
+int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */,
+    int /* type */);
+int archive_acl_from_text_l(struct archive_acl *, const char * /* text */,
+    int /* type */, struct archive_string_conv *);
 
 #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
index b05eb4d2247707a5112f2d434f06bd49ae2c72f0..5af0307298950868785ca3c5372d9fe22f22121d 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1486,34 +1487,121 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
 }
 
 /*
- * Generate a text version of the ACL.  The flags parameter controls
+ * Generate a text version of the ACL. The flags parameter controls
  * the style of the generated ACL.
  */
+wchar_t *
+archive_entry_acl_to_text_w(struct archive_entry *entry, ssize_t *len,
+    int flags)
+{
+       return (archive_acl_to_text_w(&entry->acl, len, flags,
+           entry->archive));
+}
+
+char *
+archive_entry_acl_to_text(struct archive_entry *entry, ssize_t *len,
+    int flags)
+{
+       return (archive_acl_to_text_l(&entry->acl, len, flags, NULL));
+}
+
+char *
+_archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len,
+   int flags, struct archive_string_conv *sc)
+{
+       return (archive_acl_to_text_l(&entry->acl, len, flags, sc));
+}
+
+/*
+ * ACL text parser.
+ */
+int
+archive_entry_acl_from_text_w(struct archive_entry *entry,
+    const wchar_t *wtext, int type)
+{
+       return (archive_acl_from_text_w(&entry->acl, wtext, type));
+}
+
+int
+archive_entry_acl_from_text(struct archive_entry *entry,
+    const char *text, int type)
+{
+       return (archive_acl_from_text_l(&entry->acl, text, type, NULL));
+}
+
+int
+_archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text,
+    int type, struct archive_string_conv *sc)
+{
+       return (archive_acl_from_text_l(&entry->acl, text, type, sc));
+}
+
+/* Deprecated */
+static int
+archive_entry_acl_text_compat(int *flags)
+{
+       if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0)
+               return (1);
+
+       /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */
+       if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0)
+               *flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID;
+
+       /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */
+       if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+               *flags |=  ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+       *flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA;
+
+       return (0);
+}
+
+/* Deprecated */
 const wchar_t *
 archive_entry_acl_text_w(struct archive_entry *entry, int flags)
 {
-       const wchar_t *r;
-       r = archive_acl_text_w(entry->archive, &entry->acl, flags);
-       if (r == NULL && errno == ENOMEM)
-               __archive_errx(1, "No memory");
-       return (r);
+       if (entry->acl.acl_text_w != NULL) {
+               free(entry->acl.acl_text_w);
+               entry->acl.acl_text_w = NULL;
+       }
+       if (archive_entry_acl_text_compat(&flags) == 0)
+               entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl,
+                   NULL, flags, entry->archive);
+       return (entry->acl.acl_text_w);
 }
 
+/* Deprecated */
 const char *
 archive_entry_acl_text(struct archive_entry *entry, int flags)
 {
-       const char *p;
-       if (archive_acl_text_l(&entry->acl, flags, &p, NULL, NULL) != 0
-           && errno == ENOMEM)
-               __archive_errx(1, "No memory");
-       return (p);
+       if (entry->acl.acl_text != NULL) {
+               free(entry->acl.acl_text);
+               entry->acl.acl_text = NULL;
+       }
+       if (archive_entry_acl_text_compat(&flags) == 0)
+               entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL,
+                   flags, NULL);
+
+       return (entry->acl.acl_text);
 }
 
+/* Deprecated */
 int
 _archive_entry_acl_text_l(struct archive_entry *entry, int flags,
     const char **acl_text, size_t *len, struct archive_string_conv *sc)
 {
-       return (archive_acl_text_l(&entry->acl, flags, acl_text, len, sc));
+       if (entry->acl.acl_text != NULL) {
+               free(entry->acl.acl_text);
+               entry->acl.acl_text = NULL;
+        }
+
+       if (archive_entry_acl_text_compat(&flags) == 0)
+               entry->acl.acl_text = archive_acl_to_text_l(&entry->acl,
+                   (ssize_t *)len, flags, sc);
+
+       *acl_text = entry->acl.acl_text;
+
+       return (0);
 }
 
 /*
index b6a5d89c5d6fa721708c73594e394fb2463fbf0a..0f09b985029b0ec425135d8dc76680bac7487957 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2008 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -420,6 +421,7 @@ __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const voi
 /*
  * Inheritance values (NFS4 ACLs only); included in permset.
  */
+#define        ARCHIVE_ENTRY_ACL_ENTRY_INHERITED                   0x01000000
 #define        ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT                0x02000000
 #define        ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT           0x04000000
 #define        ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT        0x08000000
@@ -433,15 +435,16 @@ __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const voi
            | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT      \
            | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY              \
            | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS         \
-           | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS)
+           | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS             \
+           | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)
 
 /* We need to be able to specify combinations of these. */
-#define        ARCHIVE_ENTRY_ACL_TYPE_ACCESS   256  /* POSIX.1e only */
-#define        ARCHIVE_ENTRY_ACL_TYPE_DEFAULT  512  /* POSIX.1e only */
-#define        ARCHIVE_ENTRY_ACL_TYPE_ALLOW    1024 /* NFS4 only */
-#define        ARCHIVE_ENTRY_ACL_TYPE_DENY     2048 /* NFS4 only */
-#define        ARCHIVE_ENTRY_ACL_TYPE_AUDIT    4096 /* NFS4 only */
-#define        ARCHIVE_ENTRY_ACL_TYPE_ALARM    8192 /* NFS4 only */
+#define        ARCHIVE_ENTRY_ACL_TYPE_ACCESS   0x00000100  /* POSIX.1e only */
+#define        ARCHIVE_ENTRY_ACL_TYPE_DEFAULT  0x00000200  /* POSIX.1e only */
+#define        ARCHIVE_ENTRY_ACL_TYPE_ALLOW    0x00000400 /* NFS4 only */
+#define        ARCHIVE_ENTRY_ACL_TYPE_DENY     0x00000800 /* NFS4 only */
+#define        ARCHIVE_ENTRY_ACL_TYPE_AUDIT    0x00001000 /* NFS4 only */
+#define        ARCHIVE_ENTRY_ACL_TYPE_ALARM    0x00002000 /* NFS4 only */
 #define        ARCHIVE_ENTRY_ACL_TYPE_POSIX1E  (ARCHIVE_ENTRY_ACL_TYPE_ACCESS \
            | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
 #define        ARCHIVE_ENTRY_ACL_TYPE_NFS4     (ARCHIVE_ENTRY_ACL_TYPE_ALLOW \
@@ -492,21 +495,43 @@ __LA_DECL int      archive_entry_acl_next_w(struct archive_entry *, int /* want_type
  * Construct a text-format ACL.  The flags argument is a bitmask that
  * can include any of the following:
  *
+ * Flags only for archive entries with POSIX.1e ACL:
  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
- * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - Include NFS4 entries.
- * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
- *    each ACL entry.  ('star' introduced this for POSIX.1e, this flag
- *    also applies to NFS4.)
  * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
- *    default ACL entry, as used in old Solaris ACLs.
+ *    default ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and
+ *    "mask" entries.
+ *
+ * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ *    each ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma
+ *    instead of newline.
  */
-#define        ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID        1024
-#define        ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT    2048
+#define        ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID        0x00000001
+#define        ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT    0x00000002
+#define        ARCHIVE_ENTRY_ACL_STYLE_SOLARIS         0x00000004
+#define        ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA 0x00000008
+
+__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,
+           ssize_t * /* len */, int /* flags */);
+__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,
+           ssize_t * /* len */, int /* flags */);
+__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,
+           const wchar_t * /* wtext */, int /* type */);
+__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,
+           const char * /* text */, int /* type */);
+
+/* Deprecated constants */
+#define        OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID            1024
+#define        OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT        2048
+
+/* Deprecated functions */
 __LA_DECL const wchar_t        *archive_entry_acl_text_w(struct archive_entry *,
-                   int /* flags */);
+                   int /* flags */) __attribute__ ((deprecated));
 __LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
-                   int /* flags */);
+                   int /* flags */) __attribute__ ((deprecated));
 
 /* Return bitmask of ACL types in an archive entry */
 __LA_DECL int   archive_entry_acl_types(struct archive_entry *);
index e85c4ded162781e701e13d2fa16b64f1a2d6271a..93707d1ff05498d0f82d1de873979251ec2445ac 100644 (file)
@@ -1,4 +1,5 @@
 .\" Copyright (c) 2010 Joerg Sonnenberger
+.\" Copyright (c) 2016 Martin Matuska
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -22,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd February 2, 2012
+.Dd December 27, 2016
 .Dt ARCHIVE_ENTRY_ACL 3
 .Os
 .Sh NAME
 .Nm archive_entry_acl_add_entry_w ,
 .Nm archive_entry_acl_clear ,
 .Nm archive_entry_acl_count ,
+.Nm archive_entry_acl_from_text ,
+.Nm archive_entry_acl_from_text_w,
 .Nm archive_entry_acl_next ,
 .Nm archive_entry_acl_next_w ,
 .Nm archive_entry_acl_reset ,
-.Nm archive_entry_acl_text_w ,
+.Nm archive_entry_acl_to_text ,
+.Nm archive_entry_acl_to_text_w ,
 .Nm archive_entry_acl_types
 .Nd functions for manipulating Access Control Lists in archive entry descriptions
 .Sh LIBRARY
@@ -63,6 +67,18 @@ Streaming Archive Library (libarchive, -larchive)
 .Ft int
 .Fn archive_entry_acl_count "struct archive_entry *a" "int type"
 .Ft int
+.Fo archive_entry_acl_from_text
+.Fa "struct archive_entry *a"
+.Fa "const char *text"
+.Fa "int type"
+.Fc
+.Ft int
+.Fo archive_entry_acl_from_text_w
+.Fa "struct archive_entry *a"
+.Fa "const wchar_t *text"
+.Fa "int type"
+.Fc
+.Ft int
 .Fo archive_entry_acl_next
 .Fa "struct archive_entry *a"
 .Fa "int type"
@@ -84,33 +100,48 @@ Streaming Archive Library (libarchive, -larchive)
 .Fc
 .Ft int
 .Fn archive_entry_acl_reset "struct archive_entry *a" "int type"
-.Ft const wchar_t *
-.Fn archive_entry_acl_text_w "struct archive_entry *a" "int flags"
+.Ft char *
+.Fo archive_entry_acl_to_text
+.Fa "struct archive_entry *a"
+.Fa "ssize_t *len_p"
+.Fa "int flags"
+.Fc
+.Ft wchar_t *
+.Fo archive_entry_acl_to_text_w
+.Fa "struct archive_entry *a"
+.Fa "ssize_t *len_p"
+.Fa "int flags"
+.Fc
 .Ft int
 .Fn archive_entry_acl_types "struct archive_entry *a"
 .\" enum?
 .Sh DESCRIPTION
-An
-.Dq Access Control List
-is a generalisation of the classic Unix permission system.
+The
+.Dq Access Control Lists (ACLs)
+extend the standard Unix perssion model.
 The ACL interface of
 .Nm libarchive
-is derived from the POSIX.1e draft, but restricted to simplify dealing
-with practical implementations in various Operating Systems and archive formats.
-.Pp
-An ACL consists of a number of independent entries.
+supports both POSIX.1e and NFSv4 style ACLs. Use of ACLs is restricted by
+various levels of ACL support in operating systems, file systems and archive
+formats.
+.Ss POSIX.1e Access Control Lists
+A POSIX.1e ACL consists of a number of independent entries.
 Each entry specifies the permission set as bitmask of basic permissions.
-Valid permissions are:
+Valid permissions in the
+.Fa permset
+are:
 .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_EXECUTE"
-.It Dv ARCHIVE_ENTRY_ACL_EXECUTE
-.It Dv ARCHIVE_ENTRY_ACL_WRITE
-.It Dv ARCHIVE_ENTRY_ACL_READ
+.It Dv ARCHIVE_ENTRY_ACL_READ ( Sy r )
+.It Dv ARCHIVE_ENTRY_ACL_WRITE ( Sy w )
+.It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x )
 .El
 The permissions correspond to the normal Unix permissions.
 .Pp
-The tag specifies the principal to which the permission applies.
+The
+.Fa tag
+specifies the principal to which the permission applies.
 Valid values are:
-.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ"
+.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ"
 .It Dv ARCHIVE_ENTRY_ACL_USER
 The user specified by the name field.
 .It Dv ARCHIVE_ENTRY_ACL_USER_OBJ
@@ -122,8 +153,9 @@ The group who owns the file.
 .It Dv ARCHIVE_ENTRY_ACL_MASK
 The maximum permissions to be obtained via group permissions.
 .It Dv ARCHIVE_ENTRY_ACL_OTHER
-Any principal who doesn't have a user or group entry.
+Any principal who is not file owner or a member of the owning group.
 .El
+.Pp
 The principals
 .Dv ARCHIVE_ENTRY_ACL_USER_OBJ ,
 .Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ
@@ -132,19 +164,123 @@ and
 are equivalent to user, group and other in the classic Unix permission
 model and specify non-extended ACL entries.
 .Pp
-All files have an access ACL
+All files with have an access ACL
 .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS .
 This specifies the permissions required for access to the file itself.
 Directories have an additional ACL
 .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT ,
 which controls the initial access ACL for newly created directory entries.
+.Ss NFSv4 Access Control Lists
+A NFSv4 ACL consists of multiple individual entries called Access Control
+Entries (ACEs).
+.Pp
+There are four possible types of a NFSv4 ACE:
+.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYE_ALLOW"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW
+Allow principal to perform actions requiring given permissions.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY
+Prevent principal from performing actions requiring given permissions.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT
+Log access attempts by principal which require given permissions.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM
+Trigger a system alarm on access attempts by principal which require given
+permissions.
+.El
+.Pp
+The
+.Fa tag
+specifies the principal to which the permission applies.
+Valid values are:
+.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ"
+.It Dv ARCHIVE_ENTRY_ACL_USER
+The user specified by the name field.
+.It Dv ARCHIVE_ENTRY_ACL_USER_OBJ
+The owner of the file.
+.It Dv ARCHIVE_ENTRY_ACL_GROUP
+The group specied by the name field.
+.It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ
+The group who owns the file.
+.It Dv ARCHIVE_ENTRY_ACL_EVERYONE
+Any principal who is not file owner or a member of the owning group.
+.El
+.Pp
+Entries with the
+.Dv ARCHIVE_ENTRY_ACL_USER
+or
+.Dv ARCHIVE_ENTRY_ACL_GROUP
+tag store the user and group name in the
+.Fa name
+string and optionally the user or group ID in the
+.Fa qualifier
+integer.
 .Pp
+NFSv4 ACE permissions and flags are stored in the same
+.Fa permset
+bitfield. Some permissions share the same constant and permission character but
+have different effect on directories than on files. The following ACE
+permissions are supported:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_READ_DATA ( Sy r )
+Read data (file).
+.It Dv ARCHIVE_ENTRY_ACL_LIST_DIRECTORY ( Sy r )
+List entries (directory).
+.It ARCHIVE_ENTRY_ACL_WRITE_DATA ( Sy w )
+Write data (file).
+.It ARCHIVE_ENTRY_ACL_ADD_FILE ( Sy w )
+Create files (directory).
+.It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x )
+Execute file or change into a directory.
+.It Dv ARCHIVE_ENTRY_ACL_APPEND_DATA ( Sy p )
+Append data (file).
+.It Dv ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY ( Sy p )
+Create subdirectories (directory).
+.It Dv ARCHIVE_ENTRY_ACL_DELETE_CHILD ( Sy D )
+Remove files and subdirectories inside a directory.
+.It Dv ARCHIVE_ENTRY_ACL_DELETE ( Sy d )
+Remove file or directory.
+.It Dv ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES ( Sy a )
+Read file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES ( Sy A )
+Write file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS ( Sy R )
+Read named file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS ( Sy W )
+Write named file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_READ_ACL ( Sy c )
+Read file or directory ACL.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_ACL ( Sy C )
+Write file or directory ACL.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_OWNER ( Sy o )
+Change owner of a file or directory.
+.It Dv ARCHIVE_ENTRY_ACL_SYNCHRONIZE ( Sy s )
+Use synchronous I/O.
+.El
+.Pp
+The following NFSv4 ACL inheritance flags are supported:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT ( Sy f )
+Inherit parent directory ACE to files.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT ( Sy d )
+Inherit parent directory ACE to subdirectories.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY ( Sy i )
+Only inherit, do not apply the permission on the directory itself.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT ( Sy n )
+Do not propagate inherit flags. Only first-level entries inherit ACLs.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS ( Sy S )
+Trigger alarm or audit on succesful access.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS ( Sy F )
+Trigger alarm or audit on failed access.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERITED ( Sy I )
+Mark that ACE was inherited.
+.El
+.Ss Functions
 .Fn archive_entry_acl_add_entry
 and
 .Fn archive_entry_acl_add_entry_w
 add a single ACL entry.
 For the access ACL and non-extended principals, the classic Unix permissions
-are updated.
+are updated. An archive enry cannot contain both POSIX.1e and NFSv4 ACL
+entries.
 .Pp
 .Fn archive_entry_acl_clear
 removes all ACL entries and resets the enumeration pointer.
@@ -153,14 +289,58 @@ removes all ACL entries and resets the enumeration pointer.
 counts the ACL entries that have the given type mask.
 .Fa type
 can be the bitwise-or of
-.Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
-and
-.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT .
-If
+.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+.El
+for POSIX.1e ACLs and
+.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_ALLOW"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM
+.El
+for NFSv4 ACLs. For POSIX.1e ACLs if
 .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 is included and at least one extended ACL entry is found,
 the three non-extened ACLs are added.
 .Pp
+.Fn archive_entry_acl_from_text
+and
+.Fn archive_entry_acl_from_text_w
+add new
+.Pq or merge with existing
+ACL entries from
+.Pq wide
+text. The argument
+.Fa type
+may take one of the following values:
+.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4
+.El
+Supports all formats that can be created with
+.Fn archive_entry_acl_to_text
+or respective
+.Fn archive_entry_acl_to_text_w .
+Existing ACL entries are preserved. To get a clean new ACL from text
+.Fn archive_entry_acl_clear
+must be called first. Entries prefixed with
+.Dq default:
+are treated as
+.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+unless
+.Fa type
+is
+.Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4 .
+Invalid entries, non-parseable ACL entries and entries beginning with
+the
+.Sq #
+character
+.Pq comments
+are skipped.
+.Pp
 .Fn archive_entry_acl_next
 and
 .Fn archive_entry_acl_next_w
@@ -182,19 +362,50 @@ or set using
 Otherwise, the function returns the same value as
 .Fn archive_entry_acl_count .
 .Pp
-.Fn archive_entry_acl_text_w
-converts the ACL entries for the given type mask into a wide string.
-In addition to the normal type flags,
-.Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
+.Fn archive_entry_acl_to_text
 and
-.Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
-can be specified to further customize the result.
-The returned long string is valid until the next call to
-.Fn archive_entry_acl_clear ,
-.Fn archive_entry_acl_add_entry ,
-.Fn archive_entry_acl_add_entry_w
+.Fn archive_entry_acl_to_text_w
+convert the ACL entries for the given type into a
+.Pq wide
+string of ACL entries separated by newline. If the the pointer
+.Fa len_p
+is not NULL, then the function shall return the length of the string
+.Pq not including the NULL terminator
+in the location pointed to by
+.Fa len_p .
+The
+.Fa flag
+argument is a bitwise-or.
+.Pp
+The following flags are effective only on POSIX.1e ACL:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+Output access ACLs.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+Output POSIX.1e default ACLs.
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
+Prefix each default ACL entry with the word
+.Dq default: .
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_SOLARIS
+The mask and other ACLs don not contain a double colon.
+.El
+.Pp
+The following flags are effective on both POSIX.1e and NFSv4 ACL:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
+Add an additional colon-separated field containing the user or group id.
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA
+Separate ACL entries with comma instead of newline.
+.El
+.Pp
+If the archive entry contains NFSv4 ACLs, all types of NFSv4 ACLs are returned.
+It the entry contains POSIX.1e ACLs and none of the flags
+.Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 or
-.Fn archive_entry_acl_text_w .
+.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+are specified, both access and default entries are returned and default entries
+are prefixed with
+.Dq default: .
 .Pp
 .Fn archive_entry_acl_types
 get ACL entry types contained in an archive entry's ACL. As POSIX.1e and NFSv4
@@ -205,11 +416,20 @@ an ACL already contains POSIX.1e or NFSv4 ACL entries.
 and
 .Fn archive_entry_acl_reset
 returns the number of ACL entries that match the given type mask.
-If the type mask includes
+For POSIX.1e ACLS if the type mask includes
 .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 and at least one extended ACL entry exists, the three classic Unix
 permissions are counted.
 .Pp
+.Fn archive_entry_acl_from_text
+and
+.Fn archive_entry_acl_from_text_w
+return
+.Dv ARCHIVE_OK
+if all entries were successfully parsed and
+.Dv ARCHIVE_WARN
+if one or more entries were invalid or non-parseable.
+.Pp
 .Fn archive_entry_acl_next
 and
 .Fn archive_entry_acl_next_w
@@ -224,23 +444,16 @@ if
 .Fn archive_entry_acl_reset
 has not been called first.
 .Pp
-.Fn archive_entry_text_w
-returns a wide string representation of the ACL entrise matching the
-given type mask.
-The returned long string is valid until the next call to
-.Fn archive_entry_acl_clear ,
-.Fn archive_entry_acl_add_entry ,
-.Fn archive_entry_acl_add_entry_w
-or
-.Fn archive_entry_acl_text_w .
+.Fn archive_entry_acl_to_text
+returns a string representing the ACL entries matching the given type and
+flags on success or NULL on error.
+.Pp
+.Fn archive_entry_acl_to_text_w
+returns a wide string representing the ACL entries matching the given type
+and flags on success or NULL on error.
 .Pp
 .Fn archive_entry_acl_types
 returns a bitmask of ACL entry types or 0 if archive entry has no ACL entries.
 .Sh SEE ALSO
-.Xr archive_entry 3
-.Xr libarchive 3 ,
-.Sh BUGS
-.Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
-and
-.Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
-are not documented.
+.Xr archive_entry 3 ,
+.Xr libarchive 3
index 02e024ae20c1ac3a5e2b5e6103d890cba6bc9134..c7c90088822d1300f6daf66fd5f26fc4481f450b 100644 (file)
@@ -63,9 +63,14 @@ int _archive_entry_uname_l(struct archive_entry *,
     const char **, size_t *, struct archive_string_conv *);
 #define archive_entry_acl_text_l _archive_entry_acl_text_l
 int _archive_entry_acl_text_l(struct archive_entry *, int,
-    const char **, size_t *, struct archive_string_conv *);
-
-
+const char **, size_t *, struct archive_string_conv *)
+__attribute__ ((deprecated));
+#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l
+char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int,
+    struct archive_string_conv *);
+#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l
+int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text,
+    int type, struct archive_string_conv *);
 #define archive_entry_copy_gname_l     _archive_entry_copy_gname_l
 int _archive_entry_copy_gname_l(struct archive_entry *,
     const char *, size_t, struct archive_string_conv *);
index f37bb162a0f0b59169f586591194dbddb4d95f9f..0a5453c640ba32b2c328f22eb058af08edda7a24 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2009 Tim Kientzle
  * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -440,7 +441,7 @@ setup_acls(struct archive_read_disk *a,
        acl = NULL;
 
 #ifdef ACL_TYPE_NFS4
-       /* Try NFS4 ACL first. */
+       /* Try NFSv4 ACL first. */
        if (*fd >= 0)
 #if HAVE_ACL_GET_FD_NP
                acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);
index 948929a3d8179f052060f30e2acfd822611251fc..b18edcc12029b832ebbfbd400cf2202b4032f643 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -204,6 +205,10 @@ static int archive_read_format_tar_read_header(struct archive_read *,
 static int     checksum(struct archive_read *, const void *);
 static int     pax_attribute(struct archive_read *, struct tar *,
                    struct archive_entry *, const char *key, const char *value);
+static int     pax_attribute_acl(struct archive_read *, struct tar *,
+                   struct archive_entry *, const char *, int);
+static int     pax_attribute_xattr(struct archive_entry *, const char *,
+                   const char *);
 static int     pax_header(struct archive_read *, struct tar *,
                    struct archive_entry *, char *attr);
 static void    pax_time(const char *, int64_t *sec, long *nanos);
@@ -1016,7 +1021,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar,
                        return (ARCHIVE_FATAL);
        }
        archive_strncpy(&(tar->localname), acl, p - acl);
-       err = archive_acl_parse_l(archive_entry_acl(entry),
+       err = archive_acl_from_text_l(archive_entry_acl(entry),
            tar->localname.s, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, tar->sconv_acl);
        if (err != ARCHIVE_OK) {
                if (errno == ENOMEM) {
@@ -1758,6 +1763,52 @@ pax_attribute_xattr(struct archive_entry *entry,
        return 0;
 }
 
+static int
+pax_attribute_acl(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const char *value, int type)
+{
+       int r;
+       const char* errstr;
+
+       switch (type) {
+       case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+               errstr = "SCHILY.acl.access";
+               break;
+       case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+               errstr = "SCHILY.acl.default";
+               break;
+       case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+               errstr = "SCHILY.acl.ace";
+               break;
+       default:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Unknown ACL type: %d", type);
+               return(ARCHIVE_FATAL);
+       }
+
+       if (tar->sconv_acl == NULL) {
+               tar->sconv_acl =
+                   archive_string_conversion_from_charset(
+                       &(a->archive), "UTF-8", 1);
+               if (tar->sconv_acl == NULL)
+                       return (ARCHIVE_FATAL);
+       }
+
+       r = archive_acl_from_text_l(archive_entry_acl(entry), value, type,
+           tar->sconv_acl);
+       if (r != ARCHIVE_OK) {
+               if (r == ARCHIVE_FATAL) {
+                       archive_set_error(&a->archive, ENOMEM,
+                           "%s %s", "Can't allocate memory for ",
+                           errstr);
+                       return (r);
+               }
+               archive_set_error(&a->archive,
+                   ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr);
+       }
+       return (r);
+}
+
 /*
  * Parse a single key=value attribute.  key/value pointers are
  * assumed to point into reasonably long-lived storage.
@@ -1874,53 +1925,20 @@ pax_attribute(struct archive_read *a, struct tar *tar,
        case 'S':
                /* We support some keys used by the "star" archiver */
                if (strcmp(key, "SCHILY.acl.access") == 0) {
-                       if (tar->sconv_acl == NULL) {
-                               tar->sconv_acl =
-                                   archive_string_conversion_from_charset(
-                                       &(a->archive), "UTF-8", 1);
-                               if (tar->sconv_acl == NULL)
-                                       return (ARCHIVE_FATAL);
-                       }
-
-                       r = archive_acl_parse_l(archive_entry_acl(entry),
-                           value, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-                           tar->sconv_acl);
-                       if (r != ARCHIVE_OK) {
-                               err = r;
-                               if (err == ARCHIVE_FATAL) {
-                                       archive_set_error(&a->archive, ENOMEM,
-                                           "Can't allocate memory for "
-                                           "SCHILY.acl.access");
-                                       return (err);
-                               }
-                               archive_set_error(&a->archive,
-                                   ARCHIVE_ERRNO_MISC,
-                                   "Parse error: SCHILY.acl.access");
-                       }
+                       r = pax_attribute_acl(a, tar, entry, value,
+                           ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+                       if (r == ARCHIVE_FATAL)
+                               return (r);
                } else if (strcmp(key, "SCHILY.acl.default") == 0) {
-                       if (tar->sconv_acl == NULL) {
-                               tar->sconv_acl =
-                                   archive_string_conversion_from_charset(
-                                       &(a->archive), "UTF-8", 1);
-                               if (tar->sconv_acl == NULL)
-                                       return (ARCHIVE_FATAL);
-                       }
-
-                       r = archive_acl_parse_l(archive_entry_acl(entry),
-                           value, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
-                           tar->sconv_acl);
-                       if (r != ARCHIVE_OK) {
-                               err = r;
-                               if (err == ARCHIVE_FATAL) {
-                                       archive_set_error(&a->archive, ENOMEM,
-                                           "Can't allocate memory for "
-                                           "SCHILY.acl.default");
-                                       return (err);
-                               }
-                               archive_set_error(&a->archive,
-                                   ARCHIVE_ERRNO_MISC,
-                                   "Parse error: SCHILY.acl.default");
-                       }
+                       r = pax_attribute_acl(a, tar, entry, value,
+                           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+                       if (r == ARCHIVE_FATAL)
+                               return (r);
+               } else if (strcmp(key, "SCHILY.acl.ace") == 0) {
+                       r = pax_attribute_acl(a, tar, entry, value,
+                           ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+                       if (r == ARCHIVE_FATAL)
+                               return (r);
                } else if (strcmp(key, "SCHILY.devmajor") == 0) {
                        archive_entry_set_rdevmajor(entry,
                            (dev_t)tar_atol10(value, strlen(value)));
index 386d7451d91ddc2999b0b6daf007ac14f0d55f2f..0063d23e1d2a8783538c3cd6c23c359dfc33300b 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -70,6 +71,8 @@ static void            add_pax_attr_int(struct archive_string *,
 static void             add_pax_attr_time(struct archive_string *,
                             const char *key, int64_t sec,
                             unsigned long nanos);
+static int              add_pax_acl(struct archive_write *,
+                           struct archive_entry *, struct pax *, int);
 static ssize_t          archive_write_pax_data(struct archive_write *,
                             const void *, size_t);
 static int              archive_write_pax_close(struct archive_write *);
@@ -449,6 +452,45 @@ get_entry_symlink(struct archive_write *a, struct archive_entry *entry,
        return (ARCHIVE_OK);
 }
 
+/* Add ACL to pax header */
+static int
+add_pax_acl(struct archive_write *a,
+    struct archive_entry *entry, struct pax *pax, int flags)
+{
+       char *p;
+       const char *attr;
+       int acl_types;
+
+       acl_types = archive_entry_acl_types(entry);
+
+       if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+               attr = "SCHILY.acl.ace";
+       else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+               attr = "SCHILY.acl.access";
+       else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+               attr = "SCHILY.acl.default";
+       else
+               return (ARCHIVE_FATAL);
+
+       p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8);
+       if (p == NULL) {
+               if (errno == ENOMEM) {
+                       archive_set_error(&a->archive, ENOMEM, "%s %s",
+                           "Can't allocate memory for ", attr);
+                       return (ARCHIVE_FATAL);
+               }
+               archive_set_error(&a->archive,
+                   ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s",
+                   "Can't translate ", attr, " to UTF-8");
+               return(ARCHIVE_WARN);
+       } else if (*p != '\0') {
+               add_pax_attr(&(pax->pax_header),
+                   attr, p);
+               free(p);
+       }
+       return(ARCHIVE_OK);
+}
+
 /*
  * TODO: Consider adding 'comment' and 'charset' fields to
  * archive_entry so that clients can specify them.  Also, consider
@@ -465,6 +507,7 @@ archive_write_pax_header(struct archive_write *a,
        const char *p;
        const char *suffix;
        int need_extension, r, ret;
+       int acl_types;
        int sparse_count;
        uint64_t sparse_total, real_size;
        struct pax *pax;
@@ -1016,16 +1059,6 @@ archive_write_pax_header(struct archive_write *a,
        if (!need_extension && p != NULL  &&  *p != '\0')
                need_extension = 1;
 
-       /* If there are non-trivial ACL entries, we need an extension. */
-       if (!need_extension && archive_entry_acl_count(entry_original,
-               ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0)
-               need_extension = 1;
-
-       /* If there are non-trivial ACL entries, we need an extension. */
-       if (!need_extension && archive_entry_acl_count(entry_original,
-               ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) > 0)
-               need_extension = 1;
-
        /* If there are extended attributes, we need an extension */
        if (!need_extension && archive_entry_xattr_count(entry_original) > 0)
                need_extension = 1;
@@ -1034,6 +1067,12 @@ archive_write_pax_header(struct archive_write *a,
        if (!need_extension && sparse_count > 0)
                need_extension = 1;
 
+       acl_types = archive_entry_acl_types(entry_original);
+
+       /* If there are any ACL entries, we need an extension */
+       if (!need_extension && acl_types != 0)
+               need_extension = 1;
+
        /*
         * Libarchive used to include these in extended headers for
         * restricted pax format, but that confused people who
@@ -1085,43 +1124,28 @@ archive_write_pax_header(struct archive_write *a,
                        add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p);
 
                /* I use star-compatible ACL attributes. */
-               r = archive_entry_acl_text_l(entry_original,
-                   ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
-                   ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID,
-                   &p, NULL, pax->sconv_utf8);
-               if (r != 0) {
-                       if (errno == ENOMEM) {
-                               archive_set_error(&a->archive, ENOMEM,
-                                   "Can't allocate memory for "
-                                   "ACL.access");
+               if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+                       ret = add_pax_acl(a, entry_original, pax,
+                           ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+                           ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+                       if (ret == ARCHIVE_FATAL)
                                return (ARCHIVE_FATAL);
-                       }
-                       archive_set_error(&a->archive,
-                           ARCHIVE_ERRNO_FILE_FORMAT,
-                           "Can't translate ACL.access to UTF-8");
-                       ret = ARCHIVE_WARN;
-               } else if (p != NULL && *p != '\0') {
-                       add_pax_attr(&(pax->pax_header),
-                           "SCHILY.acl.access", p);
                }
-               r = archive_entry_acl_text_l(entry_original,
-                   ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
-                   ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID,
-                   &p, NULL, pax->sconv_utf8);
-               if (r != 0) {
-                       if (errno == ENOMEM) {
-                               archive_set_error(&a->archive, ENOMEM,
-                                   "Can't allocate memory for "
-                                   "ACL.default");
+               if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+                       ret = add_pax_acl(a, entry_original, pax,
+                           ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+                           ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+                           ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+                       if (ret == ARCHIVE_FATAL)
+                               return (ARCHIVE_FATAL);
+               }
+               if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) {
+                       ret = add_pax_acl(a, entry_original, pax,
+                           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+                           ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+                           ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+                       if (ret == ARCHIVE_FATAL)
                                return (ARCHIVE_FATAL);
-                       }
-                       archive_set_error(&a->archive,
-                           ARCHIVE_ERRNO_FILE_FORMAT,
-                           "Can't translate ACL.default to UTF-8");
-                       ret = ARCHIVE_WARN;
-               } else if (p != NULL && *p != '\0') {
-                       add_pax_attr(&(pax->pax_header),
-                           "SCHILY.acl.default", p);
                }
 
                /* We use GNU-tar-compatible sparse attributes. */
index 9cec760052ee3cb6749c710f6bb72dab35444e7a..62359ddc20b09c8c91a4ab14668f3bff74ee56c9 100644 (file)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd March 18, 2012
+.Dd December 27, 2016
 .Dt LIBARCHIVE-FORMATS 5
 .Os
 .Sh NAME
@@ -191,8 +191,6 @@ and device numbers.
 .It Solaris extensions
 Libarchive recognizes ACL and extended attribute records written
 by Solaris tar.
-Currently, libarchive only has support for old-style ACLs; the
-newer NFSv4 ACLs are recognized but discarded.
 .El
 .Pp
 The first tar program appeared in Seventh Edition Unix in 1979.
index 6e6f0c096c900106871b8a1be97b2fc3f4eec335..c8911a06d5b05742f6956f5bdfbf1b0bb19323f5 100644 (file)
@@ -1,4 +1,5 @@
 .\" Copyright (c) 2003-2009 Tim Kientzle
+.\" Copyright (c) 2016 Martin Matuska
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd December 23, 2011
+.Dd December 27, 2016
 .Dt TAR 5
 .Os
 .Sh NAME
@@ -440,11 +441,11 @@ archives to store files much larger than the historic 8GB limit.
 Vendor-specific attributes used by Joerg Schilling's
 .Nm star
 implementation.
-.It Cm SCHILY.acl.access , Cm SCHILY.acl.default
-Stores the access and default ACLs as textual strings in a format
+.It Cm SCHILY.acl.access , Cm SCHILY.acl.default, .Cm SCHILY.acl.ace
+Stores the access, default and NFSv4 ACLs as textual strings in a format
 that is an extension of the format specified by POSIX.1e draft 17.
-In particular, each user or group access specification can include a fourth
-colon-separated field with the numeric UID or GID.
+In particular, each user or group access specification can include
+an additional colon-separated field with the numeric UID or GID.
 This allows ACLs to be restored on systems that may not have complete
 user or group information available (such as when NIS/YP or LDAP services
 are temporarily unavailable).
index 3c2671dd08fcd0a5f8a1d158724ecf64d4ff2086..2f0e9e7b3d2312d958d914f478ed3ae5aa6f5e31 100644 (file)
@@ -14,6 +14,7 @@ IF(ENABLE_TEST)
     test_acl_nfs4.c
     test_acl_pax.c
     test_acl_posix1e.c
+    test_acl_text.c
     test_archive_api_feature.c
     test_archive_clear_error.c
     test_archive_cmdline.c
@@ -62,7 +63,7 @@ IF(ENABLE_TEST)
     test_compat_plexus_archiver_tar.c
     test_compat_solaris_pax_sparse.c
     test_compat_solaris_tar_acl.c
-    test_compat_star_acl_posix1e.c
+    test_compat_star_acl.c
     test_compat_tar_hardlink.c
     test_compat_uudecode.c
     test_compat_uudecode_large.c
index 3553be6a9926ec44e49da15c99b836630664c123..d59f1552e70d0eb9f726fa2576a5470a3a014c18 100644 (file)
@@ -2421,6 +2421,132 @@ extract_reference_files(const char **names)
                extract_reference_file(*names++);
 }
 
+/* Set ACLs */
+void
+archive_test_set_acls(struct archive_entry *ae,
+    struct archive_test_acl_t *acls, int n)
+{
+       int i;
+
+       archive_entry_acl_clear(ae);
+       for (i = 0; i < n; i++) {
+               failure("type=%#010x, permset=%#010x, tag=%d, qual=%d name=%s",
+                   acls[i].type, acls[i].permset, acls[i].tag,
+                   acls[i].qual, acls[i].name);
+               assertEqualInt(ARCHIVE_OK,
+                   archive_entry_acl_add_entry(ae,
+                       acls[i].type, acls[i].permset, acls[i].tag,
+                       acls[i].qual, acls[i].name));
+       }
+}
+
+static int
+archive_test_acl_match(struct archive_test_acl_t *acl, int type, int permset,
+    int tag, int qual, const char *name)
+{
+       if (type != acl->type)
+               return (0);
+       if (permset != acl->permset)
+               return (0);
+       if (tag != acl->tag)
+               return (0);
+       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+               return (1);
+       if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
+               return (1);
+       if (tag == ARCHIVE_ENTRY_ACL_EVERYONE)
+               return (1);
+       if (tag == ARCHIVE_ENTRY_ACL_OTHER)
+               return (1);
+       if (qual != acl->qual)
+               return (0);
+       if (name == NULL) {
+               if (acl->name == NULL || acl->name[0] == '\0')
+                       return (1);
+               return (0);
+       }
+       if (acl->name == NULL) {
+               if (name[0] == '\0')
+                       return (1);
+               return (0);
+       }
+       return (0 == strcmp(name, acl->name));
+}
+
+/* Compare ACLs */
+void
+archive_test_compare_acls(struct archive_entry *ae,
+    struct archive_test_acl_t *acls, int cnt, int want_type, int mode)
+{
+       int *marker;
+       int i, r, n;
+       int type, permset, tag, qual;
+       int matched;
+       const char *name;
+
+       n = 0;
+       marker = malloc(sizeof(marker[0]) * cnt);
+
+       for (i = 0; i < cnt; i++) {
+               if ((acls[i].type & want_type) != 0) {
+                       marker[n] = i;
+                       n++;
+               }
+       }
+
+       failure("No ACL's to compare, type mask: %d", want_type);
+       assert(n > 0);
+       if (n == 0)
+               return;
+
+       while (0 == (r = archive_entry_acl_next(ae, want_type,
+                        &type, &permset, &tag, &qual, &name))) {
+               for (i = 0, matched = 0; i < n && !matched; i++) {
+                       if (archive_test_acl_match(&acls[marker[i]], type,
+                           permset, tag, qual, name)) {
+                               /* We found a match; remove it. */
+                               marker[i] = marker[n - 1];
+                               n--;
+                               matched = 1;
+                       }
+               }
+               if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+                   && tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
+                       if (!matched) printf("No match for user_obj perm\n");
+                       failure("USER_OBJ permset (%02o) != user mode (%02o)",
+                           permset, 07 & (mode >> 6));
+                       assert((permset << 6) == (mode & 0700));
+               } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+                   && tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
+                       if (!matched) printf("No match for group_obj perm\n");
+                       failure("GROUP_OBJ permset %02o != group mode %02o",
+                           permset, 07 & (mode >> 3));
+                       assert((permset << 3) == (mode & 0070));
+               } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+                   && tag == ARCHIVE_ENTRY_ACL_OTHER) {
+                       if (!matched) printf("No match for other perm\n");
+                       failure("OTHER permset (%02o) != other mode (%02o)",
+                           permset, mode & 07);
+                       assert((permset << 0) == (mode & 0007));
+               } else {
+                       failure("Could not find match for ACL "
+                           "(type=%#010x,permset=%#010x,tag=%d,qual=%d,"
+                           "name=``%s'')", type, permset, tag, qual, name);
+                       assert(matched == 1);
+               }
+       }
+       assertEqualInt(ARCHIVE_EOF, r);
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+               assert((mode_t)(mode & 0777) == (archive_entry_mode(ae)
+                   & 0777));
+       failure("Could not find match for ACL "
+           "(type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s'')",
+           acls[marker[0]].type, acls[marker[0]].permset,
+           acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
+       assert(n == 0); /* Number of ACLs not matched should == 0 */
+       free(marker);
+}
+
 /*
  *
  * TEST management
index a48c42641e89ba82be0a7e31398474a5794a1aba..71ab1c6b3fe04679abfe0fc89c6de2335c4cc72b 100644 (file)
@@ -346,6 +346,23 @@ extern const char *testworkdir;
 #include "archive.h"
 #include "archive_entry.h"
 
+/* ACL structure */
+struct archive_test_acl_t {
+       int type;  /* Type of ACL */
+       int permset; /* Permissions for this class of users. */
+       int tag; /* Owner, User, Owning group, group, other, etc. */
+       int qual; /* GID or UID of user/group, depending on tag. */
+       const char *name; /* Name of user/group, depending on tag. */
+};
+
+/* Set ACLs */
+void archive_test_set_acls(struct archive_entry *, struct archive_test_acl_t *,
+    int);
+
+/* Compare ACLs */
+void archive_test_compare_acls(struct archive_entry *,
+    struct archive_test_acl_t *, int, int, int);
+
 /* Special customized read-from-memory interface. */
 int read_open_memory(struct archive *, const void *, size_t, size_t);
 /* _minimal version exercises a slightly different set of libarchive APIs. */
index 89861d6eadf9fa847b17d6dfa31b2a892646f817..4e68623c1722d33fd7197f0dab1d0fb649b4a491 100644 (file)
@@ -334,7 +334,7 @@ compare_acls(acl_t acl, struct myacl_t *myacls, const char *filename, int start,
        /* Dump entries in the myacls array that weren't in the system acl. */
        for (i = 0; i < n; ++i) {
                failure(" ACL entry %d missing from %s: "
-                   "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
+                   "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n",
                    marker[i], filename,
                    myacls[marker[i]].type, myacls[marker[i]].permset,
                    myacls[marker[i]].tag, myacls[marker[i]].qual,
@@ -386,7 +386,7 @@ compare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char
                }
 
                failure("ACL entry on file that shouldn't be there: "
-                       "type=%d,permset=%x,tag=%d,qual=%d",
+                       "type=%#010x,permset=%#010x,tag=%d,qual=%d",
                        type,permset,tag,qual);
                assert(matched == 1);
        }
@@ -394,7 +394,7 @@ compare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char
        /* Dump entries in the myacls array that weren't in the system acl. */
        for (i = 0; i < n; ++i) {
                failure(" ACL entry %d missing from %s: "
-                   "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
+                   "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n",
                    marker[i], filename,
                    myacls[marker[i]].type, myacls[marker[i]].permset,
                    myacls[marker[i]].tag, myacls[marker[i]].qual,
index 2eb0a001cb4f3be2b28a8765cd5c91f9761ad7dd..9d256a9f8790ca1099ebb6b42cb20b72866af329 100644 (file)
@@ -28,15 +28,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-0
 #if defined(__FreeBSD__) && __FreeBSD__ > 4
 #include <sys/acl.h>
 
-struct myacl_t {
-       int type;  /* Type of ACL: "access" or "default" */
-       int permset; /* Permissions for this class of users. */
-       int tag; /* Owner, User, Owning group, group, other, etc. */
-       int qual; /* GID or UID of user/group, depending on tag. */
-       const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct myacl_t acls2[] = {
+static struct archive_test_acl_t acls2[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
          ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -53,22 +45,8 @@ static struct myacl_t acls2[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
          ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
          ARCHIVE_ENTRY_ACL_MASK, -1, "" },
-       { 0, 0, 0, 0, NULL }
 };
 
-static void
-set_acls(struct archive_entry *ae, struct myacl_t *acls)
-{
-       int i;
-
-       archive_entry_acl_clear(ae);
-       for (i = 0; acls[i].name != NULL; i++) {
-               archive_entry_acl_add_entry(ae,
-                   acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
-                   acls[i].name);
-       }
-}
-
 static int
 acl_entry_get_perm(acl_entry_t aclent) {
        int permset = 0;
@@ -127,7 +105,7 @@ acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_ta
 #endif
 
 static int
-acl_match(acl_entry_t aclent, struct myacl_t *myacl)
+acl_match(acl_entry_t aclent, struct archive_test_acl_t *myacl)
 {
        gid_t g, *gp;
        uid_t u, *up;
@@ -173,25 +151,20 @@ acl_match(acl_entry_t aclent, struct myacl_t *myacl)
 }
 
 static void
-compare_acls(acl_t acl, struct myacl_t *myacls)
+compare_acls(acl_t acl, struct archive_test_acl_t *myacls, int n)
 {
        int *marker;
        int entry_id = ACL_FIRST_ENTRY;
        int matched;
-       int i, n;
+       int i;
        acl_entry_t acl_entry;
 
        /* Count ACL entries in myacls array and allocate an indirect array. */
-       for (n = 0; myacls[n].name != NULL; ++n)
-               continue;
-       if (n) {
-               marker = malloc(sizeof(marker[0]) * n);
-               if (marker == NULL)
-                       return;
-               for (i = 0; i < n; i++)
-                       marker[i] = i;
-       } else
-               marker = NULL;
+       marker = malloc(sizeof(marker[0]) * n);
+       if (marker == NULL)
+               return;
+       for (i = 0; i < n; i++)
+               marker[i] = i;
 
        /*
         * Iterate over acls in system acl object, try to match each
@@ -219,7 +192,7 @@ compare_acls(acl_t acl, struct myacl_t *myacls)
        /* Dump entries in the myacls array that weren't in the system acl. */
        for (i = 0; i < n; ++i) {
                failure(" ACL entry missing from file: "
-                   "type=%d,permset=%d,tag=%d,qual=%d,name=``%s''\n",
+                   "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n",
                    myacls[marker[i]].type, myacls[marker[i]].permset,
                    myacls[marker[i]].tag, myacls[marker[i]].qual,
                    myacls[marker[i]].name);
@@ -291,7 +264,7 @@ DEFINE_TEST(test_acl_freebsd_posix1e_restore)
        archive_entry_set_pathname(ae, "test0");
        archive_entry_set_mtime(ae, 123456, 7890);
        archive_entry_set_size(ae, 0);
-       set_acls(ae, acls2);
+       archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
        assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
        archive_entry_free(ae);
 
@@ -304,7 +277,7 @@ DEFINE_TEST(test_acl_freebsd_posix1e_restore)
        assertEqualInt(st.st_mtime, 123456);
        acl = acl_get_file("test0", ACL_TYPE_ACCESS);
        assert(acl != (acl_t)NULL);
-       compare_acls(acl, acls2);
+       compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0]));
        acl_free(acl);
 #endif
 }
@@ -332,7 +305,12 @@ DEFINE_TEST(test_acl_freebsd_posix1e_read)
         */
 
        /* Create a test file f1 with acl1 */
-       acl1_text = "user::rwx,group::rwx,other::rwx,user:1:rw-,group:15:r-x,mask::rwx";
+       acl1_text = "user::rwx\n"
+           "group::rwx\n"
+           "other::rwx\n"
+           "user:1:rw-\n"
+           "group:15:r-x\n"
+           "mask::rwx";
        acl1 = acl_from_text(acl1_text);
        assert((void *)acl1 != NULL);
        fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
@@ -371,7 +349,12 @@ DEFINE_TEST(test_acl_freebsd_posix1e_read)
         * to read ACLs, resulting in reading the ACL from a like-named
         * file in the wrong directory.
         */
-       acl2_text = "user::rwx,group::rwx,other::---,user:1:r--,group:15:r--,mask::rwx";
+       acl2_text = "user::rwx\n"
+           "group::rwx\n"
+           "other::---\n"
+           "user:1:r--\n"
+           "group:15:r--\n"
+           "mask::rwx";
        acl2 = acl_from_text(acl2_text);
        assert((void *)acl2 != NULL);
        fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
@@ -406,10 +389,10 @@ DEFINE_TEST(test_acl_freebsd_posix1e_read)
        while (ARCHIVE_OK == archive_read_next_header2(a, ae)) {
                archive_read_disk_descend(a);
                if (strcmp(archive_entry_pathname(ae), "./f1") == 0) {
-                       assertEqualString(archive_entry_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl1_text);
+                       assertEqualString(archive_entry_acl_to_text(ae, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl1_text);
                            
                } else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) {
-                       assertEqualString(archive_entry_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl2_text);
+                       assertEqualString(archive_entry_acl_to_text(ae, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl2_text);
                }
        }
 
index c8f59371a52b5d09a60e63e7f09af387a851de85..f4e66f52294e11e5e667448f1c427e848d5c9531 100644 (file)
@@ -33,15 +33,7 @@ __FBSDID("$FreeBSD$");
  * filesystems support ACLs or not.
  */
 
-struct acl_t {
-       int type;  /* Type of entry: "allow" or "deny" */
-       int permset; /* Permissions for this class of users. */
-       int tag; /* Owner, User, Owning group, group, everyone, etc. */
-       int qual; /* GID or UID of user/group, depending on tag. */
-       const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls1[] = {
+static struct archive_test_acl_t acls1[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
          ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_READ_DATA,
@@ -52,7 +44,7 @@ static struct acl_t acls1[] = {
          ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" },
 };
 
-static struct acl_t acls2[] = {
+static struct archive_test_acl_t acls2[] = {
        /* An entry for each type. */
        { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0,
          ARCHIVE_ENTRY_ACL_USER, 108, "user108" },
@@ -136,7 +128,7 @@ static struct acl_t acls2[] = {
  * Entries that should be rejected when we attempt to set them
  * on an ACL that already has NFS4 entries.
  */
-static struct acl_t acls_bad[] = {
+static struct archive_test_acl_t acls_bad[] = {
        /* POSIX.1e ACL types */
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
          ARCHIVE_ENTRY_ACL_USER, 78, "" },
@@ -156,95 +148,6 @@ static struct acl_t acls_bad[] = {
          ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" },
 };
 
-static void
-set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-       int i;
-
-       archive_entry_acl_clear(ae);
-       for (i = 0; i < n; i++) {
-               failure("type=%d, permset=%d, tag=%d, qual=%d name=%s",
-                   acls[i].type, acls[i].permset, acls[i].tag,
-                   acls[i].qual, acls[i].name);
-               assertEqualInt(ARCHIVE_OK,
-                   archive_entry_acl_add_entry(ae,
-                       acls[i].type, acls[i].permset, acls[i].tag,
-                       acls[i].qual, acls[i].name));
-       }
-}
-
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, int qual,
-    const char *name)
-{
-       if (acl == NULL)
-               return (0);
-       if (type != acl->type)
-               return (0);
-       if (permset != acl->permset)
-               return (0);
-       if (tag != acl->tag)
-               return (0);
-       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_EVERYONE)
-               return (1);
-       if (qual != acl->qual)
-               return (0);
-       if (name == NULL) {
-               if (acl->name == NULL || acl->name[0] == '\0')
-                       return (1);
-               return (0);
-       }
-       if (acl->name == NULL) {
-               if (name[0] == '\0')
-                       return (1);
-               return (0);
-       }
-       return (0 == strcmp(name, acl->name));
-}
-
-static void
-compare_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-       int *marker = malloc(sizeof(marker[0]) * n);
-       int i;
-       int r;
-       int type, permset, tag, qual;
-       int matched;
-       const char *name;
-
-       for (i = 0; i < n; i++)
-               marker[i] = i;
-
-       while (0 == (r = archive_entry_acl_next(ae,
-                        ARCHIVE_ENTRY_ACL_TYPE_NFS4,
-                        &type, &permset, &tag, &qual, &name))) {
-               for (i = 0, matched = 0; i < n && !matched; i++) {
-                       if (acl_match(&acls[marker[i]], type, permset,
-                               tag, qual, name)) {
-                               /* We found a match; remove it. */
-                               marker[i] = marker[n - 1];
-                               n--;
-                               matched = 1;
-                       }
-               }
-               failure("Could not find match for ACL "
-                   "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-                   type, permset, tag, qual, name);
-               assertEqualInt(1, matched);
-       }
-       assertEqualInt(ARCHIVE_EOF, r);
-       failure("Could not find match for ACL "
-           "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-           acls[marker[0]].type, acls[marker[0]].permset,
-           acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
-       assertEqualInt(0, n); /* Number of ACLs not matched should == 0 */
-       free(marker);
-}
-
 DEFINE_TEST(test_acl_nfs4)
 {
        struct archive_entry *ae;
@@ -256,22 +159,31 @@ DEFINE_TEST(test_acl_nfs4)
         archive_entry_set_mode(ae, S_IFREG | 0777);
 
        /* Store and read back some basic ACL entries. */
-       set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+       archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+
+       /* Check that entry contains only NFSv4 types */
+       assert((archive_entry_acl_types(ae) &
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0);
+       assert((archive_entry_acl_types(ae) &
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0);
+
        assertEqualInt(4,
            archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
-       compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+       archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
 
        /* A more extensive set of ACLs. */
-       set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+       archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
        assertEqualInt(32,
            archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
-       compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+       archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
 
        /*
         * Check that clearing ACLs gets rid of them all by repeating
         * the first test.
         */
-       set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+       archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
        failure("Basic ACLs shouldn't be stored as extended ACLs");
        assertEqualInt(4,
            archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
@@ -280,9 +192,9 @@ DEFINE_TEST(test_acl_nfs4)
         * Different types of malformed ACL entries that should
         * fail when added to existing NFS4 ACLs.
         */
-       set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+       archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
        for (i = 0; i < (int)(sizeof(acls_bad)/sizeof(acls_bad[0])); ++i) {
-               struct acl_t *p = &acls_bad[i];
+               struct archive_test_acl_t *p = &acls_bad[i];
                failure("Malformed ACL test #%d", i);
                assertEqualInt(ARCHIVE_FAILED,
                    archive_entry_acl_add_entry(ae,
index 5150b11d63b52ac2e31221a92f6df1d73da2caf3..8dfa0e03131298906c9b3aaba8f97c704ceba79a 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_pax.c 201247 2009-12-30 05:59:21Z kientzle $");
+__FBSDID("$FreeBSD$");
 
 /*
  * Exercise the system-independent portion of the ACL support.
- * Check that pax archive can save and restore ACL data.
+ * Check that pax archive can save and restore POSIX.1e ACL data.
  *
  * This should work on all systems, regardless of whether local
  * filesystems support ACLs or not.
@@ -35,15 +36,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_pax.c 201247 2009-12-30 05
 
 static unsigned char buff[16384];
 
-struct acl_t {
-       int type;  /* Type of ACL: "access" or "default" */
-       int permset; /* Permissions for this class of users. */
-       int tag; /* Owner, User, Owning group, group, other, etc. */
-       int qual; /* GID or UID of user/group, depending on tag. */
-       const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls0[] = {
+static struct archive_test_acl_t acls0[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
          ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -52,7 +45,7 @@ static struct acl_t acls0[] = {
          ARCHIVE_ENTRY_ACL_OTHER, 0, "" },
 };
 
-static struct acl_t acls1[] = {
+static struct archive_test_acl_t acls1[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
          ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -63,7 +56,7 @@ static struct acl_t acls1[] = {
          ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
 };
 
-static struct acl_t acls2[] = {
+static struct archive_test_acl_t acls2[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
          ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -78,101 +71,149 @@ static struct acl_t acls2[] = {
          ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
 };
 
-static void
-set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-       int i;
-
-       archive_entry_acl_clear(ae);
-       for (i = 0; i < n; i++) {
-               archive_entry_acl_add_entry(ae,
-                   acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
-                   acls[i].name);
-       }
-}
+static struct archive_test_acl_t acls3[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
 
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const char *name)
-{
-       if (type != acl->type)
-               return (0);
-       if (permset != acl->permset)
-               return (0);
-       if (tag != acl->tag)
-               return (0);
-       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_OTHER)
-               return (1);
-       if (qual != acl->qual)
-               return (0);
-       if (name == NULL)
-               return (acl->name == NULL || acl->name[0] == '\0');
-       if (acl->name == NULL)
-               return (name == NULL || name[0] == '\0');
-       return (0 == strcmp(name, acl->name));
-}
+static struct archive_test_acl_t acls4[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+           ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+         ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY,
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+         ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
 
-static void
-compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
-{
-       int *marker = malloc(sizeof(marker[0]) * n);
-       int i;
-       int r;
-       int type, permset, tag, qual;
-       int matched;
-       const char *name;
-
-       for (i = 0; i < n; i++)
-               marker[i] = i;
-
-       while (0 == (r = archive_entry_acl_next(ae,
-                        ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-                        &type, &permset, &tag, &qual, &name))) {
-               for (i = 0, matched = 0; i < n && !matched; i++) {
-                       if (acl_match(&acls[marker[i]], type, permset,
-                               tag, qual, name)) {
-                               /* We found a match; remove it. */
-                               marker[i] = marker[n - 1];
-                               n--;
-                               matched = 1;
-                       }
-               }
-               if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
-                       if (!matched) printf("No match for user_obj perm\n");
-                       failure("USER_OBJ permset (%02o) != user mode (%02o)",
-                           permset, 07 & (mode >> 6));
-                       assert((permset << 6) == (mode & 0700));
-               } else if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
-                       if (!matched) printf("No match for group_obj perm\n");
-                       failure("GROUP_OBJ permset %02o != group mode %02o",
-                           permset, 07 & (mode >> 3));
-                       assert((permset << 3) == (mode & 0070));
-               } else if (tag == ARCHIVE_ENTRY_ACL_OTHER) {
-                       if (!matched) printf("No match for other perm\n");
-                       failure("OTHER permset (%02o) != other mode (%02o)",
-                           permset, mode & 07);
-                       assert((permset << 0) == (mode & 0007));
-               } else {
-                       failure("Could not find match for ACL "
-                           "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-                           type, permset, tag, qual, name);
-                       assert(matched == 1);
-               }
-       }
-       assertEqualInt(ARCHIVE_EOF, r);
-       assert((mode_t)(mode & 0777) == (archive_entry_mode(ae) & 0777));
-       failure("Could not find match for ACL "
-           "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-           acls[marker[0]].type, acls[marker[0]].permset,
-           acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
-       assert(n == 0); /* Number of ACLs not matched should == 0 */
-       free(marker);
-}
+static struct archive_test_acl_t acls5[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
+         ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALARM,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
+         ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
 
-DEFINE_TEST(test_acl_pax)
+DEFINE_TEST(test_acl_pax_posix1e)
 {
        struct archive *a;
        struct archive_entry *ae;
@@ -197,23 +238,23 @@ DEFINE_TEST(test_acl_pax)
         archive_entry_set_mode(ae, S_IFREG | 0777);
 
        /* Basic owner/owning group should just update mode bits. */
-       set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+       archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
        assertA(0 == archive_write_header(a, ae));
 
        /* With any extended ACL entry, we should read back a full set. */
-       set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+       archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
        assertA(0 == archive_write_header(a, ae));
 
 
        /* A more extensive set of ACLs. */
-       set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+       archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
        assertA(0 == archive_write_header(a, ae));
 
        /*
         * Check that clearing ACLs gets rid of them all by repeating
         * the first test.
         */
-       set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+       archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
        assertA(0 == archive_write_header(a, ae));
        archive_entry_free(ae);
 
@@ -227,13 +268,13 @@ DEFINE_TEST(test_acl_pax)
        fclose(f);
 
        /* Write out the reference data to a file for manual inspection. */
-       extract_reference_file("test_acl_pax.tar");
-       reference = slurpfile(&reference_size, "test_acl_pax.tar");
+       extract_reference_file("test_acl_pax_posix1e.tar");
+       reference = slurpfile(&reference_size, "test_acl_pax_posix1e.tar");
 
        /* Assert that the generated data matches the built-in reference data.*/
-       failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax.tar' reference file.");
+       failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_posix1e.tar' reference file.");
        assertEqualMem(buff, reference, reference_size);
-       failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax.tar' reference file.");
+       failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_posix1e.tar' reference file.");
        assertEqualInt((int)used, reference_size);
        free(reference);
 
@@ -255,15 +296,18 @@ DEFINE_TEST(test_acl_pax)
        assertA(0 == archive_read_next_header(a, &ae));
        failure("One extended ACL should flag all ACLs to be returned.");
        assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-       compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0142);
+       archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142);
        failure("Basic ACLs should set mode to 0142, not %04o",
            archive_entry_mode(ae)&0777);
        assert((archive_entry_mode(ae) & 0777) == 0142);
 
        /* Third item has pretty extensive ACLs */
        assertA(0 == archive_read_next_header(a, &ae));
-       assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-       compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0543);
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+       archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543);
        failure("Basic ACLs should set mode to 0543, not %04o",
            archive_entry_mode(ae)&0777);
        assert((archive_entry_mode(ae) & 0777) == 0543);
@@ -280,3 +324,93 @@ DEFINE_TEST(test_acl_pax)
        assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
        assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 }
+
+DEFINE_TEST(test_acl_pax_nfs4)
+{
+       struct archive *a;
+       struct archive_entry *ae;
+       size_t used;
+       FILE *f;
+       void *reference;
+       size_t reference_size;
+
+       /* Write an archive to memory. */
+       assert(NULL != (a = archive_write_new()));
+       assertA(0 == archive_write_set_format_pax(a));
+       assertA(0 == archive_write_add_filter_none(a));
+       assertA(0 == archive_write_set_bytes_per_block(a, 1));
+       assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
+       assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+       /* Write a series of files to the archive with different ACL info. */
+
+       /* Create a simple archive_entry. */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_pathname(ae, "file");
+        archive_entry_set_mode(ae, S_IFREG | 0777);
+
+       /* NFS4 ACLs mirroring 0754 file mode */
+       archive_test_set_acls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]));
+       assertA(0 == archive_write_header(a, ae));
+
+       /* A more extensive set of NFS4 ACLs. */
+       archive_test_set_acls(ae, acls4, sizeof(acls4)/sizeof(acls4[0]));
+       assertA(0 == archive_write_header(a, ae));
+
+       /* Set with special (audit, alarm) NFS4 ACLs. */
+       archive_test_set_acls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]));
+       assertA(0 == archive_write_header(a, ae));
+
+       archive_entry_free(ae);
+
+       /* Close out the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+       /* Write out the data we generated to a file for manual inspection. */
+       assert(NULL != (f = fopen("testout", "wb")));
+       assertEqualInt(used, (size_t)fwrite(buff, 1, (unsigned int)used, f));
+       fclose(f);
+
+       /* Write out the reference data to a file for manual inspection. */
+       extract_reference_file("test_acl_pax_nfs4.tar");
+       reference = slurpfile(&reference_size, "test_acl_pax_nfs4.tar");
+
+       /* Assert that the generated data matches the built-in reference data.*/
+       failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_nfs4.tar' reference file.");
+       assertEqualMem(buff, reference, reference_size);
+       failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_nfs4.tar' reference file.");
+       assertEqualInt((int)used, reference_size);
+       free(reference);
+
+       /* Read back each entry and check that the ACL data is right. */
+       assert(NULL != (a = archive_read_new()));
+       assertA(0 == archive_read_support_format_all(a));
+       assertA(0 == archive_read_support_filter_all(a));
+       assertA(0 == archive_read_open_memory(a, buff, used));
+
+       /* First item has NFS4 ACLs mirroring file mode */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(3, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_ALLOW));
+       archive_test_compare_acls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0);
+
+       /* Second item has has more fine-grained NFS4 ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_test_compare_acls(ae, acls4, sizeof(acls4)/sizeof(acls4[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+       /* Third item has has audit and alarm NFS4 ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_test_compare_acls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+       /* Close the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_acl_pax_nfs4.tar.uu b/libarchive/test/test_acl_pax_nfs4.tar.uu
new file mode 100644 (file)
index 0000000..6a41557
--- /dev/null
@@ -0,0 +1,129 @@
+begin 644 test_acl_pax_nfs4.tar
+M4&%X2&5A9&5R+V9I;&4`````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#<W-R``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,C`R
+M(#`P,#`P,#`P,#`P(#`Q,C`P,0`@>```````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,```````
+M````````````````````````````````````````````````````````````
+M```````````````````P,#`P,#`@`#`P,#`P,"``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````Q,S`@4T-(24Q9+F%C;"YA8V4];W=N97)`.G)W
+M>'`M+6%!4E=C0V]S.BTM+2TM+2TZ86QL;W<L9W)O=7!`.G)W+7`M+6$M4BUC
+M+2US.BTM+2TM+2TZ86QL;W<L979E<GEO;F5`.G(M+2TM+6$M4BUC+2US.BTM
+M+2TM+2TZ86QL;W<*````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&9I;&4`````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`W-S<@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P,"`P,#`P,#`P,#`P
+M,"`P,3`P,C0`(#``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#``````````````````````
+M````````````````````````````````````````````````````````````
+M````,#`P,#`P(``P,#`P,#`@````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````4&%X2&5A9&5R+V9I;&4`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````#`P,#<W-R``,#`P,#`P(``P,#`P,#`@`#`P,#`P
+M,#`P-#`V(#`P,#`P,#`P,#`P(#`Q,C`P-P`@>```````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````!U<W1A<@`P
+M,```````````````````````````````````````````````````````````
+M```````````````````````````P,#`P,#`@`#`P,#`P,"``````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````R-C(@4T-(24Q9+F%C;"YA8V4];W=N
+M97)`.G)W+7`M+6%!4E=C0V]S.BTM+2TM+2TZ86QL;W<L=7-E<CIU<V5R-S<Z
+M<BTM+2TM82U2+6,M+7,Z+2TM+2TM23IA;&QO=SHW-RQU<V5R.G5S97(W.#IR
+M=W@M+2TM+2TM+2TM+3HM+2TM+2TM.F1E;GDZ-S@L9W)O=7!`.G)W+7`M+6$M
+M4BUC+2US.BTM+2TM+2TZ86QL;W<L9W)O=7`Z9W)O=7`W.#HM=RUP+2TM02U7
+M+4-O+3HM+2TM+2TM.F1E;GDZ-S@L979E<GEO;F5`.G(M+2TM+6$M4BUC+2US
+M.BTM+2TM+2TZ86QL;W<*````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````&9I;&4`
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````P,#`W-S<@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P,"`P,#`P
+M,#`P,#`P,"`P,3`P,C0`(#``````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````=7-T87(`,#``````````````
+M````````````````````````````````````````````````````````````
+M````````````,#`P,#`P(``P,#`P,#`@````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````4&%X2&5A9&5R+V9I;&4`````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````#`P,#<W-R``,#`P,#`P(``P,#`P,#`@
+M`#`P,#`P,#`P-#$P(#`P,#`P,#`P,#`P(#`Q,C`P,@`@>```````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````!U
+M<W1A<@`P,```````````````````````````````````````````````````
+M```````````````````````````````````P,#`P,#`@`#`P,#`P,"``````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````R-C0@4T-(24Q9+F%C;"YA
+M8V4];W=N97)`.G)W>'`M+6%!4E=C0V]S.BTM+2TM+2TZ86QL;W<L=7-E<CIU
+M<V5R-S<Z<G<M<"TM82U2+6,M;W,Z+2TM+2TM+3IA;&QO=SHW-RQU<V5R.G5S
+M97(W-SHM=RUP+2TM+2TM+2TM+3HM+2TM4RTM.F%U9&ET.C<W+&=R;W5P0#IR
+M=RUP+2UA+5(M8RTM<SHM+2TM+2TM.F%L;&]W+&=R;W5P.F=R;W5P-S@Z<BTM
+M+2TM82U2+6,M+2TZ+2TM+2U&+3IA;&%R;3HW."QE=F5R>6]N94`Z<BTM+2TM
+M82U2+6,M+7,Z+2TM+2TM+3IA;&QO=PH`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`&9I;&4`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````P,#`W-S<@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P
+M,"`P,#`P,#`P,#`P,"`P,3`P,C0`(#``````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````=7-T87(`,#``````
+M````````````````````````````````````````````````````````````
+M````````````````````,#`P,#`P(``P,#`P,#`@````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+'````````````
+`
+end
similarity index 99%
rename from libarchive/test/test_acl_pax.tar.uu
rename to libarchive/test/test_acl_pax_posix1e.tar.uu
index 58d7b62463d41367b2f5d5c0262fced6a6086902..46cf97599d036783d2e58dfd9ab06660de6bf44f 100644 (file)
@@ -1,4 +1,4 @@
-begin 644 test_acl_pax.tar
+begin 644 test_acl_pax_posix1e.tar
 M9FEL90``````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M`````````````#`P,#$T,B``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,#`P
index 9984d44188ee5f7efa23c68c93dde546c5bf0b8d..01167dae20b016e795d3abb847097a597d469f3c 100644 (file)
@@ -27,21 +27,14 @@ __FBSDID("$FreeBSD: src/lib/libarchive/test/test_acl_basic.c,v 1.6 2008/10/19 00
 
 /*
  * Exercise the system-independent portion of the ACL support.
- * Check that archive_entry objects can save and restore POSIX.1e-style ACL data.
+ * Check that archive_entry objects can save and restore POSIX.1e-style
+ * ACL data.
  *
  * This should work on all systems, regardless of whether local
  * filesystems support ACLs or not.
  */
 
-struct acl_t {
-       int type;  /* Type of ACL: "access" or "default" */
-       int permset; /* Permissions for this class of users. */
-       int tag; /* Owner, User, Owning group, group, other, etc. */
-       int qual; /* GID or UID of user/group, depending on tag. */
-       const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls0[] = {
+static struct archive_test_acl_t acls0[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
          ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -50,7 +43,7 @@ static struct acl_t acls0[] = {
          ARCHIVE_ENTRY_ACL_OTHER, 0, "" },
 };
 
-static struct acl_t acls1[] = {
+static struct archive_test_acl_t acls1[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
          ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -61,7 +54,7 @@ static struct acl_t acls1[] = {
          ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
 };
 
-static struct acl_t acls2[] = {
+static struct archive_test_acl_t acls2[] = {
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
          ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
        { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -80,7 +73,7 @@ static struct acl_t acls2[] = {
  * NFS4 entry types; attempts to set these on top of POSIX.1e
  * attributes should fail.
  */
-static struct acl_t acls_nfs4[] = {
+static struct archive_test_acl_t acls_nfs4[] = {
        /* NFS4 types */
        { ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ,
          ARCHIVE_ENTRY_ACL_USER, 78, "" },
@@ -104,106 +97,6 @@ static struct acl_t acls_nfs4[] = {
          ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
 };
 
-static void
-set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-       int i;
-
-       archive_entry_acl_clear(ae);
-       for (i = 0; i < n; i++) {
-               archive_entry_acl_add_entry(ae,
-                   acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
-                   acls[i].name);
-       }
-}
-
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const char *name)
-{
-       if (type != acl->type)
-               return (0);
-       if (permset != acl->permset)
-               return (0);
-       if (tag != acl->tag)
-               return (0);
-       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_OTHER)
-               return (1);
-       if (qual != acl->qual)
-               return (0);
-       if (name == NULL) {
-               if (acl->name == NULL || acl->name[0] == '\0')
-                       return (1);
-               return (0);
-       }
-       if (acl->name == NULL) {
-               if (name[0] == '\0')
-                       return (1);
-               return (0);
-       }
-       return (0 == strcmp(name, acl->name));
-}
-
-static void
-compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
-{
-       int *marker = malloc(sizeof(marker[0]) * n);
-       int i;
-       int r;
-       int type, permset, tag, qual;
-       int matched;
-       const char *name;
-
-       for (i = 0; i < n; i++)
-               marker[i] = i;
-
-       while (0 == (r = archive_entry_acl_next(ae,
-                        ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-                        &type, &permset, &tag, &qual, &name))) {
-               for (i = 0, matched = 0; i < n && !matched; i++) {
-                       if (acl_match(&acls[marker[i]], type, permset,
-                               tag, qual, name)) {
-                               /* We found a match; remove it. */
-                               marker[i] = marker[n - 1];
-                               n--;
-                               matched = 1;
-                       }
-               }
-               if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
-                       if (!matched) printf("No match for user_obj perm\n");
-                       failure("USER_OBJ permset (%02o) != user mode (%02o)",
-                           permset, 07 & (mode >> 6));
-                       assert((permset << 6) == (mode & 0700));
-               } else if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
-                       if (!matched) printf("No match for group_obj perm\n");
-                       failure("GROUP_OBJ permset %02o != group mode %02o",
-                           permset, 07 & (mode >> 3));
-                       assert((permset << 3) == (mode & 0070));
-               } else if (tag == ARCHIVE_ENTRY_ACL_OTHER) {
-                       if (!matched) printf("No match for other perm\n");
-                       failure("OTHER permset (%02o) != other mode (%02o)",
-                           permset, mode & 07);
-                       assert((permset << 0) == (mode & 0007));
-               } else {
-                       failure("Could not find match for ACL "
-                           "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-                           type, permset, tag, qual, name);
-                       assert(matched == 1);
-               }
-       }
-       assertEqualInt(ARCHIVE_EOF, r);
-       assert((mode_t)(mode & 0777) == (archive_entry_mode(ae) & 0777));
-       failure("Could not find match for ACL "
-           "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-           acls[marker[0]].type, acls[marker[0]].permset,
-           acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
-       assert(n == 0); /* Number of ACLs not matched should == 0 */
-       free(marker);
-}
-
 DEFINE_TEST(test_acl_posix1e)
 {
        struct archive_entry *ae;
@@ -223,7 +116,7 @@ DEFINE_TEST(test_acl_posix1e)
         * triggering unnecessary extensions.  It's better to identify
         * trivial ACLs at the point they are being read from disk.
         */
-       set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+       archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
        failure("Basic ACLs shouldn't be stored as extended ACLs");
        assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
        failure("Basic ACLs should set mode to 0142, not %04o",
@@ -232,19 +125,28 @@ DEFINE_TEST(test_acl_posix1e)
 
 
        /* With any extended ACL entry, we should read back a full set. */
-       set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+       archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
        failure("One extended ACL should flag all ACLs to be returned.");
+
+       /* Check that entry contains only POSIX.1e types */
+       assert((archive_entry_acl_types(ae) &
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0);
+       assert((archive_entry_acl_types(ae) &
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0);
+
        assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-       compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0142);
+       archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142);
        failure("Basic ACLs should set mode to 0142, not %04o",
            archive_entry_mode(ae)&0777);
        assert((archive_entry_mode(ae) & 0777) == 0142);
 
 
        /* A more extensive set of ACLs. */
-       set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+       archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
        assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-       compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0543);
+       archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543);
        failure("Basic ACLs should set mode to 0543, not %04o",
            archive_entry_mode(ae)&0777);
        assert((archive_entry_mode(ae) & 0777) == 0543);
@@ -253,7 +155,7 @@ DEFINE_TEST(test_acl_posix1e)
         * Check that clearing ACLs gets rid of them all by repeating
         * the first test.
         */
-       set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+       archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
        failure("Basic ACLs shouldn't be stored as extended ACLs");
        assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
        failure("Basic ACLs should set mode to 0142, not %04o",
@@ -264,9 +166,9 @@ DEFINE_TEST(test_acl_posix1e)
         * Different types of malformed ACL entries that should
         * fail when added to existing POSIX.1e ACLs.
         */
-       set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+       archive_test_set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
        for (i = 0; i < (int)(sizeof(acls_nfs4)/sizeof(acls_nfs4[0])); ++i) {
-               struct acl_t *p = &acls_nfs4[i];
+               struct archive_test_acl_t *p = &acls_nfs4[i];
                failure("Malformed ACL test #%d", i);
                assertEqualInt(ARCHIVE_FAILED,
                    archive_entry_acl_add_entry(ae,
diff --git a/libarchive/test/test_acl_text.c b/libarchive/test/test_acl_text.c
new file mode 100644 (file)
index 0000000..2781e6e
--- /dev/null
@@ -0,0 +1,456 @@
+/*-
+ * Copyright (c) 2016 Martin Matuska
+ * 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 "test.h"
+__FBSDID("$FreeBSD$");
+
+/*
+ * Test converting ACLs to text, both wide and non-wide
+ *
+ * This should work on all systems, regardless of whether local
+ * filesystems support ACLs or not.
+ */
+
+static struct archive_test_acl_t acls0[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ |
+           ARCHIVE_ENTRY_ACL_WRITE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, 100, "user100" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+         ARCHIVE_ENTRY_ACL_USER, 1000, "user1000" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ |
+           ARCHIVE_ENTRY_ACL_WRITE,
+         ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+           ARCHIVE_ENTRY_ACL_READ |
+           ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0,
+         ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, 101, "user101"},
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+           ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_GROUP, 79, "group79" },
+};
+
+static struct archive_test_acl_t acls1[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+         ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY,
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_DELETE_CHILD |
+           ARCHIVE_ENTRY_ACL_DELETE |
+           ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+           ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT |
+           ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY |
+           ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
+         ARCHIVE_ENTRY_ACL_USER, 101, "user101" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+         ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+const char* acltext[] = {
+       "user::rwx\n"
+       "group::r-x\n"
+       "other::r-x\n"
+       "user:user100:r-x\n"
+       "user:user1000:---\n"
+       "group:group78:rwx\n"
+       "default:user::r-x\n"
+       "default:group::r-x\n"
+       "default:other::---\n"
+       "default:user:user101:r-x\n"
+       "default:group:group79:--x",
+
+       "user::rwx\n"
+       "group::r-x\n"
+       "other::r-x\n"
+       "user:user100:r-x:100\n"
+       "user:user1000:---:1000\n"
+       "group:group78:rwx:78\n"
+       "default:user::r-x\n"
+       "default:group::r-x\n"
+       "default:other::---\n"
+       "default:user:user101:r-x:101\n"
+       "default:group:group79:--x:79",
+
+       "u::rwx\n"
+       "g::r-x\n"
+       "o::r-x\n"
+       "u:user100:r-x:100\n"
+       "u:user1000:---:1000\n"
+       "g:group78:rwx:78\n"
+       "d:user::r-x\n"
+       "d:group::r-x\n"
+       "d:other::---\n"
+       "d:user:user101:r-x:101\n"
+       "d:group:group79:--x:79",
+
+       "user::rwx\n"
+       "group::r-x\n"
+       "other::r-x\n"
+       "user:user100:r-x\n"
+       "user:user1000:---\n"
+       "group:group78:rwx",
+
+       "user::rwx,"
+       "group::r-x,"
+       "other::r-x,"
+       "user:user100:r-x,"
+       "user:user1000:---,"
+       "group:group78:rwx",
+
+       "user::rwx\n"
+       "group::r-x\n"
+       "other::r-x\n"
+       "user:user100:r-x:100\n"
+       "user:user1000:---:1000\n"
+       "group:group78:rwx:78",
+
+       "user::r-x\n"
+       "group::r-x\n"
+       "other::---\n"
+       "user:user101:r-x\n"
+       "group:group79:--x",
+
+       "user::r-x\n"
+       "group::r-x\n"
+       "other::---\n"
+       "user:user101:r-x:101\n"
+       "group:group79:--x:79",
+
+       "default:user::r-x\n"
+       "default:group::r-x\n"
+       "default:other::---\n"
+       "default:user:user101:r-x\n"
+       "default:group:group79:--x",
+
+       "user:user77:rw-p--a-R-c-o-:-------:allow\n"
+       "user:user101:-w-pdD--------:fdin---:deny\n"
+       "group:group78:r-----a-R-c---:------I:allow\n"
+       "owner@:rwxp--aARWcCo-:-------:allow\n"
+       "group@:rw-p--a-R-c---:-------:allow\n"
+       "everyone@:r-----a-R-c--s:-------:allow",
+
+       "user:user77:rw-p--a-R-c-o-:-------:allow:77\n"
+       "user:user101:-w-pdD--------:fdin---:deny:101\n"
+       "group:group78:r-----a-R-c---:------I:allow:78\n"
+       "owner@:rwxp--aARWcCo-:-------:allow\n"
+       "group@:rw-p--a-R-c---:-------:allow\n"
+       "everyone@:r-----a-R-c--s:-------:allow"
+};
+
+static wchar_t *
+convert_s_to_ws(const char *s)
+{
+       size_t len;
+       wchar_t *ws = NULL;
+
+       if (s != NULL) {
+               len = strlen(s) + 1;
+               ws = malloc(len * sizeof(wchar_t));
+               assert(mbstowcs(ws, s, len) != (size_t)-1);
+       }
+
+       return (ws);
+}
+
+static void
+compare_acl_text(struct archive_entry *ae, int flags, const char *s)
+{
+       const char *text;
+       const wchar_t *wtext;
+       wchar_t *ws;
+       ssize_t slen;
+
+       ws = convert_s_to_ws(s);
+
+       text = archive_entry_acl_to_text(ae, &slen, flags);
+       assertEqualString(text, s);
+       if (text != NULL)
+               assertEqualInt(strlen(text), slen);
+       wtext = archive_entry_acl_to_text_w(ae, &slen, flags);
+       assertEqualWString(wtext, ws);
+       if (wtext != NULL) {
+               assertEqualInt(wcslen(wtext), slen);
+               free(ws);
+               ws = NULL;
+       }
+}
+
+DEFINE_TEST(test_acl_from_text)
+{
+       struct archive_entry *ae;
+       wchar_t *ws = NULL;
+
+       /* Create an empty archive_entry. */
+       assert((ae = archive_entry_new()) != NULL);
+
+       /* 1a. Read POSIX.1e access ACLs from text */
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text(ae, acltext[5],
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0755);
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+
+       /* 1b. Now read POSIX.1e default ACLs and append them */
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text(ae, acltext[7],
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+       assertEqualInt(11, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_entry_acl_clear(ae);
+
+       /* 1a and 1b with wide strings */
+       ws = convert_s_to_ws(acltext[5]);
+
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text_w(ae, ws,
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0755);
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+
+       free(ws);
+       ws = convert_s_to_ws(acltext[7]);
+
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text_w(ae, ws,
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+       assertEqualInt(11, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_entry_acl_clear(ae);
+
+       /* 2. Read POSIX.1e default ACLs from text */
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text(ae, acltext[7],
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0);
+       assertEqualInt(5, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       archive_entry_acl_clear(ae);
+
+       /* ws is still acltext[7] */
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text_w(ae, ws,
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0);
+       assertEqualInt(5, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       archive_entry_acl_clear(ae);
+
+       /* 3. Read POSIX.1e access and default ACLs from text */
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text(ae, acltext[1],
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+       assertEqualInt(11, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_entry_acl_clear(ae);
+
+       free(ws);
+       ws = convert_s_to_ws(acltext[1]);
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text_w(ae, ws,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+       assertEqualInt(11, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_entry_acl_clear(ae);
+
+       /* 4. Read POSIX.1e access and default ACLs from text (short form) */
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text(ae, acltext[2],
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+       assertEqualInt(11, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_entry_acl_clear(ae);
+
+       free(ws);
+       ws = convert_s_to_ws(acltext[2]);
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text_w(ae, ws,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+       assertEqualInt(11, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+       archive_entry_acl_clear(ae);
+
+       /* 5. Read NFSv4 ACLs from text */
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text(ae, acltext[10],
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_entry_acl_clear(ae);
+
+       free(ws);
+       ws = convert_s_to_ws(acltext[10]);
+
+       assertEqualInt(ARCHIVE_OK,
+           archive_entry_acl_from_text_w(ae, ws,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_entry_acl_clear(ae);
+}
+
+DEFINE_TEST(test_acl_to_text)
+{
+       struct archive_entry *ae;
+
+       /* Create an empty archive_entry. */
+       assert((ae = archive_entry_new()) != NULL);
+
+       /* Write POSIX.1e ACLs  */
+       archive_test_set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+
+       /* No flags should give output like getfacl(1) on linux */
+       compare_acl_text(ae, 0, acltext[0]);
+
+       /* This should give the same output as previous test */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, acltext[0]);
+
+       /* This should give the same output as previous two tests */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+           ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT, acltext[0]);
+
+       /* POSIX.1e access and default ACLs with appended ID */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[1]);
+
+       /* POSIX.1e access acls only, like getfacl(1) on FreeBSD */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, acltext[3]);
+
+       /* POSIX.1e access acls separated with comma */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+           ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA,
+           acltext[4]);
+
+       /* POSIX.1e access acls with appended user or group ID */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+           ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[5]);
+
+       /* POSIX.1e default acls */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, acltext[6]);
+
+       /* POSIX.1e default acls with appended user or group ID */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+           ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[7]);
+
+       /* POSIX.1e default acls prefixed with default: */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+           ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT, acltext[8]);
+
+       /* Write NFSv4 ACLs */
+       archive_test_set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+
+       /* NFSv4 ACLs like getfacl(1) on FreeBSD */
+       compare_acl_text(ae, 0, acltext[9]);
+
+       /* NFSv4 ACLs like "getfacl -i" on FreeBSD */
+       compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[10]);
+}
diff --git a/libarchive/test/test_compat_star_acl.c b/libarchive/test/test_compat_star_acl.c
new file mode 100644 (file)
index 0000000..f2a78b2
--- /dev/null
@@ -0,0 +1,321 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * 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 "test.h"
+__FBSDID("$FreeBSD$");
+
+/*
+ * Verify reading entries with POSIX.1e and NFSv4 ACLs from archives created
+ * by star.
+ *
+ * This should work on all systems, regardless of whether local filesystems
+ * support ACLs or not.
+ */
+
+static struct archive_test_acl_t acls0[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, -1, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
+         ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls1[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, -1, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+         ARCHIVE_ENTRY_ACL_USER, -1, "user78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+         ARCHIVE_ENTRY_ACL_GROUP, -1, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+         ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls2[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, -1 ,"" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_USER, -1, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_GROUP, -1, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_WRITE,
+         ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls3[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+static struct archive_test_acl_t acls4[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+           ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+         ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE,
+         ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY,
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+         ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+static struct archive_test_acl_t acls5[] = {
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+           ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+           ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+         ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+       { ARCHIVE_ENTRY_ACL_TYPE_DENY,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_WRITE_DATA |
+           ARCHIVE_ENTRY_ACL_APPEND_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_DELETE |
+           ARCHIVE_ENTRY_ACL_DELETE_CHILD |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_ACL |
+           ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+           ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+           ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+         ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+       { ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+           ARCHIVE_ENTRY_ACL_READ_DATA |
+           ARCHIVE_ENTRY_ACL_EXECUTE |
+           ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+           ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+           ARCHIVE_ENTRY_ACL_READ_ACL |
+           ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+         ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+DEFINE_TEST(test_compat_star_acl_posix1e)
+{
+       char name[] = "test_compat_star_acl_posix1e.tar";
+       struct archive *a;
+       struct archive_entry *ae;
+
+       /* Read archive file */
+       assert(NULL != (a = archive_read_new()));
+        assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+        assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+        extract_reference_file(name);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name,
+           10240));
+
+       /* First item has a few ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       failure("One extended ACL should flag all ACLs to be returned.");
+       assertEqualInt(5, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+       archive_test_compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142);
+       failure("Basic ACLs should set mode to 0142, not %04o",
+           archive_entry_mode(ae)&0777);
+       assert((archive_entry_mode(ae) & 0777) == 0142);
+
+       /* Second item has pretty extensive ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(7, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+       archive_test_compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543);
+       failure("Basic ACLs should set mode to 0543, not %04o",
+           archive_entry_mode(ae)&0777);
+       assert((archive_entry_mode(ae) & 0777) == 0543);
+
+       /* Third item has default ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+       archive_test_compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0142);
+       failure("Basic ACLs should set mode to 0142, not %04o",
+           archive_entry_mode(ae)&0777);
+       assert((archive_entry_mode(ae) & 0777) == 0142);
+
+       /* Close the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_compat_star_acl_nfs4)
+{
+       char name[] = "test_compat_star_acl_nfs4.tar";
+       struct archive *a;
+       struct archive_entry *ae;
+
+       /* Read archive file */
+       assert(NULL != (a = archive_read_new()));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       extract_reference_file(name);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240
+));
+
+       /* First item has NFS4 ACLs mirroring file mode */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(3, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_ALLOW));
+       archive_test_compare_acls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0);
+
+       /* Second item has has fine-grained NFS4 ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(6, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_test_compare_acls(ae, acls4, sizeof(acls4)/sizeof(acls0[4]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+       /* Third item has file and directory inheritance NFS4 ACLs */
+       assertA(0 == archive_read_next_header(a, &ae));
+       assertEqualInt(5, archive_entry_acl_reset(ae,
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+       archive_test_compare_acls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]),
+           ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+       /* Close the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_compat_star_acl_nfs4.tar.uu b/libarchive/test/test_compat_star_acl_nfs4.tar.uu
new file mode 100644 (file)
index 0000000..79b228e
--- /dev/null
@@ -0,0 +1,231 @@
+begin 644 test_compat_star_acl_nfs4.tar
+M+B\N+T!087A(96%D97(`````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V,#`@,#`P,#`P,"`P,#`P,#`P(#`P,#`P,#`P,C<R
+M(#`P,#`P,#`P,#`P(#`P,38P-S0@9P``````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,')O;W0`
+M````````````````````````````````````=VAE96P`````````````````
+M```````````````````P,#`P,#`P(#`P,#`P,#`@````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````P,#`P,#`P,#`P,"`P,#`P,#`P
+M,#`P,"`````````````````U-R!30TA)3%DN<F5L96%S93US=&%R(#$N-2XS
+M("AA;60V-"UU;FMN;W=N+69R965B<V0Q,2XP*0HR-R!30TA)3%DN87)C:'1Y
+M<&4]97AU<W1A<@HT-R!30TA)3%DN=F]L:&1R+F1U;7!D871E/3$T-SDQ-C<W
+M,C<N,38W,C(U,C@Q"C(U(%-#2$E,62YV;VQH9'(N=F]L;F\],0HS,"!30TA)
+M3%DN=F]L:&1R+F)L;V-K<VEZ93TR,`H`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````"XO+B]`4&%X2&5A
+M9&5R````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#,S-"`P,#`P,#`P,#`P
+M,"`P,#$V,3$T('@`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!R;V]T````````````````
+M`````````````````````'=H965L````````````````````````````````
+M````,#`P,#`P,"`P,#`P,#`P(```````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````,#`P,#`P,#`P,#`@,#`P,#`P,#`P,#`@````````
+M````````,S`@871I;64],30W.3$Q.34U-"XP-#,U-#DP,#`*,S`@8W1I;64]
+M,30W.3$Q.3DQ,BXY,SDQ-C@P,#`*,S`@;71I;64],30W.3$Q.34U-"XP-#,U
+M-#DP,#`*,3,P(%-#2$E,62YA8VPN86-E/6]W;F5R0#IR=WAP+2UA05)78T-O
+M<SHM+2TM+2TM.F%L;&]W+&=R;W5P0#IR=RUP+2UA+5(M8RTM<SHM+2TM+2TM
+M.F%L;&]W+&5V97)Y;VYE0#IR+2TM+2UA+5(M8RTM<SHM+2TM+2TM.F%L;&]W
+M"@``````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````!F:6QE,0``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````,#`P,#<V-"`P,#`P
+M,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,3$S,#(@,#`Q-#8S-B`P
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````'5S=&%R`#`P<F]O=```````````````````````````````
+M``````!W:&5E;````````````````````````````````````#`P,#`P,#`@
+M,#`P,#`P,"``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````#$S,#$R,S$Q,S`R(#$S,#$R,S$R,#4P(````````````````"XO+B]`
+M4&%X2&5A9&5R````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````P,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#4T,"`P,#`P
+M,#`P,#`P,"`P,#$V,3$S('@`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````=7-T87(`,#!R;V]T````````
+M`````````````````````````````'=H965L````````````````````````
+M````````````,#`P,#`P,"`P,#`P,#`P(```````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````,#`P,#`P,#`P,#`@,#`P,#`P,#`P,#`@
+M````````````````,S`@871I;64],30W.3$Q.34U-2XR-C<P,3@P,#`*,S`@
+M8W1I;64],30W.3$V,34Y."XY,SDV-#8P,#`*,S`@;71I;64],30W.3$Q.34U
+M-2XR-C<P,3@P,#`*,C8R(%-#2$E,62YA8VPN86-E/75S97(Z=7-E<C<X.G)W
+M>"TM+2TM+2TM+2TM.BTM+2TM+2TZ9&5N>3HW."QG<F]U<#IG<F]U<#<X.BUW
+M+7`M+2U!+5<M0V\M.BTM+2TM+2TZ9&5N>3HW."QU<V5R.G5S97(W-SIR+2TM
+M+2UA+5(M8RTM<SHM+2TM+2U).F%L;&]W.C<W+&]W;F5R0#IR=RUP+2UA05)7
+M8T-O<SHM+2TM+2TM.F%L;&]W+&=R;W5P0#IR=RUP+2UA+5(M8RTM<SHM+2TM
+M+2TM.F%L;&]W+&5V97)Y;VYE0#IR+2TM+2UA+5(M8RTM<SHM+2TM+2TM.F%L
+M;&]W"@``````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````!F:6QE,@``````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````,#`P,#8V
+M-"`P,#`P,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,3$S,#,@,#`Q
+M-#8U-R`P````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````'5S=&%R`#`P<F]O=```````````````````````
+M``````````````!W:&5E;````````````````````````````````````#`P
+M,#`P,#`@,#`P,#`P,"``````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#$S,#$R,S$Q,S`S(#$S,#$R-#,S,S<V(```````````````
+M`"XO+B]`4&%X2&5A9&5R````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````P,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#0V
+M-2`P,#`P,#`P,#`P,"`P,#$V,3(Q('@`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````=7-T87(`,#!R;V]T
+M`````````````````````````````````````'=H965L````````````````
+M````````````````````,#`P,#`P,"`P,#`P,#`P(```````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````,#`P,#`P,#`P,#`@,#`P,#`P
+M,#`P,#`@````````````````,S`@871I;64],30W.3$V-S0Y-2XV.#<X-C8P
+M,#`*,S`@8W1I;64],30W.3$V-S<R,RXT-#`X-C<P,#`*,S`@;71I;64],30W
+M.3$R,#8W."XT-#$U.#`P,#`*,C$Y(%-#2$E,62YA8VPN86-E/6=R;W5P.F=R
+M;W5P-S@Z<G=X<$1D84%25V-#;W,Z9F0M+2TM+3ID96YY.C<X+'5S97(Z=7-E
+M<C<W.G(M+2TM+6$M4BUC+2US.F9D+2TM+2TZ86QL;W<Z-S<L;W=N97)`.G)W
+M>'`M+6%!4E=C0V]S.BTM+2TM+2TZ86QL;W<L9W)O=7!`.G)W>'`M+6%!4E=C
+M+2US.BTM+2TM+2TZ86QL;W<L979E<GEO;F5`.G(M>"TM+6$M4BUC+2US.BTM
+M+2TM+2TZ86QL;W<*````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````!D:7(Q+P``````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M,#`P,#<W-2`P,#`P,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,3,T
+M-#8@,#`Q-#8S,2`U````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````'5S=&%R`#`P<F]O=```````````````
+M``````````````````````!W:&5E;```````````````````````````````
+M`````#`P,#`P,#`@,#`P,#`P,"``````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````#$S,#$R-#0W,#`W(#$S,#$R-#0W,S4S(```````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+9````````````````````````````````````
+`
+end
diff --git a/libarchive/test/test_compat_star_acl_posix1e.c b/libarchive/test/test_compat_star_acl_posix1e.c
deleted file mode 100644 (file)
index 10bffd9..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/*-
- * Copyright (c) 2003-2007 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 "test.h"
-__FBSDID("$FreeBSD$");
-
-/*
- * Verify reading entries with POSIX.1e ACLs from archives created by star
- *
- * This should work on all systems, regardless of whether local filesystems
- * support ACLs or not.
- */
-
-struct acl_t {
-       int type;  /* Type of ACL: "access" or "default" */
-       int permset; /* Permissions for this class of users. */
-       int tag; /* Owner, User, Owning group, group, other, etc. */
-       const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls0[] = {
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
-         ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_USER, "user77" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_MASK, ""},
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
-         ARCHIVE_ENTRY_ACL_OTHER, "" },
-};
-
-static struct acl_t acls1[] = {
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_USER, "user77" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
-         ARCHIVE_ENTRY_ACL_USER, "user78" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
-         ARCHIVE_ENTRY_ACL_GROUP, "group78" },
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
-         ARCHIVE_ENTRY_ACL_MASK, ""}, 
-       { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
-         ARCHIVE_ENTRY_ACL_OTHER, "" },
-};
-
-static struct acl_t acls2[] = {
-       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
-         ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
-       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_USER, "user77" },
-       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
-         ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
-       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
-         ARCHIVE_ENTRY_ACL_GROUP, "group78" },
-       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
-         ARCHIVE_ENTRY_ACL_MASK, ""},
-       { ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_WRITE,
-         ARCHIVE_ENTRY_ACL_OTHER, "" },
-};
-
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, const char *name)
-{
-       if (type != acl->type)
-               return (0);
-       if (permset != acl->permset)
-               return (0);
-       if (tag != acl->tag)
-               return (0);
-       if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_OTHER)
-               return (1);
-       if (tag == ARCHIVE_ENTRY_ACL_MASK)
-               return (1);
-       if (name == NULL)
-               return (acl->name == NULL || acl->name[0] == '\0');
-       if (acl->name == NULL)
-               return (name == NULL || name[0] == '\0');
-       return (0 == strcmp(name, acl->name));
-}
-
-static void
-compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode,
-    int want_type)
-{
-       int *marker = malloc(sizeof(marker[0]) * n);
-       int i;
-       int r;
-       int type, permset, tag, qual;
-       int matched;
-       const char *name;
-
-       for (i = 0; i < n; i++)
-               marker[i] = i;
-
-       while (0 == (r = archive_entry_acl_next(ae, want_type,
-                        &type, &permset, &tag, &qual, &name))) {
-               for (i = 0, matched = 0; i < n && !matched; i++) {
-                       if (acl_match(&acls[marker[i]], type, permset,
-                               tag, name)) {
-                               /* We found a match; remove it. */
-                               marker[i] = marker[n - 1];
-                               n--;
-                               matched = 1;
-                       }
-               }
-               if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
-                       if (!matched) printf("No match for user_obj perm\n");
-                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
-                               failure("USER_OBJ permset (%02o) != user mode (%02o)",
-                                   permset, 07 & (mode >> 6));
-                               assert((permset << 6) == (mode & 0700));
-                       }
-               } else if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
-                       if (!matched) printf("No match for group_obj perm\n");
-                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
-                               failure("GROUP_OBJ permset %02o != group mode %02o",
-                                   permset, 07 & (mode >> 3));
-                               assert((permset << 3) == (mode & 0070));
-                       }
-               } else if (tag == ARCHIVE_ENTRY_ACL_OTHER) {
-                       if (!matched) printf("No match for other perm\n");
-                       if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
-                               failure("OTHER permset (%02o) != other mode (%02o)",
-                                   permset, mode & 07);
-                               assert((permset << 0) == (mode & 0007));
-                       }
-               } else if (tag != ARCHIVE_ENTRY_ACL_MASK) {
-                       failure("Could not find match for ACL "
-                           "(type=%d,permset=%d,tag=%d,name=``%s'')",
-                           type, permset, tag, name);
-                       assert(matched == 1);
-               }
-       }
-       assertEqualInt(ARCHIVE_EOF, r);
-       assert((mode_t)(mode & 0777) == (archive_entry_mode(ae) & 0777));
-       failure("Could not find match for ACL "
-           "(type=%d,permset=%d,tag=%d,name=``%s'')",
-           acls[marker[0]].type, acls[marker[0]].permset,
-           acls[marker[0]].tag, acls[marker[0]].name);
-       assert(n == 0); /* Number of ACLs not matched should == 0 */
-       free(marker);
-}
-
-DEFINE_TEST(test_compat_star_acl_posix1e)
-{
-       char name[] = "test_compat_star_acl_posix1e.tar";
-       struct archive *a;
-       struct archive_entry *ae;
-
-       /* Read archive file */
-       assert(NULL != (a = archive_read_new()));
-        assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
-        assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
-        extract_reference_file(name);
-       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
-
-       /* First item has a few ACLs */
-       assertA(0 == archive_read_next_header(a, &ae));
-       failure("One extended ACL should flag all ACLs to be returned.");
-       assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-       compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), 0142, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-       failure("Basic ACLs should set mode to 0142, not %04o",
-           archive_entry_mode(ae)&0777);
-       assert((archive_entry_mode(ae) & 0777) == 0142);
-
-       /* Second item has pretty extensive ACLs */
-       assertA(0 == archive_read_next_header(a, &ae));
-       assertEqualInt(7, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-       compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0543, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-       failure("Basic ACLs should set mode to 0543, not %04o",
-           archive_entry_mode(ae)&0777);
-       assert((archive_entry_mode(ae) & 0777) == 0543);
-
-       /* Third item has default ACLs */
-       assertA(0 == archive_read_next_header(a, &ae));
-       assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
-       compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0142, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
-       failure("Basic ACLs should set mode to 0142, not %04o",
-           archive_entry_mode(ae)&0777);
-       assert((archive_entry_mode(ae) & 0777) == 0142);
-
-       /* Close the archive. */
-       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
-       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
-}