diff options
Diffstat (limited to 'lib/libarchive/archive_entry.c')
-rw-r--r-- | lib/libarchive/archive_entry.c | 245 |
1 files changed, 129 insertions, 116 deletions
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 <limits.h> #endif +#ifdef HAVE_LINUX_FS_H +#include <linux/fs.h> /* for Linux file flags */ +#endif +#ifdef HAVE_LINUX_EXT2_FS_H +#include <linux/ext2_fs.h> /* for Linux file flags */ +#endif #include <stddef.h> #include <stdio.h> #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) { |