Merge r1053,r1055,r1056,r1057,r1065 from libarchive.googlecode.com:

* Fix parsing of POSIX.1e ACLs from Solaris tar archives
 * Test the above
 * Preserve the order of POSIX.1e ACL entries
 * Update tests whose results depended on the order of ACL entries
 * Identify NFSv4 ACLs in Solaris tar archives and warn that
   they're not yet supported. (In particular, don't try to parse
   them as POSIX.1e ACLs.)

Thanks to: Edward Napierala sent me some Solaris 10 tar archives to test
This commit is contained in:
Tim Kientzle 2009-04-27 18:27:54 +00:00
parent 3388ca6b71
commit 01a94543e9
6 changed files with 314 additions and 48 deletions

View File

@ -115,6 +115,7 @@ static int acl_special(struct archive_entry *entry,
static struct ae_acl *acl_new_entry(struct archive_entry *entry,
int type, int permset, int tag, int id);
static int isint_w(const wchar_t *start, const wchar_t *end, int *result);
static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);
static void next_field_w(const wchar_t **wp, const wchar_t **start,
const wchar_t **end, wchar_t *sep);
static int prefix_w(const wchar_t *start, const wchar_t *end,
@ -1238,7 +1239,7 @@ static struct ae_acl *
acl_new_entry(struct archive_entry *entry,
int type, int permset, int tag, int id)
{
struct ae_acl *ap;
struct ae_acl *ap, *aq;
if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS &&
type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
@ -1251,20 +1252,26 @@ acl_new_entry(struct archive_entry *entry,
/* XXX TODO: More sanity-checks on the arguments XXX */
/* If there's a matching entry already in the list, overwrite it. */
for (ap = entry->acl_head; ap != NULL; ap = ap->next) {
ap = entry->acl_head;
aq = NULL;
while (ap != NULL) {
if (ap->type == type && ap->tag == tag && ap->id == id) {
ap->permset = permset;
return (ap);
}
aq = ap;
ap = ap->next;
}
/* Add a new entry to the list. */
/* Add a new entry to the end of the list. */
ap = (struct ae_acl *)malloc(sizeof(*ap));
if (ap == NULL)
return (NULL);
memset(ap, 0, sizeof(*ap));
ap->next = entry->acl_head;
entry->acl_head = ap;
if (aq == NULL)
entry->acl_head = ap;
else
aq->next = ap;
ap->type = type;
ap->tag = tag;
ap->id = id;
@ -1586,11 +1593,10 @@ __archive_entry_acl_parse_w(struct archive_entry *entry,
struct {
const wchar_t *start;
const wchar_t *end;
} field[4];
} field[4], name;
int fields;
int type, tag, permset, id;
const wchar_t *p;
wchar_t sep;
while (text != NULL && *text != L'\0') {
@ -1609,9 +1615,6 @@ __archive_entry_acl_parse_w(struct archive_entry *entry,
++fields;
} while (sep == L':');
if (fields < 3)
return (ARCHIVE_WARN);
/* Check for a numeric ID in field 1 or 3. */
id = -1;
isint_w(field[1].start, field[1].end, &id);
@ -1619,27 +1622,6 @@ __archive_entry_acl_parse_w(struct archive_entry *entry,
if (id == -1 && fields > 3)
isint_w(field[3].start, field[3].end, &id);
/* Parse the permissions from field 2. */
permset = 0;
p = field[2].start;
while (p < field[2].end) {
switch (*p++) {
case 'r': case 'R':
permset |= ARCHIVE_ENTRY_ACL_READ;
break;
case 'w': case 'W':
permset |= ARCHIVE_ENTRY_ACL_WRITE;
break;
case 'x': case 'X':
permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
break;
case '-':
break;
default:
return (ARCHIVE_WARN);
}
}
/*
* Solaris extension: "defaultuser::rwx" is the
* default ACL corresponding to "user::rwx", etc.
@ -1651,22 +1633,47 @@ __archive_entry_acl_parse_w(struct archive_entry *entry,
} else
type = default_type;
name.start = name.end = NULL;
if (prefix_w(field[0].start, field[0].end, L"user")) {
if (id != -1 || field[1].start < field[1].end)
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;
else
name = field[1];
} else
tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
} else if (prefix_w(field[0].start, field[0].end, L"group")) {
if (id != -1 || field[1].start < field[1].end)
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;
else
name = field[1];
} else
tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
} else if (prefix_w(field[0].start, field[0].end, L"other")) {
if (id != -1 || field[1].start < field[1].end)
if (fields == 2
&& field[1].start < field[1].end
&& ismode_w(field[1].start, field[2].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 (id != -1 || field[1].start < field[1].end)
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
@ -1674,7 +1681,7 @@ __archive_entry_acl_parse_w(struct archive_entry *entry,
/* Add entry to the internal list. */
archive_entry_acl_add_entry_w_len(entry, type, permset,
tag, id, field[1].start, field[1].end - field[1].start);
tag, id, name.start, name.end - name.start);
}
return (ARCHIVE_OK);
}
@ -1797,6 +1804,38 @@ isint_w(const wchar_t *start, const wchar_t *end, int *result)
return (1);
}
/*
* Parse a string as a mode field. Returns true if
* the string is non-empty and consists only of mode characters,
* false otherwise.
*/
static int
ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
{
const wchar_t *p;
p = start;
*permset = 0;
while (p < end) {
switch (*p++) {
case 'r': case 'R':
*permset |= ARCHIVE_ENTRY_ACL_READ;
break;
case 'w': case 'W':
*permset |= ARCHIVE_ENTRY_ACL_WRITE;
break;
case 'x': case 'X':
*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
break;
case '-':
break;
default:
return (0);
}
}
return (1);
}
/*
* Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
* to point to just after the separator. *start points to the first

View File

@ -732,6 +732,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar,
const struct archive_entry_header_ustar *header;
size_t size;
int err;
int64_t type;
char *acl, *p;
wchar_t *wp;
@ -744,24 +745,57 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar,
err = read_body_to_string(a, tar, &(tar->acl_text), h);
if (err != ARCHIVE_OK)
return (err);
/* Recursively read next header */
err = tar_read_header(a, tar, entry);
if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
return (err);
/* Skip leading octal number. */
/* XXX TODO: Parse the octal number and sanity-check it. */
/* TODO: Examine the first characters to see if this
* is an AIX ACL descriptor. We'll likely never support
* them, but it would be polite to recognize and warn when
* we do see them. */
/* Leading octal number indicates ACL type and number of entries. */
p = acl = tar->acl_text.s;
while (*p != '\0' && p < acl + size)
type = 0;
while (*p != '\0' && p < acl + size) {
if (*p < '0' || *p > '7') {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Malformed Solaris ACL attribute (invalid digit)");
return(ARCHIVE_WARN);
}
type <<= 3;
type += *p - '0';
if (type > 077777777) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Malformed Solaris ACL attribute (count too large)");
return (ARCHIVE_WARN);
}
p++;
}
switch (type & ~0777777) {
case 01000000:
/* POSIX.1e ACL */
break;
case 03000000:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Solaris NFSv4 ACLs not supported");
return (ARCHIVE_WARN);
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Malformed Solaris ACL attribute (unsupported type %o)",
(int)type);
return (ARCHIVE_WARN);
}
p++;
if (p >= acl + size) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Malformed Solaris ACL attribute");
"Malformed Solaris ACL attribute (body overflow)");
return(ARCHIVE_WARN);
}
/* Skip leading octal number. */
/* ACL text is null-terminated; find the end. */
size -= (p - acl);
acl = p;
@ -771,6 +805,9 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar,
wp = utf8_decode(tar, acl, p - acl);
err = __archive_entry_acl_parse_w(entry, wp,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
if (err != ARCHIVE_OK)
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Malformed Solaris ACL attribute (unparsable)");
return (err);
}

