From: Tim Kientzle Date: Sat, 2 May 2026 23:23:32 +0000 (-0700) Subject: [ACL] Add more tests for POSIX.1e ACLs X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9d5b4feeba119750e2a81bfa78ae53040bbf9dfe;p=thirdparty%2Flibarchive.git [ACL] Add more tests for POSIX.1e ACLs This mirrors the tests just added for NFS4 ACLs. They didn't uncover any problems. --- diff --git a/Makefile.am b/Makefile.am index 72b64e7e5..64b57275e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -383,6 +383,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_acl_platform_nfs4.c \ libarchive/test/test_acl_platform_posix1e.c \ libarchive/test/test_acl_posix1e.c \ + libarchive/test/test_acl_posix1e_text.c \ libarchive/test/test_acl_text.c \ libarchive/test/test_ar_mode.c \ libarchive/test/test_archive_api_feature.c \ diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index 8cd423932..125f9e709 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -17,6 +17,7 @@ IF(ENABLE_TEST) test_acl_platform_nfs4.c test_acl_platform_posix1e.c test_acl_posix1e.c + test_acl_posix1e_text.c test_acl_text.c test_ar_mode.c test_archive_api_feature.c diff --git a/libarchive/test/test_acl_posix1e_text.c b/libarchive/test/test_acl_posix1e_text.c new file mode 100644 index 000000000..57084465d --- /dev/null +++ b/libarchive/test/test_acl_posix1e_text.c @@ -0,0 +1,530 @@ +/*- + * Copyright (c) 2026 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" +#include + +/* + * Focused tests for POSIX.1e ACL text parsing and serialization. + * + * test_acl_text.c covers basic POSIX.1e round-trips but does not exercise: + * the mask entry, the Solaris two-field format for mask and other, uppercase + * mode letters (R/W/X), isint() overflow on the POSIX path, and various + * invalid-input error paths. These tests fill those gaps. + * + * Note: archive_entry_acl_reset(ae, TYPE_ACCESS) always returns the count + * of extended ACL entries PLUS 3 synthesized base entries (USER_OBJ, + * GROUP_OBJ, OTHER) derived from the mode bits. Tests below account for + * this by adding 3 to the expected count and skipping those 3 entries + * before iterating over the named/mask entries of interest. + */ + +/* ACCESS-only ACL including a mask entry. */ +static const char *posix1e_access = + "user::rwx\n" + "group::r-x\n" + "other::r-x\n" + "user:alice:r-x\n" + "group:staff:rwx\n" + "mask::r-x"; + +/* Combined ACCESS + DEFAULT ACL, with mask in both sections. */ +static const char *posix1e_full = + "user::rwx\n" + "group::r-x\n" + "other::r-x\n" + "user:alice:r-x\n" + "group:staff:rwx\n" + "mask::r-x\n" + "default:user::r-x\n" + "default:group::r-x\n" + "default:other::---\n" + "default:user:alice:r-x\n" + "default:group:staff:--x\n" + "default:mask::r-x"; + +/* posix1e_full with numeric IDs appended to named user/group entries. */ +static const char *posix1e_extra_id = + "user::rwx\n" + "group::r-x\n" + "other::r-x\n" + "user:alice:r-x:77\n" + "group:staff:rwx:78\n" + "mask::r-x\n" + "default:user::r-x\n" + "default:group::r-x\n" + "default:other::---\n" + "default:user:alice:r-x:77\n" + "default:group:staff:--x:78\n" + "default:mask::r-x"; + +/* + * Solaris-style ACCESS ACL: mask and other use one colon (no empty name + * field between tag and permission string). + */ +static const char *posix1e_solaris = + "user::rwx\n" + "group::r-x\n" + "other:r-x\n" + "user:alice:r-x\n" + "mask:r-x"; + +static wchar_t * +s_to_ws(const char *s) +{ + size_t len = strlen(s) + 1; + wchar_t *ws = malloc(len * sizeof(wchar_t)); + assert(ws != NULL); + assert(mbstowcs(ws, s, len) != (size_t)-1); + return (ws); +} + +/* + * Skip the three base entries (USER_OBJ, GROUP_OBJ, OTHER) that + * archive_entry_acl_reset() synthesises from mode bits and that + * archive_entry_acl_next() always returns first for ACCESS ACLs. + */ +static void +skip_base_entries(struct archive_entry *ae) +{ + int type, permset, tag, qual; + const char *name; + int i; + + for (i = 0; i < 3; i++) + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name); +} + +/* + * Round-trip: parse POSIX.1e text → serialize → compare. + * Covers ACCESS-only, combined ACCESS+DEFAULT, and the EXTRA_ID style, + * for both the narrow and wide serializers. + */ +DEFINE_TEST(test_acl_posix1e_text_roundtrip) +{ + struct archive_entry *ae; + char *text; + wchar_t *wtext, *ws; + ssize_t len; + + ae = archive_entry_new(); + assert(ae != NULL); + archive_entry_set_pathname(ae, "file"); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* ACCESS-only with mask entry */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, posix1e_access, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + text = archive_entry_acl_to_text(ae, &len, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + assertEqualString(posix1e_access, text); + free(text); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Combined ACCESS + DEFAULT — serializer auto-adds MARK_DEFAULT */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, posix1e_full, + ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); + text = archive_entry_acl_to_text(ae, &len, 0); + assertEqualString(posix1e_full, text); + free(text); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* EXTRA_ID format */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, posix1e_extra_id, + ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); + text = archive_entry_acl_to_text(ae, &len, + ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID); + assertEqualString(posix1e_extra_id, text); + free(text); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Wide: ACCESS-only round-trip */ + ws = s_to_ws(posix1e_access); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text_w(ae, ws, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + wtext = archive_entry_acl_to_text_w(ae, &len, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + assertEqualWString(ws, wtext); + free(wtext); + free(ws); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Wide: combined ACCESS + DEFAULT */ + ws = s_to_ws(posix1e_full); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text_w(ae, ws, + ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)); + wtext = archive_entry_acl_to_text_w(ae, &len, 0); + assertEqualWString(ws, wtext); + free(wtext); + free(ws); + + archive_entry_free(ae); +} + +/* + * Solaris two-field format: "mask:rwx" and "other:rwx" omit the empty + * name field, so each has only two colon-separated tokens instead of three. + * The parser recognises this form; the serializer reproduces it when + * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS is set. + */ +DEFINE_TEST(test_acl_posix1e_text_solaris) +{ + struct archive_entry *ae; + char *text; + int type, permset, tag, qual; + const char *name; + ssize_t len; + + ae = archive_entry_new(); + assert(ae != NULL); + archive_entry_set_pathname(ae, "file"); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* + * Parse Solaris-format text. Extended entries are: named user alice + * and mask — 2 extended + 3 base = 5 total. + */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, posix1e_solaris, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(5, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + skip_base_entries(ae); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag); + assertEqualString("alice", name); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt(ARCHIVE_ENTRY_ACL_MASK, tag); + assertEqualInt(ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE, + permset); + + /* Serialize with STYLE_SOLARIS and compare */ + text = archive_entry_acl_to_text(ae, &len, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS); + assertEqualString(posix1e_solaris, text); + free(text); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Solaris style with non-empty name on other field → ARCHIVE_WARN */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother:nobody:r-x\n", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + + archive_entry_free(ae); +} + +/* + * ismode() accepts uppercase R, W, X as well as lowercase; the serializer + * always emits lowercase. Both narrow and wide parsers share this + * behaviour. + */ +DEFINE_TEST(test_acl_posix1e_text_ismode) +{ + struct archive_entry *ae; + char *text; + int type, permset, tag, qual; + const char *name; + ssize_t len; + + ae = archive_entry_new(); + assert(ae != NULL); + archive_entry_set_pathname(ae, "file"); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* + * Uppercase R/W/X treated identically to r/w/x. + * 1 extended entry (alice) + 3 base = 4 total. + */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, + "user::RWX\ngroup::R-X\nother::---\nuser:alice:RwX", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(4, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + skip_base_entries(ae); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag); + assertEqualString("alice", name); + assertEqualInt( + ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_WRITE | + ARCHIVE_ENTRY_ACL_EXECUTE, permset); + + /* Serializer always emits lowercase */ + text = archive_entry_acl_to_text(ae, &len, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + assertEqualString( + "user::rwx\ngroup::r-x\nother::---\nuser:alice:rwx", text); + free(text); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Same via the wide parser */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text_w(ae, + L"user::RWX\ngroup::R-X\nother::---\nuser:alice:RwX", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(4, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + skip_base_entries(ae); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt( + ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_WRITE | + ARCHIVE_ENTRY_ACL_EXECUTE, permset); + + archive_entry_free(ae); +} + +/* + * Numeric IDs in the name field and the extra-ID override field, + * including the isint() overflow boundary now enforced on the POSIX path. + */ +DEFINE_TEST(test_acl_posix1e_text_numeric_id) +{ + struct archive_entry *ae; + char *text; + ssize_t len; + int type, permset, tag, qual; + const char *name; + + ae = archive_entry_new(); + assert(ae != NULL); + archive_entry_set_pathname(ae, "file"); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* 1. Numeric-only name → id derived from name field */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother::r-x\nuser:1000:rwx", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + /* 1 extended + 3 base = 4 */ + assertEqualInt(4, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + skip_base_entries(ae); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt(1000, qual); + assertEqualString("1000", name); + /* Without EXTRA_ID the id is carried by the name string */ + text = archive_entry_acl_to_text(ae, &len, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + assert(strstr(text, "user:1000:rwx") != NULL); + free(text); + /* With EXTRA_ID the id is also appended */ + text = archive_entry_acl_to_text(ae, &len, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS | ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID); + assert(strstr(text, "user:1000:rwx:1000") != NULL); + free(text); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* 2. Trailing extra-ID field overrides name-derived id */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother::r-x\nuser:alice:r-x:42", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(4, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + skip_base_entries(ae); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt(42, qual); + assertEqualString("alice", name); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* 3. Overflow in extra-ID field → extended entry rejected, no ext entries */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother::r-x\nuser:alice:r-x:99999999999", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* 4. INT_MAX (2147483647) in the extra-ID field → also rejected */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother::r-x\nuser:alice:r-x:2147483647", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* 5. INT_MAX - 1 (2147483646) is the largest accepted id */ + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother::r-x\nuser:alice:r-x:2147483646", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(4, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + skip_base_entries(ae); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt(2147483646, qual); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* 6. Overflow in the name field (all-digit name too large) → rejected */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother::r-x\nuser:99999999999:r-x", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + + archive_entry_free(ae); +} + +/* + * Malformed POSIX.1e ACL entries — each should return ARCHIVE_WARN without + * crashing and without storing invalid data. + */ +DEFINE_TEST(test_acl_posix1e_text_invalid) +{ + struct archive_entry *ae; + int type, permset, tag, qual; + const char *name; + + ae = archive_entry_new(); + assert(ae != NULL); + archive_entry_set_pathname(ae, "file"); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Invalid character in permission field */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "user:alice:rqx", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Unknown tag */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "bogus:alice:rwx", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* NFSv4 "everyone@" tag is not recognised by the POSIX parser */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "everyone@:r-----a-R-c--s:-------:allow", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Bare tag with no colon-separated fields */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, "user", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* + * Valid entry followed by invalid — valid entry must be stored. + * 1 extended (alice) + 3 base = 4 total. + */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text(ae, + "user::rwx\ngroup::r-x\nother::r-x\n" + "user:alice:r-x\n" + "bogus:bob:rwx", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(4, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + skip_base_entries(ae); + assertEqualInt(ARCHIVE_OK, + archive_entry_acl_next(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + &type, &permset, &tag, &qual, &name)); + assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag); + assertEqualString("alice", name); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Wide parser: invalid permission character */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text_w(ae, + L"user:alice:rqx", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Wide parser: unknown tag */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text_w(ae, + L"bogus:alice:rwx", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + archive_entry_acl_clear(ae); + archive_entry_set_mode(ae, AE_IFREG | 0755); + + /* Wide parser: NFSv4 tag rejected by POSIX parser */ + assertEqualInt(ARCHIVE_WARN, + archive_entry_acl_from_text_w(ae, + L"everyone@:r-----a-R-c--s:-------:allow", + ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + assertEqualInt(0, + archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + + archive_entry_free(ae); +}