From c533ace5debb7bd4b941d9cb5ab620dc140cf83f Mon Sep 17 00:00:00 2001 From: kientzle Date: Sun, 11 Mar 2007 10:29:52 +0000 Subject: Libarchive 2.0.23: * The ACL formatter was mis-formatting entries which had a user/group ID but no name. Make the parser tolerant of these, so that old archives can be correctly restored; fix the formatter to generate correct entries. * Fix overwrite detection by introducing a new "FAILED" return code that indicates the current entry cannot be continued but the archive as a whole is still sound. * Header cleanup: Remove some unused headers, add some that are required with new Linux systems. --- lib/libarchive/Makefile | 2 +- lib/libarchive/archive.h.in | 2 + lib/libarchive/archive_entry.c | 245 ++++++++++++++++++---------------- lib/libarchive/archive_read_extract.c | 38 ------ lib/libarchive/archive_write.c | 2 +- lib/libarchive/archive_write_disk.c | 5 +- lib/libarchive/test/test.h | 8 ++ 7 files changed, 145 insertions(+), 157 deletions(-) (limited to 'lib') diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index f5a37e9..a3f35b1 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -9,7 +9,7 @@ LDADD= -lbz2 -lz # Major: Bumped ONLY when API/ABI breakage happens (see SHLIB_MAJOR) # Minor: Bumped when significant new features are added # Revision: Bumped on any notable change -VERSION= 2.0.20 +VERSION= 2.0.23 ARCHIVE_API_MAJOR!= echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/\..*//' ARCHIVE_API_MINOR!= echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/[0-9]*\.//' -e 's/\..*//' diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in index 6ba6e9e..5282418 100644 --- a/lib/libarchive/archive.h.in +++ b/lib/libarchive/archive.h.in @@ -92,6 +92,8 @@ struct archive_entry; #define ARCHIVE_OK 0 /* Operation was successful. */ #define ARCHIVE_RETRY (-10) /* Retry might succeed. */ #define ARCHIVE_WARN (-20) /* Partial success. */ +/* For example, if write_header "fails", then you can't push data. */ +#define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */ #define ARCHIVE_FATAL (-30) /* No more operations are possible. */ /* diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index d019d06..2e51c6e 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -45,6 +45,12 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_LIMITS_H #include #endif +#ifdef HAVE_LINUX_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif #include #include #ifdef HAVE_STDLIB_H @@ -122,6 +128,7 @@ static void aes_set_mbs(struct aes *, const char *mbs); static void aes_copy_mbs(struct aes *, const char *mbs); /* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */ static void aes_copy_wcs(struct aes *, const wchar_t *wcs); +static void aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t); static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, @@ -134,10 +141,14 @@ static int acl_special(struct archive_entry *entry, int type, int permset, int tag); 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 void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test); +static void +archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, + int permset, int tag, int id, const wchar_t *name, size_t); /* @@ -326,6 +337,12 @@ aes_set_wcs(struct aes *aes, const wchar_t *wcs) static void aes_copy_wcs(struct aes *aes, const wchar_t *wcs) { + aes_copy_wcs_len(aes, wcs, wcslen(wcs)); +} + +static void +aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len) +{ if (aes->aes_mbs_alloc) { free(aes->aes_mbs_alloc); aes->aes_mbs_alloc = NULL; @@ -335,10 +352,11 @@ aes_copy_wcs(struct aes *aes, const wchar_t *wcs) aes->aes_wcs_alloc = NULL; } aes->aes_mbs = NULL; - aes->aes_wcs_alloc = (wchar_t *)malloc((wcslen(wcs) + 1) * sizeof(wchar_t)); + aes->aes_wcs_alloc = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); if (aes->aes_wcs_alloc == NULL) __archive_errx(1, "No memory for aes_copy_wcs()"); - wcscpy(aes->aes_wcs_alloc, wcs); + wmemcpy(aes->aes_wcs_alloc, wcs, len); + aes->aes_wcs_alloc[len] = L'\0'; aes->aes_wcs = aes->aes_wcs_alloc; } @@ -846,6 +864,13 @@ void archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { + archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name)); +} + +void +archive_entry_acl_add_entry_w_len(struct archive_entry *entry, + int type, int permset, int tag, int id, const wchar_t *name, size_t len) +{ struct ae_acl *ap; if (acl_special(entry, type, permset, tag) == 0) @@ -855,8 +880,8 @@ archive_entry_acl_add_entry_w(struct archive_entry *entry, /* XXX Error XXX */ return; } - if (name != NULL && *name != L'\0') - aes_copy_wcs(&ap->name, name); + if (name != NULL && *name != L'\0' && len > 0) + aes_copy_wcs_len(&ap->name, name, len); else aes_clean(&ap->name); } @@ -1079,6 +1104,8 @@ archive_entry_acl_text_w(struct archive_entry *entry, int flags) wname = aes_get_wcs(&ap->name); if (wname != NULL) length += wcslen(wname); + else + length += sizeof(uid_t) * 3 + 1; length ++; /* colon */ length += 3; /* rwx */ length += 1; /* colon */ @@ -1161,6 +1188,8 @@ archive_entry_acl_text_w(struct archive_entry *entry, int flags) static void append_id_w(wchar_t **wp, int id) { + if (id < 0) + id = 0; if (id > 9) append_id_w(wp, id / 10); *(*wp)++ = L"0123456789"[id % 10]; @@ -1205,6 +1234,10 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, if (wname != NULL) { wcscpy(*wp, wname); *wp += wcslen(*wp); + } else if (tag == ARCHIVE_ENTRY_ACL_USER + || tag == ARCHIVE_ENTRY_ACL_GROUP) { + append_id_w(wp, id); + id = -1; } *(*wp)++ = L':'; *(*wp)++ = (perm & 0444) ? L'r' : L'-'; @@ -1227,73 +1260,47 @@ int __archive_entry_acl_parse_w(struct archive_entry *entry, const wchar_t *text, int default_type) { + struct { + const wchar_t *start; + const wchar_t *end; + } field[4]; + + int fields; int type, tag, permset, id; - const wchar_t *start, *end; - const wchar_t *name_start, *name_end; + const wchar_t *p; wchar_t sep; - wchar_t *namebuff; - int namebuff_length; - - name_start = name_end = NULL; - namebuff = NULL; - namebuff_length = 0; while (text != NULL && *text != L'\0') { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - /* - * Solaris extension: "defaultuser::rwx" is the - * default ACL corresponding to "user::rwx", etc. + * Parse the fields out of the next entry, + * advance 'text' to start of next entry. */ - if (end-start > 7 && wmemcmp(start, L"default", 7) == 0) { - type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; - start += 7; - } else - type = default_type; - - if (prefix_w(start, end, L"user")) { + fields = 0; + do { + const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) { - tag = ARCHIVE_ENTRY_ACL_USER; - name_start = start; - name_end = end; - } else - tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (prefix_w(start, end, L"group")) { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) { - tag = ARCHIVE_ENTRY_ACL_GROUP; - name_start = start; - name_end = end; - } else - tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (prefix_w(start, end, L"other")) { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) - goto fail; - tag = ARCHIVE_ENTRY_ACL_OTHER; - } else if (prefix_w(start, end, L"mask")) { - next_field_w(&text, &start, &end, &sep); - if (sep != L':') - goto fail; - if (end > start) - goto fail; - tag = ARCHIVE_ENTRY_ACL_MASK; - } else - goto fail; + if (fields < 4) { + field[fields].start = start; + field[fields].end = end; + } + ++fields; + } while (sep == L':'); + + if (fields < 3) + return (ARCHIVE_WARN); - next_field_w(&text, &start, &end, &sep); + /* 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); + + /* Parse the permissions from field 2. */ permset = 0; - while (start < end) { - switch (*start++) { + p = field[2].start; + while (p < field[2].end) { + switch (*p++) { case 'r': case 'R': permset |= ARCHIVE_ENTRY_ACL_READ; break; @@ -1306,71 +1313,47 @@ __archive_entry_acl_parse_w(struct archive_entry *entry, case '-': break; default: - goto fail; + return (ARCHIVE_WARN); } } /* - * Support star-compatible numeric UID/GID extension. - * This extension adds a ":" followed by the numeric - * ID so that "group:groupname:rwx", for example, - * becomes "group:groupname:rwx:999", where 999 is the - * numeric GID. This extension makes it possible, for - * example, to correctly restore ACLs on a system that - * might have a damaged passwd file or be disconnected - * from a central NIS server. This extension is compatible - * with POSIX.1e draft 17. + * Solaris extension: "defaultuser::rwx" is the + * default ACL corresponding to "user::rwx", etc. */ - if (sep == L':' && (tag == ARCHIVE_ENTRY_ACL_USER || - tag == ARCHIVE_ENTRY_ACL_GROUP)) { - next_field_w(&text, &start, &end, &sep); - - id = 0; - while (start < end && *start >= '0' && *start <= '9') { - if (id > (INT_MAX / 10)) - id = INT_MAX; - else { - id *= 10; - id += *start - '0'; - start++; - } - } + 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 - id = -1; /* No id specified. */ + type = default_type; - /* Skip any additional entries. */ - while (sep == L':') { - next_field_w(&text, &start, &end, &sep); - } + if (prefix_w(field[0].start, field[0].end, L"user")) { + if (id != -1 || field[1].start < field[1].end) + tag = ARCHIVE_ENTRY_ACL_USER; + 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) + tag = ARCHIVE_ENTRY_ACL_GROUP; + 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) + 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) + return (ARCHIVE_WARN); + tag = ARCHIVE_ENTRY_ACL_MASK; + } else + return (ARCHIVE_WARN); /* Add entry to the internal list. */ - if (name_end == name_start) { - archive_entry_acl_add_entry_w(entry, type, permset, - tag, id, NULL); - } else { - if (namebuff_length <= name_end - name_start) { - if (namebuff != NULL) - free(namebuff); - namebuff_length = name_end - name_start + 256; - namebuff = - (wchar_t *)malloc(namebuff_length * sizeof(wchar_t)); - if (namebuff == NULL) - goto fail; - } - wmemcpy(namebuff, name_start, name_end - name_start); - namebuff[name_end - name_start] = L'\0'; - archive_entry_acl_add_entry_w(entry, type, - permset, tag, id, namebuff); - } + archive_entry_acl_add_entry_w_len(entry, type, permset, + tag, id, field[1].start, field[1].end - field[1].start); } - if (namebuff != NULL) - free(namebuff); return (ARCHIVE_OK); - -fail: - if (namebuff != NULL) - free(namebuff); - return (ARCHIVE_WARN); } /* @@ -1466,6 +1449,32 @@ archive_entry_xattr_next(struct archive_entry * entry, */ /* + * Parse a string to a positive decimal integer. Returns true if + * the string is non-empty and consists only of decimal digits, + * false otherwise. + */ +static int +isint_w(const wchar_t *start, const wchar_t *end, int *result) +{ + int n = 0; + if (start >= end) + return (0); + while (start < end) { + if (*start < '0' || *start > '9') + return (0); + if (n > (INT_MAX / 10)) + n = INT_MAX; + else { + n *= 10; + n += *start - '0'; + } + start++; + } + *result = n; + return (1); +} + +/* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last @@ -1502,6 +1511,10 @@ next_field_w(const wchar_t **wp, const wchar_t **start, (*wp)++; } +/* + * Return true if the characters [start...end) are a prefix of 'test'. + * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. + */ static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) { diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index 29586da..4285d13 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -29,53 +29,15 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif -#ifdef HAVE_SYS_ACL_H -#include -#endif -#ifdef HAVE_ATTR_XATTR_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#ifdef HAVE_EXT2FS_EXT2_FS_H -#include /* for Linux file flags */ -#endif #ifdef HAVE_ERRNO_H #include #endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_GRP_H -#include -#endif -#ifdef HAVE_LINUX_EXT2_FS_H -#include /* for Linux file flags */ -#endif -#ifdef HAVE_LIMITS_H -#include -#endif -#ifdef HAVE_PWD_H -#include -#endif -#include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif -#ifdef HAVE_UNISTD_H -#include -#endif #include "archive.h" #include "archive_private.h" diff --git a/lib/libarchive/archive_write.c b/lib/libarchive/archive_write.c index e68b0b8..ee1a9db 100644 --- a/lib/libarchive/archive_write.c +++ b/lib/libarchive/archive_write.c @@ -306,7 +306,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) archive_entry_ino(entry) == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Can't add archive to itself"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } /* Format and write header. */ diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c index f6187b8..1f8f0ce 100644 --- a/lib/libarchive/archive_write_disk.c +++ b/lib/libarchive/archive_write_disk.c @@ -58,6 +58,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_GRP_H #include #endif +#ifdef HAVE_LINUX_FS_H +#include /* for Linux file flags */ +#endif #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif @@ -697,7 +700,7 @@ restore_entry(struct archive_write_disk *a) a->st.st_dev == a->skip_file_dev && a->st.st_ino == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } if (!S_ISDIR(a->st.st_mode)) { diff --git a/lib/libarchive/test/test.h b/lib/libarchive/test/test.h index 7849215..685a8eb 100644 --- a/lib/libarchive/test/test.h +++ b/lib/libarchive/test/test.h @@ -32,6 +32,8 @@ * simplify the very repetitive test-*.c test programs. */ +#define _FILE_OFFSET_BITS 64 + #include #include #include @@ -54,6 +56,12 @@ #error Oops: No config.h and no pre-built configuration in test.h. #endif +/* No non-FreeBSD platform will have __FBSDID, so just define it here. */ +#ifdef __FreeBSD__ +#include /* For __FBSDID */ +#else +#define __FBSDID(a) /* null */ +#endif /* * "list.h" is simply created by "grep DEFINE_TEST"; it has -- cgit v1.1