View File

@ -16,6 +16,7 @@ TESTS= \
test_compat_bzip2.c \
test_compat_gtar.c \
test_compat_gzip.c \
test_compat_solaris_tar_acl.c \
test_compat_tar_hardlink.c \
test_compat_xz.c \
test_compat_zip.c \

View File

@ -151,10 +151,10 @@ static unsigned char reference[] = {
0,0,0,0,0,0,0,0,0,0,'1','1','3',' ','S','C','H','I','L','Y','.','a','c','l',
'.','a','c','c','e','s','s','=','u','s','e','r',':',':','r','-','x',',','g',
'r','o','u','p',':',':','r','-','-',',','o','t','h','e','r',':',':','-','w',
'x',',','g','r','o','u','p',':','g','r','o','u','p','7','8',':','r','w','x',
':','7','8',',','u','s','e','r',':','u','s','e','r','7','8',':','-','-','-',
':','7','8',',','u','s','e','r',':','u','s','e','r','7','7',':','r','-','-',
':','7','7',10,'1','6',' ','S','C','H','I','L','Y','.','d','e','v','=','0',
'x',',','u','s','e','r',':','u','s','e','r','7','7',':','r','-','-',':','7',
'7',',','u','s','e','r',':','u','s','e','r','7','8',':','-','-','-',':','7',
'8',',','g','r','o','u','p',':','g','r','o','u','p','7','8',':','r','w','x',
':','7','8',10,'1','6',' ','S','C','H','I','L','Y','.','d','e','v','=','0',
10,'1','6',' ','S','C','H','I','L','Y','.','i','n','o','=','0',10,'1','8',
' ','S','C','H','I','L','Y','.','n','l','i','n','k','=','0',10,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -464,7 +464,7 @@ DEFINE_TEST(test_acl_pax)
/* Assert that the generated data matches the built-in reference data.*/
failure("Generated pax archive does not match reference; check 'testout' and 'reference' files.");
assert(0 == memcmp(buff, reference, sizeof(reference)));
assertEqualMem(buff, reference, sizeof(reference));
failure("Generated pax archive does not match reference; check 'testout' and 'reference' files.");
assertEqualInt((int)used, sizeof(reference));

View File

@ -0,0 +1,128 @@
/*-
* Copyright (c) 2003-2009 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$");
/*
* Exercise support for reading Solaris-style ACL data
* from tar archives.
*
* This should work on all systems, regardless of whether local
* filesystems support ACLs or not.
*/
DEFINE_TEST(test_compat_solaris_tar_acl)
{
struct archive *a;
struct archive_entry *ae;
const char *reference1 = "test_compat_solaris_tar_acl.tar";
int type, permset, tag, qual;
const char *name;
/* Sample file generated on Solaris 10 */
extract_reference_file(reference1);
assert(NULL != (a = archive_read_new()));
assertA(0 == archive_read_support_format_all(a));
assertA(0 == archive_read_support_compression_all(a));
assertA(0 == archive_read_open_filename(a, reference1, 512));
/* Archive has 1 entry with some ACLs set on it. */
assertA(0 == archive_read_next_header(a, &ae));
failure("Basic ACLs should set mode to 0640, not %04o",
archive_entry_mode(ae)&0777);
assertEqualInt((archive_entry_mode(ae) & 0777), 0640);
assertEqualInt(7, archive_entry_acl_reset(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
assertEqualInt(006, permset);
assertEqualInt(ARCHIVE_ENTRY_ACL_USER_OBJ, tag);
assertEqualInt(-1, qual);
assert(name == NULL);
assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
assertEqualInt(004, permset);
assertEqualInt(ARCHIVE_ENTRY_ACL_GROUP_OBJ, tag);
assertEqualInt(-1, qual);
assert(name == NULL);
assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
assertEqualInt(000, permset);
assertEqualInt(ARCHIVE_ENTRY_ACL_OTHER, tag);
assertEqualInt(-1, qual);
assert(name == NULL);
assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
assertEqualInt(001, permset);
assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag);
assertEqualInt(71, qual);
assertEqualString(name, "lp");
assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
assertEqualInt(004, permset);
assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag);
assertEqualInt(666, qual);
assertEqualString(name, "666");
assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
assertEqualInt(007, permset);
assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag);
assertEqualInt(1000, qual);
assertEqualString(name, "trasz");
assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
assertEqualInt(004, permset);
assertEqualInt(ARCHIVE_ENTRY_ACL_MASK, tag);
assertEqualInt(-1, qual);
assertEqualString(name, NULL);
assertEqualInt(ARCHIVE_EOF, archive_entry_acl_next(ae,
ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
&type, &permset, &tag, &qual, &name));
/* Close the archive. */
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
}

View File

@ -0,0 +1,61 @@
$FreeBSD$
begin 644 test_acl_solaris.tar
M9FEL92UW:71H+7!O<VEX+6%C;',`````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````#`P,#`V-#0`,#`P,3<U,``P,#`P,#`P`#`P,#`P,#`P,30T
M`#$Q,3<T-C`T,34W`#`P,34Q-S8`00``````````````````````````````
M````````````````````````````````````````````````````````````
M``````````````````````````````````````````!U<W1A<@`P,'1R87-Z
M````````````````````````````````````<F]O=```````````````````
M```````````````````P,#`P,C$P`#`P,#`P,3``````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M```````````````````````Q,#`P,#`W`'5S97(Z.G)W+2QU<V5R.FQP.BTM
M>#HW,2QU<V5R.C8V-CIR+2TZ-C8V+'5S97(Z=')A<WHZ<G=X.C$P,#`L9W)O
M=7`Z.G(M+2QM87-K.G(M+2QO=&AE<CIR+2T``````````3````````/-@```
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````!%&8`````````&L`````,3`P,#`P-P!U
M<V5R.CIR=RTL=7-E<CIL<#HM+7@Z-S$L=7-E<CHV-C8Z<BTM.C8V-BQU<V5R
M.G1R87-Z.G)W>#HQ,#`P+&=R;W5P.CIR+2TL;6%S:SIR+69I;&4M=VET:"UP
M;W-I>"UA8VQS````````````````````````````````````````````````
M```````````````````````````````````````````````````````````P
M,#`P-C0T`#`P,#$W-3``,#`P,#`P,``P,#`P,#`P,#`P,``Q,3$W-#8P-#$U
M-P`P,#$U,30T`#``````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````=7-T87(`,#!T<F%S>@``````````````
M`````````````````````')O;W0`````````````````````````````````
M````,#`P,#(Q,``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````````````````````````````````````````````````````````````
H````````````````````````````````````````````````````````
`
end